[php-src] PHP-8.6: Fix stream error operation leak in stream_select()
| From: | Seifeddine Gmati via ndossche | Date: | Thu, 22 Oct 2026 14:06:42 +0000 |
| Subject: | [php-src] PHP-8.6: Fix stream error operation leak in stream_select() | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-142577@lists.php.net to get a copy of this message | ||
Author: Seifeddine Gmati (azjezz)
Committer: ndossche (ndossche)
Date: 2026-10-22T15:57:26+02:00
Commit: https://github.com/php/php-src/commit/2762817831d4aa86f242e38b9a9efc17d1b7877b
Raw diff: https://github.com/php/php-src/commit/2762817831d4aa86f242e38b9a9efc17d1b7877b.diff
Fix stream error operation leak in stream_select()
Ensure the stream error operation is ended before returning false
when
PHP_SAFE_MAX_FD() fails.
Closes GH-24507.
Signed-off-by: azjezz <azjezz@protonmail.com>
Changed paths:
A ext/standard/tests/streams/gh24506.phpt
M NEWS
M ext/standard/streamsfuncs.c
Diff:
diff --git a/NEWS b/NEWS
index 20c05edb71e0..9f6000dfb911 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,10 @@ PHP NEWS
. Fixed bug GH-24461 (DISCARD ALL on persistent connection cleanup drops
PgBouncer's prepared statements). (David Carlier, Artem Ukrainskiy)
+- Streams:
+ . Fixed bug GH-24506 (stream_select() leaks a stream-error operation level
+ on its FD_SETSIZE failure path). (azjezz)
+
21 Oct 2026, PHP 8.6.0RC4
- Core:
diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c
index a03aed1ccdd4..98de1e44af7f 100644
--- a/ext/standard/streamsfuncs.c
+++ b/ext/standard/streamsfuncs.c
@@ -835,6 +835,7 @@ PHP_FUNCTION(stream_select)
}
if (!PHP_SAFE_MAX_FD(max_fd, max_set_count)) {
+ php_stream_error_operation_end(context);
RETURN_FALSE;
}
diff --git a/ext/standard/tests/streams/gh24506.phpt b/ext/standard/tests/streams/gh24506.phpt
new file mode 100644
index 000000000000..dc11c2c7cee6
--- /dev/null
+++ b/ext/standard/tests/streams/gh24506.phpt
@@ -0,0 +1,52 @@
+--TEST--
+GH-24506 (stream_select() leaks stream error operations on FD_SETSIZE failure)
+--EXTENSIONS--
+posix
+--SKIPIF--
+<?php
+if (PHP_FD_SETSIZE > 4096) {
+ die('skip FD_SETSIZE is too large');
+}
+if (!function_exists('posix_setrlimit')
+ || !posix_setrlimit(POSIX_RLIMIT_NOFILE, PHP_FD_SETSIZE + 32, PHP_FD_SETSIZE + 32)) {
+ die('skip Failed to set POSIX_RLIMIT_NOFILE');
+}
+?>
+--FILE--
+<?php
+posix_setrlimit(POSIX_RLIMIT_NOFILE, PHP_FD_SETSIZE + 32, PHP_FD_SETSIZE + 32);
+
+$streams = [];
+for ($i = 0; $i < PHP_FD_SETSIZE; $i++) {
+ $streams[] = fopen(__FILE__, 'r');
+}
+[$reader, $writer] = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+
+$warnings = 0;
+set_error_handler(static function (int $errno, string $message) use (&$warnings): bool {
+ if (str_contains($message, 'larger value of FD_SETSIZE')) {
+ $warnings++;
+ return true;
+ }
+ return false;
+});
+
+$failures = 0;
+for ($i = 0; $i < 1001; $i++) {
+ $read = [$reader];
+ $write = $except = null;
+ if (stream_select($read, $write, $except, 0) === false) {
+ $failures++;
+ }
+}
+restore_error_handler();
+
+var_dump($warnings, $failures);
+var_dump(fwrite($writer, 'x'));
+var_dump(fread($reader, 1));
+?>
+--EXPECT--
+int(1001)
+int(1001)
+int(1)
+string(1) "x"