8000 Prevent "interval" overflow in ExtUvLoop · reactphp/event-loop@5d2e219 · GitHub
[go: up one dir, main page]

Skip to content
Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5d2e219

Browse files
Prevent "interval" overflow in ExtUvLoop
1 parent babf91e commit 5d2e219

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

src/ExtUvLoop.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public function addTimer($interval, $callback)
127127
$this->timers->attach($timer, $event);
128128
\uv_timer_start(
129129
$event,
130-
(int) ($interval * 1000) + 1,
130+
$this->convertFloatSecondsToMilliseconds($interval),
131131
0,
132132
$callback
133133
);
@@ -146,12 +146,13 @@ public function addPeriodicTimer($interval, $callback)
146146
\call_user_func($timer->getCallback(), $timer);
147147
};
148148

149+
$interval = $this->convertFloatSecondsToMilliseconds($interval);
149150
$event = \uv_timer_init($this->uv);
150151
$this->timers->attach($timer, $event);
151152
\uv_timer_start(
152153
$event,
153-
(int) ($interval * 1000) + 1,
154-
(int) ($interval * 1000) + 1,
154+
$interval,
155+
(int) $interval === 0 ? 1 : $interval,
155156
$callback
156157
);
157158

@@ -313,4 +314,26 @@ private function createStreamListener()
313314

314315
return $callback;
315316
}
317+
318+
/**
319+
* @param float $interval
320+
* @return int
321+
*/
322+
private function convertFloatSecondsToMilliseconds($interval)
323+
{
324+
if ($interval < 0) {
325+
return 0;
326+
}
327+
328+
$maxValue = (int) (\PHP_INT_MAX / 1000);
329+
$intInterval = (int) $interval;
330+
331+
if (($intInterval <= 0 && $interval > 1) || $intInterval >= $maxValue) {
332+
throw new \InvalidArgumentException(
333+
"Interval overflow, value must be lower than '{$maxValue}', but '{$interval}' passed."
334+
);
335+
}
336+
337+
return (int) \floor($interval * 1000);
338+
}
316339
}

tests/AbstractLoopTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,11 +589,20 @@ public function testSignalsKeepTheLoopRunningAndRemovingItStopsTheLoop()
589589
$this->assertRunFasterThan(1.6);
590590
}
591591

592+
public function testTimerIntervalBelowZeroRunsImmediately()
593+
{
594+
$this->loop->addTimer(-1, function () {});
595+
596+
$this->assertRunFasterThan(0.002);
597+
}
598+
592599
public function testTimerIntervalCanBeFarInFuture()
593600
{
601+
// Maximum interval for ExtUvLoop implementation
602+
$interval = ((int) (PHP_INT_MAX / 1000)) - 1;
594603
$loop = $this->loop;
595604
// start a timer very far in the future
596-
$timer = $this->loop->addTimer(PHP_INT_MAX, function () { });
605+
$timer = $this->loop->addTimer($interval, function () { });
597606

598607
$this->loop->futureTick(function () use ($timer, $loop) {
599608
$loop->cancelTimer($timer);

tests/ExtUvLoopTest.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,82 @@ public function createLoop()
1414

1515
return new ExtUvLoop();
1616
}
17+
18+
/** @dataProvider intervalProvider */
19+
public function testTimerInterval($interval, $expectedExceptionMessage)
20+
{
21+
$this->expectException('InvalidArgumentException');
22+
$this->expectExceptionMessage($expectedExceptionMessage);
23+
24+
$this->loop
25+
->addTimer(
26+
$interval,
27+
function () {
28+
return 0;
29+
}
30+
);
31+
}
32+
33+
public function intervalProvider()
34+
{
35+
$oversizeInterval = PHP_INT_MAX / 1000;
36+
$maxValue = (int) (PHP_INT_MAX / 1000);
37+
$oneMaxValue = $maxValue + 1;
38+
$tenMaxValue = $maxValue + 10;
39+
$tenMillionsMaxValue = $maxValue + 10000000;
40+
$intMax = PHP_INT_MAX;
41+
$oneIntMax = PHP_INT_MAX + 1;
42+
$tenIntMax = PHP_INT_MAX + 10;
43+
$oneHundredIntMax = PHP_INT_MAX + 100;
44+
$oneThousandIntMax = PHP_INT_MAX + 1000;
45+
$tenMillionsIntMax = PHP_INT_MAX + 10000000;
46+
$tenThousandsTimesIntMax = PHP_INT_MAX * 1000;
47+
48+
return array(
49+
array(
50+
$oversizeInterval,
51+
"Interval overflow, value must be lower than '{$maxValue}', but '{$oversizeInterval}' passed."
52+
),
53+
array(
54+
$oneMaxValue,
55+
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneMaxValue}' passed.",
56+
),
57+
array(
58+
$tenMaxValue,
59+
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenMaxValue}' passed.",
60+
),
61+
array(
62+
$tenMillionsMaxValue,
63+
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenMillionsMaxValue}' passed.",
64+
),
65+
array(
66+
$intMax,
67+
"Interval overflow, value must be lower than '{$maxValue}', but '{$intMax}' passed.",
68+
),
69+
array(
70+
$oneIntMax,
71+
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneIntMax}' passed.",
72+
),
73+
array(
74+
$tenIntMax,
75+
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenIntMax}' passed.",
76+
),
77+
array(
78+
$oneHundredIntMax,
79+
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneHundredIntMax}' passed.",
80+
),
81+
array(
82+
$oneThousandIntMax,
83+
"Interval overflow, value must be lower than '{$maxValue}', but '{$oneThousandIntMax}' passed.",
84+
),
85+
array(
86+
$tenMillionsIntMax,
87+
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenMillionsIntMax}' passed.",
88+
),
89+
array(
90+
$tenThousandsTimesIntMax,
91+
"Interval overflow, value must be lower than '{$maxValue}', but '{$tenThousandsTimesIntMax}' passed.",
92+
),
93+
);
94+
}
1795
}

0 commit comments

Comments
 (0)
0