From 3e61f4dce7356116a1267c98795fcdf29597f82a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 1 Nov 2026 20:07:39 +0100 Subject: [PATCH] Fix the WSAPoll backend of Io\Poll accepting handles that are not sockets WSAPoll() can only poll sockets, but the backend accepted any fd. A file or a pipe such as STDOUT was then dropped by wait() without being reported. Adding such a handle now fails with ERR_NOSUPPORT, as epoll does for regular files. --- .../poll_stream_add_unpollable_wsapoll.phpt | 45 +++++++++++++++++++ main/poll/poll_backend_wsapoll.c | 10 +++++ 2 files changed, 55 insertions(+) create mode 100644 ext/standard/tests/poll/poll_stream_add_unpollable_wsapoll.phpt diff --git a/ext/standard/tests/poll/poll_stream_add_unpollable_wsapoll.phpt b/ext/standard/tests/poll/poll_stream_add_unpollable_wsapoll.phpt new file mode 100644 index 000000000000..ac4d2f89ac22 --- /dev/null +++ b/ext/standard/tests/poll/poll_stream_add_unpollable_wsapoll.phpt @@ -0,0 +1,45 @@ +--TEST-- +Io\Poll: WSAPoll reports a handle that is not a socket as unsupported +--SKIPIF-- +isAvailable()) { + die("skip WSAPoll backend required\n"); +} +if (!function_exists('proc_open')) { + die("skip proc_open required\n"); +} +?> +--FILE-- + ['pipe', 'w']], $pipes); +foreach (['regular file' => fopen(__FILE__, 'r'), 'pipe' => $pipes[1]] as $label => $stream) { + try { + $ctx->add(new StreamPollHandle($stream), [Io\Poll\Event::Read]); + echo "$label: added\n"; + } catch (Io\Poll\FailedHandleAddException $e) { + printf("%s: %s, code %s\n", $label, $e->getMessage(), + Io\Poll\FailedPollOperationException::ERROR_NOSUPPORT === $e->getCode() ? 'ERROR_NOSUPPORT' : $e->getCode()); + } + fclose($stream); +} +proc_close($proc); + +// a socket is still added and reported +[$r, $w] = pt_new_socket_pair(); +$watcher = $ctx->add(new StreamPollHandle($r), [Io\Poll\Event::Read]); +echo "socket: added\n"; +fwrite($w, "ping"); +$events = $ctx->wait(Time\Duration::fromSeconds(5)); +var_dump(count($events), $events[0] === $watcher); +?> +--EXPECT-- +regular file: Failed to add handle, code ERROR_NOSUPPORT +pipe: Failed to add handle, code ERROR_NOSUPPORT +socket: added +int(1) +bool(true) diff --git a/main/poll/poll_backend_wsapoll.c b/main/poll/poll_backend_wsapoll.c index 101a7cfe0e4b..55f5c04901eb 100644 --- a/main/poll/poll_backend_wsapoll.c +++ b/main/poll/poll_backend_wsapoll.c @@ -120,6 +120,16 @@ static zend_result wsapoll_backend_add(php_poll_ctx *ctx, int fd, uint32_t event return FAILURE; } + /* WSAPoll() can only poll sockets. Files and pipes give a CRT fd, + * which it never reports. */ + int type; + int type_len = sizeof(type); + if (getsockopt((SOCKET) fd, SOL_SOCKET, SO_TYPE, (char *) &type, &type_len) != 0 + && WSAGetLastError() == WSAENOTSOCK) { + php_poll_set_error(ctx, PHP_POLL_ERR_NOSUPPORT); + return FAILURE; + } + php_poll_fd_entry *entry = php_poll_fd_table_get_new(backend_data->fd_table, fd); if (!entry) { php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM);