Skip to content

Commit 39f98f7

Browse files
nicolas-grekasbukka
authored andcommitted
ext/standard: Fix Io\Poll timeouts longer than INT_MAX milliseconds
php_poll_timespec_to_ms() cast seconds * 1000 to an int, so a wait() of more than about 24 days wrapped around: five years came out as 48ms on the poll backend. The value is capped now. epoll is only affected where it falls back to epoll_wait(), since epoll_pwait2() takes the timespec as is.
1 parent 2a43bd1 commit 39f98f7

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Io\Poll: a timeout that overflows an int of milliseconds keeps waiting
3+
--SKIPIF--
4+
<?php
5+
if (!Io\Poll\Backend::Poll->isAvailable()) {
6+
die("skip poll backend not available\n");
7+
}
8+
if (PHP_OS_FAMILY === 'Windows') {
9+
die("skip POSIX only\n");
10+
}
11+
?>
12+
--FILE--
13+
<?php
14+
$process = proc_open([PHP_BINARY, '-r', 'usleep(300000); echo "ready";'], [1 => ['pipe', 'w']], $pipes);
15+
16+
$poll_ctx = new Io\Poll\Context(Io\Poll\Backend::Poll);
17+
$watcher = $poll_ctx->add(new StreamPollHandle($pipes[1]), [Io\Poll\Event::Read]);
18+
19+
// 158913790 seconds is about 5 years, and wraps around to 48ms as an int of milliseconds
20+
$events = $poll_ctx->wait(Time\Duration::fromSeconds(158913790));
21+
22+
var_dump(count($events), $events[0] === $watcher);
23+
24+
fclose($pipes[1]);
25+
proc_close($process);
26+
?>
27+
--EXPECT--
28+
int(1)
29+
bool(true)

‎main/poll/php_poll_internal.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ static inline int php_poll_timespec_to_ms(const struct timespec *timeout)
164164
return -1;
165165
}
166166

167+
/* Cap rather than wrap around: a timeout that long is as good as indefinite,
168+
* where truncating it to an int made poll() return after a few milliseconds */
169+
if (timeout->tv_sec >= (INT_MAX - 1000) / 1000) {
170+
return INT_MAX;
171+
}
172+
167173
int ms = (int) (timeout->tv_sec * 1000);
168174
/* Round nanoseconds up to the next millisecond to avoid premature return */
169175
if (timeout->tv_nsec > 0) {

0 commit comments

Comments
 (0)