From 9b18ebb301f1f995ec06ca3236142c1ad38a0924 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 17 Jul 2026 20:06:41 +0100 Subject: [PATCH 1/5] ext/sockets: php_if_index_to_addr4()/php_add4_to_if_index() use-after-free on the Windows address table. GetIpAddrTable() was retried against addr_table after an erealloc() whose return value was discarded, so on ERROR_INSUFFICIENT_BUFFER it wrote into the freed block. Assign the reallocated pointer back before retrying. --- ext/sockets/multicast.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/sockets/multicast.c b/ext/sockets/multicast.c index e258ca3e66e9..50808ef5b3aa 100644 --- a/ext/sockets/multicast.c +++ b/ext/sockets/multicast.c @@ -635,7 +635,7 @@ zend_result php_if_index_to_addr4(unsigned if_index, php_socket *php_sock, struc retry: retval = GetIpAddrTable(addr_table, &size, 0); if (retval == ERROR_INSUFFICIENT_BUFFER) { - erealloc(addr_table, size); + addr_table = erealloc(addr_table, size); goto retry; } if (retval != NO_ERROR) { @@ -677,7 +677,7 @@ zend_result php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, uns retry: retval = GetIpAddrTable(addr_table, &size, 0); if (retval == ERROR_INSUFFICIENT_BUFFER) { - erealloc(addr_table, size); + addr_table = erealloc(addr_table, size); goto retry; } if (retval != NO_ERROR) { From 97c71885a10f9282b98c38f87205fd2cde7c84c3 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 17 Jul 2026 20:06:42 +0100 Subject: [PATCH 2/5] socket_import_stream() double-close of the stream descriptor on error. socket_import_file_descriptor() sets bsd_socket before zstream is copied, so a failing import freed the object with zstream still UNDEF and closed the fd still owned by the stream. Invalidate bsd_socket on the error path. --- ext/sockets/sockets.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 79d5f77856d5..062c3120bbec 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -2357,6 +2357,7 @@ PHP_FUNCTION(socket_import_stream) retsock = Z_SOCKET_P(return_value); if (!socket_import_file_descriptor(socket, retsock)) { + retsock->bsd_socket = -1; zval_ptr_dtor(return_value); RETURN_FALSE; } From bf3456c55e238c6fcc2c6cc7ece3d5ec80919ab8 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 17 Jul 2026 20:06:42 +0100 Subject: [PATCH 3/5] socket_set_option() data race on the reuseport CBPF filter buffers. The cbpf/bpfprog buffers were function-local statics shared across threads, so concurrent SO_ATTACH_REUSEPORT_CBPF calls could install a torn filter. Move them to function scope so they outlive the block up to setsockopt() without being shared. --- ext/sockets/sockets.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 062c3120bbec..8d291a0e9b4c 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1864,6 +1864,10 @@ PHP_FUNCTION(socket_set_option) int timeout; #else struct timeval tv; +#endif +#ifdef SO_ATTACH_REUSEPORT_CBPF + struct sock_filter cbpf[8] = {0}; + struct sock_fprog bpfprog; #endif zend_long level, optname; void *opt_ptr; @@ -2074,8 +2078,6 @@ PHP_FUNCTION(socket_set_option) optname = SO_DETACH_BPF; } else { uint32_t k = (uint32_t)cbpf_val; - static struct sock_filter cbpf[8] = {0}; - static struct sock_fprog bpfprog; switch (k) { case SKF_AD_CPU: From 7547e96ee3aa1f5906ebccada9c7e58dbcd9f246 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 17 Jul 2026 20:06:43 +0100 Subject: [PATCH 4/5] socket_get_option() leaks uninitialized bytes for sub-int options. The generic path only corrected 1-byte options; an option returning 0/2/3 bytes left the high bytes of other_val uninitialized before RETURN_LONG. Zero-initialize it before getsockopt(). --- ext/sockets/sockets.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 8d291a0e9b4c..565a2d791bca 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1839,6 +1839,7 @@ PHP_FUNCTION(socket_get_option) #endif optlen = sizeof(other_val); + other_val = 0; if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&other_val, &optlen) != 0) { PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno); From a34f9ee860d1195381571e15c18989e96932a457 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 17 Jul 2026 20:06:43 +0100 Subject: [PATCH 5/5] to_zval_read_iov() missing bail out on oversized iov_len. The error was recorded but execution fell through to array_init_size() and the loop with a truncated length. Return right after reporting it. --- ext/sockets/conversions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c index eb4e58b3988e..867723f42de2 100644 --- a/ext/sockets/conversions.c +++ b/ext/sockets/conversions.c @@ -1206,6 +1206,7 @@ static void to_zval_read_iov(const char *msghdr_c, zval *zv, res_context *ctx) if (iovlen > UINT_MAX) { do_to_zval_err(ctx, "unexpectedly large value for iov_len: %lu", (unsigned long)iovlen); + return; } array_init_size(zv, (uint32_t)iovlen);