From e88a3e4250e9906adc471c4f9f25becc14615e9b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 9 Sep 2026 18:49:50 +0100 Subject: [PATCH] ext/sockets: socket_select() silent set truncation on Windows. php_sock_array_to_fd_set() returned a flag rather than the socket count, so PHP_SAFE_MAX_FD() got a zero and never fired. Sets larger than FD_SETSIZE were quietly truncated and select() reported on the survivors. --- ext/sockets/sockets.c | 15 ++++-- .../socket_select_fd_setsize_overflow.phpt | 50 +++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 ext/sockets/tests/socket_select_fd_setsize_overflow.phpt diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 565a2d791bca..d157d2023505 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -542,7 +542,7 @@ static int php_sock_array_to_fd_set(uint32_t arg_num, zval *sock_array, fd_set * num++; } ZEND_HASH_FOREACH_END(); - return num ? 1 : 0; + return num; } /* }}} */ @@ -593,7 +593,7 @@ PHP_FUNCTION(socket_select) struct timeval *tv_p = NULL; fd_set rfds, wfds, efds; PHP_SOCKET max_fd = 0; - int retval, sets = 0; + int retval, sets = 0, max_set_count = 0; zend_long sec, usec = 0; bool sec_is_null = 0; @@ -615,18 +615,27 @@ PHP_FUNCTION(socket_select) if (retval == -1) { RETURN_THROWS(); } + if (retval > max_set_count) { + max_set_count = retval; + } } if (w_array != NULL) { sets += retval = php_sock_array_to_fd_set(2, w_array, &wfds, &max_fd); if (retval == -1) { RETURN_THROWS(); } + if (retval > max_set_count) { + max_set_count = retval; + } } if (e_array != NULL) { sets += retval = php_sock_array_to_fd_set(3, e_array, &efds, &max_fd); if (retval == -1) { RETURN_THROWS(); } + if (retval > max_set_count) { + max_set_count = retval; + } } if (!sets) { @@ -634,7 +643,7 @@ PHP_FUNCTION(socket_select) RETURN_THROWS(); } - if (!PHP_SAFE_MAX_FD(max_fd, 0)) { + if (!PHP_SAFE_MAX_FD(max_fd, max_set_count)) { RETURN_FALSE; } diff --git a/ext/sockets/tests/socket_select_fd_setsize_overflow.phpt b/ext/sockets/tests/socket_select_fd_setsize_overflow.phpt new file mode 100644 index 000000000000..bb20d0df919a --- /dev/null +++ b/ext/sockets/tests/socket_select_fd_setsize_overflow.phpt @@ -0,0 +1,50 @@ +--TEST-- +socket_select() reports an overflowing set instead of silently truncating it +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- + 256); + +$read = $sockets; +$write = $except = null; +var_dump(socket_select($read, $write, $except, 0)); + +$write = $sockets; +$read = $except = null; +var_dump(socket_select($read, $write, $except, 0)); + +$except = $sockets; +$read = $write = null; +var_dump(socket_select($read, $write, $except, 0)); + +foreach ($sockets as $socket) { + socket_close($socket); +} +?> +--EXPECTF-- +bool(true) + +Warning: socket_select(): PHP needs to be recompiled with a larger value of FD_SETSIZE.%A +bool(false) + +Warning: socket_select(): PHP needs to be recompiled with a larger value of FD_SETSIZE.%A +bool(false) + +Warning: socket_select(): PHP needs to be recompiled with a larger value of FD_SETSIZE.%A +bool(false)