[php-src] PR #24621: Fix GH-24616: Socket transports accept ports above 65535
| From: | Marti2203 | Date: | Fri, 23 Oct 2026 18:31:39 +0000 |
| Subject: | [php-src] PR #24621: Fix GH-24616: Socket transports accept ports above 65535 | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-39785@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/24621
Author: Marti2203
parse_ip_address_ex() in main/streams/xp_socket.c reads the port with
atoi() and does not check its range. The port is later narrowed to unsigned
short, so a port above 65535 (or a negative one) wraps to another port.
fsockopen(), stream_socket_client() and
stream_socket_server(), for both tcp:// and udp://, connect
to or bind the wrong port:
```php
$srv = stream_socket_server("tcp://127.0.0.1:0");
$port = (int) substr(stream_socket_get_name($srv, false), strlen("127.0.0.1:"));
$c = stream_socket_client("tcp://127.0.0.1:" . ($port + 65536)); // connects to $port
$c2 = fsockopen("127.0.0.1", $port + 65536); // connects to $port
```
php_network_parse_network_address_with_port() in main/network.c (used for
the target address of stream_socket_sendto()) has the same atoi() pattern,
and stores the result in a short.
### Fix
Parse the port with ZEND_STRTOL() and reject values outside 0..65535. In
parse_ip_address_ex(), this applies to both the plain and the [IPv6]:port
forms, and fails with Failed to parse port of address "..." (like the
existing Failed to parse address "..." errors).
php_network_parse_network_address_with_port() returns FAILURE, so
stream_socket_sendto() reports its existing "Failed to parse ... into a valid
network address" warning.
Everything else that atoi() accepted is still accepted, for example trailing characters
after the port and an empty port (port 0), to keep the change small for a stable branch. An
out-of-range port in the bindto context option is now handled like any other
bindto value that does not parse (it is ignored, as before for unparsable values).
ext/standard/tests/streams/bug69521.phpt used port 74321 and so relied on this wrap (it
actually listened on 8785). It now uses an ephemeral port.
### Test
ext/standard/tests/network/gh24616.phpt uses port + 65536 of a listening server with
stream_socket_client(), fsockopen() and the IPv6 form. It also tries
negative and very large ports, stream_socket_server() on 65536, and
stream_socket_sendto() to a UDP port + 65536. Then it checks that the real port still
connects.
- Without the fix: the client and fsockopen() calls return a connected stream,
stream_socket_server() binds, and stream_socket_sendto() sends 1 byte.
- With the fix: all of them fail, and the valid port still works.
- ext/standard/tests/network, ext/standard/tests/streams and
ext/standard/tests/file (debug build): 807 passed, 323 skipped, 0 failed.
This rejects input that was accepted before, so if you prefer to treat it as a behaviour change, I
can retarget it to master.
Fixes GH-24616
This issue was found by an LLM-Enhanced Static Analysis Engine and verified manually; the patch was
manually reviewed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)