Skip to content

Fix GH-24173: Sockets left in non-blocking mode on Windows - #24174

Open
vibbow wants to merge 1 commit into
php:PHP-8.4from
vibbow:fix-gh24173-win-restore-blocking
Open

vibbow wants to merge 1 commit into
php:PHP-8.4from
vibbow:fix-gh24173-win-restore-blocking

Conversation

@vibbow

@vibbow vibbow commented Oct 7, 2026

Copy link
Copy Markdown

Fixes #24173.

php_network_connect_socket() switches the socket to non-blocking mode for the connect, and for a synchronous connect it is supposed to restore the original mode afterwards. On Windows, SET_SOCKET_BLOCKING_MODE() sets save = TRUE, and FIONBIO doesn't write back the previous mode. So RESTORE_SOCKET_BLOCKING_MODE() passed TRUE again, and the socket stayed non-blocking while the stream reported blocked => true. Paths that call recv() without polling first, such as stream_socket_recvfrom() and socket_import_stream() + socket_read(), then failed immediately with WSAEWOULDBLOCK.

Winsock can't query a socket's blocking mode. However, all callers of php_network_connect_socket() pass freshly created sockets, which are blocking: php_network_connect_socket_to_host(), the Unix socket connect in xp_socket.c, and php_connect_nonb() in ext/ftp. So this restores to blocking, which matches the POSIX behavior. Async connects don't call RESTORE, so they are unaffected.

The test is Windows-only. It sets a 100 ms SO_RCVTIMEO on the imported socket and reads with no data available. A blocking socket times out with SOCKET_ETIMEDOUT; a non-blocking one fails at once with SOCKET_EWOULDBLOCK. On Windows, the test fails without the fix and passes with it.

Also tested on Windows 11 with a minimal NTS x64 build:

  • With the reproducer from the issue, all three cases now wait for the data, about 300 ms each.
  • stream_set_timeout() + fread() still times out as expected.
  • stream_set_blocking($c, false) + fread() still returns immediately.
  • Connect latency and refused/timed-out connects are unchanged.

This is independent of #24172 (GH-24171), which touches the same function.

🤖 Generated with Claude Code

php_network_connect_socket() switches the socket to non-blocking mode
for the connect and restores the original mode afterwards for
synchronous connects. On Windows, SET_SOCKET_BLOCKING_MODE() sets
`save = TRUE` and FIONBIO does not write back the previous mode, so
RESTORE_SOCKET_BLOCKING_MODE() set the socket to non-blocking again.

Winsock cannot query a socket's blocking mode, but all callers pass
freshly created (blocking) sockets, so restore to blocking. This
matches the POSIX behavior.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant