Skip to content

Commit 89b2304

Browse files
committed
Fix poll event leak on negative stream timeout
Add test and changelog entry for the negative timeout overflow fix in network_async.c where tv_sec < 0 caused poll event refcount leak.
1 parent 5b939a9 commit 89b2304

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ All notable changes to the Async extension for PHP will be documented in this fi
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [0.6.3] - 2026-03-24
8+
## [0.6.3] - 2026-03-25
99

1010
### Fixed
1111
- **`Scope::awaitCompletion()` ignoring completion**: `async_scope_notify_coroutine_finished()` was missing the call to `scope_check_completion_and_notify()`, so `awaitCompletion()` never woke up when all coroutines finished and always waited until the timeout expired.
1212
- **`Scope::awaitAfterCancellation()` cleanup**: Replaced `zend_async_waker_clean()` with `ZEND_ASYNC_WAKER_DESTROY()` on error paths, and switched to checking the return value of `zend_async_resume_when()` instead of `EG(exception)`.
13+
- **Negative stream timeout causing poll event leak**: When a stream context timeout was negative (e.g. `PHP_INT_MIN`), the signed `tv_sec` overflowed to a huge positive value when cast to `zend_ulong` milliseconds. This created an async waker with a timer event that held an extra reference to the poll event (refcount 3 instead of 2), causing it to leak. Fixed by checking `tv_sec < 0` before the conversion and falling back to synchronous `php_pollfd_for()`.
1314

1415
## [0.6.2] - 2026-03-24
1516

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
GH-16809 (fopen HTTP wrapper timeout stream context option overflow)
3+
--INI--
4+
allow_url_fopen=1
5+
--SKIPIF--
6+
<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?>
7+
--FILE--
8+
<?php
9+
$serverCode = <<<'CODE'
10+
echo 1;
11+
CODE;
12+
13+
include __DIR__."/../../../../sapi/cli/tests/php_cli_server.inc";
14+
php_cli_server_start($serverCode, null, []);
15+
16+
$uri = "http://" . PHP_CLI_SERVER_ADDRESS . '/test';
17+
$config = [
18+
'http' => [
19+
'timeout' => PHP_INT_MIN,
20+
],
21+
];
22+
$ctx = stream_context_create($config);
23+
var_dump(fopen($uri, "r", false, $ctx));
24+
25+
$config['http']['timeout'] = PHP_INT_MAX;
26+
$ctx = stream_context_create($config);
27+
var_dump(fopen($uri, "r", false, $ctx));
28+
?>
29+
--EXPECTF--
30+
resource(%d) of type (stream)
31+
32+
Warning: fopen(http://%s): Failed to open stream: timeout must be lower than %d in %s on line %d
33+
bool(false)

0 commit comments

Comments
 (0)