Commit 2762817831d for php
commit 2762817831d4aa86f242e38b9a9efc17d1b7877b
Author: Seifeddine Gmati <29315886+azjezz@users.noreply.github.com>
Date: Wed Oct 21 21:33:10 2026 +0100
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>
diff --git a/NEWS b/NEWS
index 20c05edb71e..9f6000dfb91 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 a03aed1ccdd..98de1e44af7 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 00000000000..dc11c2c7cee
--- /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"