Skip to content

Commit fcb2945

Browse files
committed
detect infinite loop
1 parent dd7559a commit fcb2945

File tree

2 files changed

+63
-8
lines changed

2 files changed

+63
-8
lines changed

ext/random/random.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ static zend_object_handlers random_randomizer_object_handlers;
179179
static uint32_t rand_range32(const php_random_engine_algo *algo, void *state, uint32_t umax, bool *engine_unsafe) {
180180
uint32_t result, limit;
181181
size_t generated_size;
182+
uint32_t count;
182183

183184
RANDOM_ENGINE_GENERATE_GUARD(algo, state, result, generated_size, engine_unsafe);
184185
while (generated_size < sizeof(uint32_t)) {
@@ -208,7 +209,15 @@ static uint32_t rand_range32(const php_random_engine_algo *algo, void *state, ui
208209
limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
209210

210211
/* Discard numbers over the limit to avoid modulo bias */
212+
count = 0;
211213
while (UNEXPECTED(result > limit)) {
214+
/* If the requirements cannot be met in a cycles, the engine is unsafe */
215+
if (++count > 50) {
216+
if (engine_unsafe != NULL) {
217+
*engine_unsafe = true;
218+
}
219+
return 0;
220+
}
212221
RANDOM_ENGINE_GENERATE_GUARD(algo, state, result, generated_size, engine_unsafe);
213222
while (generated_size < sizeof(uint32_t)) {
214223
uint32_t ret;
@@ -229,6 +238,7 @@ static uint32_t rand_range32(const php_random_engine_algo *algo, void *state, ui
229238
static uint64_t rand_range64(const php_random_engine_algo *algo, void *state, uint64_t umax, bool *engine_unsafe) {
230239
uint64_t result, limit;
231240
size_t generated_size;
241+
uint32_t count;
232242

233243
RANDOM_ENGINE_GENERATE_GUARD(algo, state, result, generated_size, engine_unsafe);
234244

@@ -260,7 +270,15 @@ static uint64_t rand_range64(const php_random_engine_algo *algo, void *state, ui
260270
limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
261271

262272
/* Discard numbers over the limit to avoid modulo bias */
273+
count = 0;
263274
while (UNEXPECTED(result > limit)) {
275+
/* If the requirements cannot be met in a cycles, the engine is unsafe */
276+
if (++count > 50) {
277+
if (engine_unsafe != NULL) {
278+
*engine_unsafe = true;
279+
}
280+
return 0;
281+
}
264282
RANDOM_ENGINE_GENERATE_GUARD(algo, state, result, generated_size, engine_unsafe);
265283

266284
while (generated_size < sizeof(uint64_t)) {

ext/random/tests/03_randomizer/user_unsafe.phpt

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Random: Randomizer: User: Engine unsafe
33
--FILE--
44
<?php
55

6+
// Empty generator
67
$randomizer = (new \Random\Randomizer(
78
new class () implements \Random\Engine {
89
public function generate(): string
@@ -15,30 +16,66 @@ $randomizer = (new \Random\Randomizer(
1516
try {
1617
$randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX);
1718
} catch (\RuntimeException $e) {
18-
echo "catched\n";
19+
echo "{$e->getMessage()}\n";
1920
}
2021

2122
try {
2223
$randomizer->getBytes(1);
2324
} catch (\RuntimeException $e) {
24-
echo "catched\n";
25+
echo "{$e->getMessage()}\n";
2526
}
2627

2728
try {
2829
$randomizer->shuffleArray(\range(1, 10));
2930
} catch (\RuntimeException $e) {
30-
echo "catched\n";
31+
echo "{$e->getMessage()}\n";
3132
}
3233

3334
try {
3435
$randomizer->shuffleString('foobar');
3536
} catch (\RuntimeException $e) {
36-
echo "catched\n";
37+
echo "{$e->getMessage()}\n";
38+
}
39+
40+
// Infinite loop
41+
$randomizer = (new \Random\Randomizer(
42+
new class () implements \Random\Engine {
43+
public function generate(): string
44+
{
45+
return "\xff\xff\xff\xff\xff\xff\xff\xff";
46+
}
47+
}
48+
));
49+
50+
try {
51+
$randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX);
52+
} catch (\RuntimeException $e) {
53+
echo "{$e->getMessage()}\n";
54+
}
55+
56+
try {
57+
$randomizer->getBytes(1);
58+
} catch (\RuntimeException $e) {
59+
echo "{$e->getMessage()}\n";
60+
}
61+
62+
try {
63+
$randomizer->shuffleArray(\range(1, 10));
64+
} catch (\RuntimeException $e) {
65+
echo "{$e->getMessage()}\n";
66+
}
67+
68+
try {
69+
$randomizer->shuffleString('foobar');
70+
} catch (\RuntimeException $e) {
71+
echo "{$e->getMessage()}\n";
3772
}
3873

3974
?>
4075
--EXPECT--
41-
catched
42-
catched
43-
catched
44-
catched
76+
Random number generate failed
77+
Random number generate failed
78+
Random number generate failed
79+
Random number generate failed
80+
Random number generate failed
81+
Random number generate failed

0 commit comments

Comments
 (0)