From 0c85173bc644e6069473c08b51b0123de0027650 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 8 Sep 2026 21:21:39 +0100 Subject: [PATCH 1/5] ext/sockets: AF_VSOCK support addition. vsock carries traffic across the VM boundary with no NIC, no IP and no DNS, so it works before guest networking is up, or where there is none at all as in AWS Nitro Enclaves. A guest connects to VMADDR_CID_HOST and a host to the guest's context ID, both through the usual socket_bind()/socket_connect() with the CID passed as the address string. --- ext/sockets/config.m4 | 2 +- ext/sockets/sockets.c | 182 ++++++++++++++++++++++++++++++++-- ext/sockets/sockets.stub.php | 32 ++++++ ext/sockets/sockets_arginfo.h | 10 +- 4 files changed, 214 insertions(+), 12 deletions(-) diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index bf183f5e6e7d..08ffeb037c0e 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -5,7 +5,7 @@ PHP_ARG_ENABLE([sockets], if test "$PHP_SOCKETS" != "no"; then AC_CHECK_FUNCS([hstrerror if_nametoindex if_indextoname sockatmark accept4]) - AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h linux/udp.h]) + AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h linux/udp.h linux/vm_sockets.h]) AC_DEFINE([HAVE_SOCKETS], [1], [Define to 1 if the PHP extension 'sockets' is available.]) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 66df65657988..c4828ffb06db 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -72,6 +72,11 @@ # if defined(HAVE_LINUX_UDP_H) # include # endif +# if defined(HAVE_LINUX_VM_SOCKETS_H) +# include +# else +# undef AF_VSOCK +# endif #endif #include @@ -113,6 +118,36 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets) (void)family #endif +#ifdef AF_VSOCK +#define PHP_VSOCK_ID_MAX ((int64_t) UINT32_MAX) + +static bool php_set_vsock_addr(struct sockaddr_vm *svm, zend_string *addr, zend_long port, + uint32_t addr_arg_num, uint32_t port_arg_num) +{ + zend_long cid; + double dval; + + if (is_numeric_string(ZSTR_VAL(addr), ZSTR_LEN(addr), &cid, &dval, 0) != IS_LONG + || (int64_t) cid < INT32_MIN || (int64_t) cid > PHP_VSOCK_ID_MAX) { + zend_argument_value_error(addr_arg_num, "must be a numeric context ID between 0 and " ZEND_ULONG_FMT, + (zend_ulong) UINT32_MAX); + return false; + } + + if ((int64_t) port < INT32_MIN || (int64_t) port > PHP_VSOCK_ID_MAX) { + zend_argument_value_error(port_arg_num, "must be between 0 and " ZEND_ULONG_FMT, (zend_ulong) UINT32_MAX); + return false; + } + + memset(svm, 0, sizeof(*svm)); + svm->svm_family = AF_VSOCK; + svm->svm_cid = (uint32_t) cid; + svm->svm_port = (uint32_t) port; + + return true; +} +#endif + static PHP_GINIT_FUNCTION(sockets); static PHP_GSHUTDOWN_FUNCTION(sockets); static PHP_MINIT_FUNCTION(sockets); @@ -994,6 +1029,9 @@ PHP_FUNCTION(socket_getsockname) #endif #ifdef AF_PACKET struct sockaddr_ll *sll; +#endif +#ifdef AF_VSOCK + struct sockaddr_vm *svm; #endif char addrbuf[INET6_ADDRSTRLEN]; struct sockaddr_un *s_un; @@ -1060,9 +1098,23 @@ PHP_FUNCTION(socket_getsockname) } RETURN_TRUE; #endif +#ifdef AF_VSOCK + case AF_VSOCK: { + size_t cid_len; + + svm = (struct sockaddr_vm *) sa; + cid_len = (size_t) snprintf(addrbuf, sizeof(addrbuf), "%u", svm->svm_cid); + + ZEND_TRY_ASSIGN_REF_STRINGL(addr, addrbuf, cid_len); + if (objint != NULL) { + ZEND_TRY_ASSIGN_REF_LONG(objint, (zend_long) svm->svm_port); + } + RETURN_TRUE; + } +#endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_VSOCK, AF_INET, or AF_INET6"); RETURN_THROWS(); } } @@ -1078,6 +1130,9 @@ PHP_FUNCTION(socket_getpeername) struct sockaddr_in *sin; #ifdef HAVE_IPV6 struct sockaddr_in6 *sin6; +#endif +#ifdef AF_VSOCK + struct sockaddr_vm *svm; #endif char addrbuf[INET6_ADDRSTRLEN]; struct sockaddr_un *s_un; @@ -1131,9 +1186,23 @@ PHP_FUNCTION(socket_getpeername) ZEND_TRY_ASSIGN_REF_STRING(arg2, s_un->sun_path); RETURN_TRUE; +#ifdef AF_VSOCK + case AF_VSOCK: { + size_t cid_len; + + svm = (struct sockaddr_vm *) sa; + cid_len = (size_t) snprintf(addrbuf, sizeof(addrbuf), "%u", svm->svm_cid); + + ZEND_TRY_ASSIGN_REF_STRINGL(arg2, addrbuf, cid_len); + if (arg3 != NULL) { + ZEND_TRY_ASSIGN_REF_LONG(arg3, (zend_long) svm->svm_port); + } + RETURN_TRUE; + } +#endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_VSOCK, AF_INET, or AF_INET6"); RETURN_THROWS(); } } @@ -1157,9 +1226,12 @@ PHP_FUNCTION(socket_create) #endif #ifdef AF_PACKET && domain != AF_PACKET +#endif +#ifdef AF_VSOCK + && domain != AF_VSOCK #endif && domain != AF_INET) { - zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET6, or AF_INET"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_VSOCK, AF_INET6, or AF_INET"); RETURN_THROWS(); } @@ -1272,9 +1344,26 @@ PHP_FUNCTION(socket_connect) (socklen_t)(offsetof(struct sockaddr_un, sun_path) + ZSTR_LEN(addr))); break; } +#ifdef AF_VSOCK + case AF_VSOCK: { + struct sockaddr_vm svm; + + if (port_is_null) { + zend_argument_value_error(3, "cannot be null when the socket type is AF_VSOCK"); + RETURN_THROWS(); + } + + if (!php_set_vsock_addr(&svm, addr, port, 2, 3)) { + RETURN_THROWS(); + } + + retval = connect(php_sock->bsd_socket, (struct sockaddr *) &svm, sizeof(svm)); + break; + } +#endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_VSOCK, AF_INET, or AF_INET6"); RETURN_THROWS(); } @@ -1326,10 +1415,16 @@ PHP_FUNCTION(socket_bind) php_sock = Z_SOCKET_P(arg1); ENSURE_SOCKET_VALID(php_sock); - if (objint < 0 || objint > USHRT_MAX) { - zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX); - RETURN_THROWS(); +#ifdef AF_VSOCK + if (php_sock->type != AF_VSOCK) { +#endif + if (objint < 0 || objint > USHRT_MAX) { + zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX); + RETURN_THROWS(); + } +#ifdef AF_VSOCK } +#endif switch(php_sock->type) { case AF_UNIX: @@ -1395,9 +1490,22 @@ PHP_FUNCTION(socket_bind) retval = bind(php_sock->bsd_socket, sock_type, sizeof(struct sockaddr_ll)); break; } +#endif +#ifdef AF_VSOCK + case AF_VSOCK: + { + struct sockaddr_vm *sa = (struct sockaddr_vm *) sock_type; + + if (!php_set_vsock_addr(sa, addr, objint, 2, 3)) { + RETURN_THROWS(); + } + + retval = bind(php_sock->bsd_socket, sock_type, sizeof(struct sockaddr_vm)); + break; + } #endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_VSOCK, AF_INET, or AF_INET6"); RETURN_THROWS(); } @@ -1501,6 +1609,9 @@ PHP_FUNCTION(socket_recvfrom) #endif #ifdef AF_PACKET struct sockaddr_ll sll; +#endif +#ifdef AF_VSOCK + struct sockaddr_vm svm; #endif char addrbuf[INET6_ADDRSTRLEN]; socklen_t slen; @@ -1677,9 +1788,37 @@ PHP_FUNCTION(socket_recvfrom) } break; } +#endif +#ifdef AF_VSOCK + case AF_VSOCK: { + size_t cid_len; + + slen = sizeof(svm); + memset(&svm, 0, slen); + + retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), length, flags, (struct sockaddr *)&svm, &slen); + + if (retval < 0) { + PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno); + zend_string_efree(recv_buf); + RETURN_FALSE; + } + ZSTR_LEN(recv_buf) = MIN((size_t)retval, (size_t)length); + ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0'; + + cid_len = (size_t) snprintf(addrbuf, sizeof(addrbuf), "%u", svm.svm_cid); + + ZEND_TRY_ASSIGN_REF_NEW_STR(zdata, recv_buf); + ZEND_TRY_ASSIGN_REF_STRINGL(zaddr, addrbuf, cid_len); + + if (zport) { + ZEND_TRY_ASSIGN_REF_LONG(zport, (zend_long) svm.svm_port); + } + break; + } #endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_VSOCK, AF_INET, or AF_INET6"); RETURN_THROWS(); } @@ -1699,6 +1838,9 @@ PHP_FUNCTION(socket_sendto) #endif #ifdef AF_PACKET struct sockaddr_ll sll; +#endif +#ifdef AF_VSOCK + struct sockaddr_vm svm; #endif int retval; size_t buf_len; @@ -1722,11 +1864,17 @@ PHP_FUNCTION(socket_sendto) #ifdef AF_PACKET if (php_sock->type != AF_PACKET) { +#endif +#ifdef AF_VSOCK + if (php_sock->type != AF_VSOCK) { #endif if (port < 0 || port > USHRT_MAX) { zend_argument_value_error(6, "must be between 0 and %u", USHRT_MAX); RETURN_THROWS(); } +#ifdef AF_VSOCK + } +#endif #ifdef AF_PACKET } #endif @@ -1798,9 +1946,23 @@ PHP_FUNCTION(socket_sendto) retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *)&sll, sizeof(sll)); break; +#endif +#ifdef AF_VSOCK + case AF_VSOCK: + if (port_is_null) { + zend_argument_value_error(6, "cannot be null when the socket type is AF_VSOCK"); + RETURN_THROWS(); + } + + if (!php_set_vsock_addr(&svm, addr, port, 5, 6)) { + RETURN_THROWS(); + } + + retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *)&svm, sizeof(svm)); + break; #endif default: - zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6"); + zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_VSOCK, AF_INET, or AF_INET6"); RETURN_THROWS(); } diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 681d5ba6d0ff..15703d31082b 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -40,6 +40,38 @@ */ const AF_PACKET = UNKNOWN; #endif +#ifdef AF_VSOCK +/** + * @var int + * @cvalue AF_VSOCK + */ +const AF_VSOCK = UNKNOWN; +/** + * @var int + * @cvalue VMADDR_CID_ANY + */ +const VMADDR_CID_ANY = UNKNOWN; +/** + * @var int + * @cvalue VMADDR_CID_HYPERVISOR + */ +const VMADDR_CID_HYPERVISOR = UNKNOWN; +/** + * @var int + * @cvalue VMADDR_CID_LOCAL + */ +const VMADDR_CID_LOCAL = UNKNOWN; +/** + * @var int + * @cvalue VMADDR_CID_HOST + */ +const VMADDR_CID_HOST = UNKNOWN; +/** + * @var int + * @cvalue VMADDR_PORT_ANY + */ +const VMADDR_PORT_ANY = UNKNOWN; +#endif /** * @var int * @cvalue SOCK_STREAM diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 3f62bb3607fe..33752fe5955b 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit sockets.stub.php instead. - * Stub hash: aceee39bed5332f7f26d5d768976c4d5ab96ab3c */ + * Stub hash: 8a5bc212b70cb5b74f99287d53b348707917f292 */ #include "zend_constants.h" @@ -328,6 +328,14 @@ static void register_sockets_symbols(int module_number) #endif #if defined(AF_PACKET) REGISTER_LONG_CONSTANT("AF_PACKET", AF_PACKET, CONST_PERSISTENT); +#endif +#if defined(AF_VSOCK) + REGISTER_LONG_CONSTANT("AF_VSOCK", AF_VSOCK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("VMADDR_CID_ANY", VMADDR_CID_ANY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("VMADDR_CID_HYPERVISOR", VMADDR_CID_HYPERVISOR, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("VMADDR_CID_LOCAL", VMADDR_CID_LOCAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("VMADDR_CID_HOST", VMADDR_CID_HOST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("VMADDR_PORT_ANY", VMADDR_PORT_ANY, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("SOCK_STREAM", SOCK_STREAM, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SOCK_DGRAM", SOCK_DGRAM, CONST_PERSISTENT); From 6abf3473a1cc6eed70ef0829ae0aefd4751c92ed Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 8 Sep 2026 21:54:55 +0100 Subject: [PATCH 2/5] add tests and fix build --- ext/sockets/sockets.c | 10 ++- ext/sockets/tests/socket_vsock_errors.phpt | 65 +++++++++++++++ ext/sockets/tests/socket_vsock_loopback.phpt | 84 ++++++++++++++++++++ 3 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 ext/sockets/tests/socket_vsock_errors.phpt create mode 100644 ext/sockets/tests/socket_vsock_loopback.phpt diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index c4828ffb06db..133d089d4214 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -119,7 +119,11 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets) #endif #ifdef AF_VSOCK -#define PHP_VSOCK_ID_MAX ((int64_t) UINT32_MAX) +#if SIZEOF_ZEND_LONG > 4 +# define PHP_VSOCK_ID_OUT_OF_RANGE(v) ((v) < INT32_MIN || (v) > (zend_long) UINT32_MAX) +#else +# define PHP_VSOCK_ID_OUT_OF_RANGE(v) 0 +#endif static bool php_set_vsock_addr(struct sockaddr_vm *svm, zend_string *addr, zend_long port, uint32_t addr_arg_num, uint32_t port_arg_num) @@ -128,13 +132,13 @@ static bool php_set_vsock_addr(struct sockaddr_vm *svm, zend_string *addr, zend_ double dval; if (is_numeric_string(ZSTR_VAL(addr), ZSTR_LEN(addr), &cid, &dval, 0) != IS_LONG - || (int64_t) cid < INT32_MIN || (int64_t) cid > PHP_VSOCK_ID_MAX) { + || PHP_VSOCK_ID_OUT_OF_RANGE(cid)) { zend_argument_value_error(addr_arg_num, "must be a numeric context ID between 0 and " ZEND_ULONG_FMT, (zend_ulong) UINT32_MAX); return false; } - if ((int64_t) port < INT32_MIN || (int64_t) port > PHP_VSOCK_ID_MAX) { + if (PHP_VSOCK_ID_OUT_OF_RANGE(port)) { zend_argument_value_error(port_arg_num, "must be between 0 and " ZEND_ULONG_FMT, (zend_ulong) UINT32_MAX); return false; } diff --git a/ext/sockets/tests/socket_vsock_errors.phpt b/ext/sockets/tests/socket_vsock_errors.phpt new file mode 100644 index 000000000000..ab6fa0f4454e --- /dev/null +++ b/ext/sockets/tests/socket_vsock_errors.phpt @@ -0,0 +1,65 @@ +--TEST-- +AF_VSOCK address and port validation +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + socket_bind($socket, '1.5', 1024); +} catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + socket_bind($socket, '4294967296', 1024); +} catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + socket_bind($socket, (string) VMADDR_CID_LOCAL, 4294967296); +} catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + socket_connect($socket, (string) VMADDR_CID_LOCAL); +} catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + socket_sendto($socket, 'x', 1, 0, 'host', 1024); +} catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +var_dump(socket_bind($socket, (string) VMADDR_CID_LOCAL, VMADDR_PORT_ANY)); + +socket_close($socket); +?> +--EXPECT-- +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 +ValueError: socket_bind(): Argument #3 ($port) must be between 0 and 4294967295 +ValueError: socket_connect(): Argument #3 ($port) cannot be null when the socket type is AF_VSOCK +ValueError: socket_sendto(): Argument #5 ($address) must be a numeric context ID between 0 and 4294967295 +bool(true) diff --git a/ext/sockets/tests/socket_vsock_loopback.phpt b/ext/sockets/tests/socket_vsock_loopback.phpt new file mode 100644 index 000000000000..d363b5c6c833 --- /dev/null +++ b/ext/sockets/tests/socket_vsock_loopback.phpt @@ -0,0 +1,84 @@ +--TEST-- +AF_VSOCK stream round trip over the loopback transport +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +string(1) "1" +bool(true) +bool(true) +bool(true) +int(4) +string(4) "ping" +int(4) +int(4) +string(4) "pong" +int(4) +int(4) +string(4) "peng" +bool(true) +string(1) "1" +bool(true) +bool(true) +bool(true) From f80db3cb6a5832bbbdd5ddfee93f08c5f906f44c Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 8 Sep 2026 22:37:36 +0100 Subject: [PATCH 3/5] remove wrong test assumption --- ext/sockets/tests/socket_vsock_errors.phpt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ext/sockets/tests/socket_vsock_errors.phpt b/ext/sockets/tests/socket_vsock_errors.phpt index ab6fa0f4454e..affc8b323a58 100644 --- a/ext/sockets/tests/socket_vsock_errors.phpt +++ b/ext/sockets/tests/socket_vsock_errors.phpt @@ -33,12 +33,6 @@ try { echo $e::class, ': ', $e->getMessage(), PHP_EOL; } -try { - socket_bind($socket, (string) VMADDR_CID_LOCAL, 4294967296); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; -} - try { socket_connect($socket, (string) VMADDR_CID_LOCAL); } catch (ValueError $e) { @@ -59,7 +53,6 @@ socket_close($socket); ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 -ValueError: socket_bind(): Argument #3 ($port) must be between 0 and 4294967295 ValueError: socket_connect(): Argument #3 ($port) cannot be null when the socket type is AF_VSOCK ValueError: socket_sendto(): Argument #5 ($address) must be a numeric context ID between 0 and 4294967295 bool(true) From 4ad8b384ae716d8c01e76e6438d79267db340279 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 8 Sep 2026 22:48:18 +0100 Subject: [PATCH 4/5] add tests --- .../tests/socket_vsock_port_range.phpt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 ext/sockets/tests/socket_vsock_port_range.phpt diff --git a/ext/sockets/tests/socket_vsock_port_range.phpt b/ext/sockets/tests/socket_vsock_port_range.phpt new file mode 100644 index 000000000000..2f84acc45e54 --- /dev/null +++ b/ext/sockets/tests/socket_vsock_port_range.phpt @@ -0,0 +1,37 @@ +--TEST-- +AF_VSOCK rejects a port beyond the 32 bit range +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + socket_sendto($socket, 'x', 1, 0, (string) VMADDR_CID_LOCAL, 4294967296); +} catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +socket_close($socket); +?> +--EXPECT-- +ValueError: socket_bind(): Argument #3 ($port) must be between 0 and 4294967295 +ValueError: socket_sendto(): Argument #6 ($port) must be between 0 and 4294967295 From ea2438446ee0dba583921445d44534d9b29019c9 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 9 Sep 2026 06:32:40 +0100 Subject: [PATCH 5/5] fix tests, enable macos support (unlike Linux, it truly needs guest/host to really work no test can be exercised really). --- ext/sockets/config.m4 | 2 +- ext/sockets/sockets.c | 47 +++++++++++++++---- ext/sockets/sockets.stub.php | 2 + ext/sockets/sockets_arginfo.h | 6 ++- ext/sockets/tests/socket_vsock_errors.phpt | 32 ++++++------- ext/sockets/tests/socket_vsock_loopback.phpt | 3 ++ .../tests/socket_vsock_port_range.phpt | 39 +++++++++++---- 7 files changed, 96 insertions(+), 35 deletions(-) diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index 08ffeb037c0e..83b3734fc2f1 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -5,7 +5,7 @@ PHP_ARG_ENABLE([sockets], if test "$PHP_SOCKETS" != "no"; then AC_CHECK_FUNCS([hstrerror if_nametoindex if_indextoname sockatmark accept4]) - AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h linux/udp.h linux/vm_sockets.h]) + AC_CHECK_HEADERS([sys/sockio.h sys/vsock.h linux/filter.h linux/if_packet.h linux/if_ether.h linux/udp.h linux/vm_sockets.h]) AC_DEFINE([HAVE_SOCKETS], [1], [Define to 1 if the PHP extension 'sockets' is available.]) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 133d089d4214..ee72c671d79b 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -74,6 +74,9 @@ # endif # if defined(HAVE_LINUX_VM_SOCKETS_H) # include +# elif defined(HAVE_SYS_VSOCK_H) +# include +# define PHP_VSOCK_HAS_SVM_LEN 1 # else # undef AF_VSOCK # endif @@ -125,27 +128,55 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets) # define PHP_VSOCK_ID_OUT_OF_RANGE(v) 0 #endif +static bool php_vsock_parse_cid(zend_string *addr, uint32_t *cid) +{ + zend_long lval; + double dval; + + switch (is_numeric_string(ZSTR_VAL(addr), ZSTR_LEN(addr), &lval, &dval, 0)) { + case IS_LONG: + if (PHP_VSOCK_ID_OUT_OF_RANGE(lval)) { + return false; + } + *cid = (uint32_t) lval; + return true; + + case IS_DOUBLE: + if (!(dval >= (double) INT32_MIN && dval <= (double) UINT32_MAX) + || dval != (double) (int64_t) dval) { + return false; + } + *cid = (uint32_t) (int64_t) dval; + return true; + + default: + return false; + } +} + static bool php_set_vsock_addr(struct sockaddr_vm *svm, zend_string *addr, zend_long port, uint32_t addr_arg_num, uint32_t port_arg_num) { - zend_long cid; - double dval; + uint32_t cid; - if (is_numeric_string(ZSTR_VAL(addr), ZSTR_LEN(addr), &cid, &dval, 0) != IS_LONG - || PHP_VSOCK_ID_OUT_OF_RANGE(cid)) { - zend_argument_value_error(addr_arg_num, "must be a numeric context ID between 0 and " ZEND_ULONG_FMT, - (zend_ulong) UINT32_MAX); + if (!php_vsock_parse_cid(addr, &cid)) { + zend_argument_value_error(addr_arg_num, "must be a numeric context ID between %d and " ZEND_ULONG_FMT, + INT32_MIN, (zend_ulong) UINT32_MAX); return false; } if (PHP_VSOCK_ID_OUT_OF_RANGE(port)) { - zend_argument_value_error(port_arg_num, "must be between 0 and " ZEND_ULONG_FMT, (zend_ulong) UINT32_MAX); + zend_argument_value_error(port_arg_num, "must be between %d and " ZEND_ULONG_FMT, + INT32_MIN, (zend_ulong) UINT32_MAX); return false; } memset(svm, 0, sizeof(*svm)); +#ifdef PHP_VSOCK_HAS_SVM_LEN + svm->svm_len = sizeof(*svm); +#endif svm->svm_family = AF_VSOCK; - svm->svm_cid = (uint32_t) cid; + svm->svm_cid = cid; svm->svm_port = (uint32_t) port; return true; diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 15703d31082b..2aec81a9fbc1 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -56,11 +56,13 @@ * @cvalue VMADDR_CID_HYPERVISOR */ const VMADDR_CID_HYPERVISOR = UNKNOWN; +#ifdef VMADDR_CID_LOCAL /** * @var int * @cvalue VMADDR_CID_LOCAL */ const VMADDR_CID_LOCAL = UNKNOWN; +#endif /** * @var int * @cvalue VMADDR_CID_HOST diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 33752fe5955b..08f6c2efa7ba 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit sockets.stub.php instead. - * Stub hash: 8a5bc212b70cb5b74f99287d53b348707917f292 */ + * Stub hash: e3d05b2f0e972e7e9f8c8d7b90ce4f621aa49714 */ #include "zend_constants.h" @@ -333,7 +333,11 @@ static void register_sockets_symbols(int module_number) REGISTER_LONG_CONSTANT("AF_VSOCK", AF_VSOCK, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("VMADDR_CID_ANY", VMADDR_CID_ANY, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("VMADDR_CID_HYPERVISOR", VMADDR_CID_HYPERVISOR, CONST_PERSISTENT); +#endif +#if defined(AF_VSOCK) && defined(VMADDR_CID_LOCAL) REGISTER_LONG_CONSTANT("VMADDR_CID_LOCAL", VMADDR_CID_LOCAL, CONST_PERSISTENT); +#endif +#if defined(AF_VSOCK) REGISTER_LONG_CONSTANT("VMADDR_CID_HOST", VMADDR_CID_HOST, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("VMADDR_PORT_ANY", VMADDR_PORT_ANY, CONST_PERSISTENT); #endif diff --git a/ext/sockets/tests/socket_vsock_errors.phpt b/ext/sockets/tests/socket_vsock_errors.phpt index affc8b323a58..9a47be34e3ba 100644 --- a/ext/sockets/tests/socket_vsock_errors.phpt +++ b/ext/sockets/tests/socket_vsock_errors.phpt @@ -17,42 +17,42 @@ $socket = socket_create(AF_VSOCK, SOCK_STREAM, 0); try { socket_bind($socket, 'host', 1024); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } try { socket_bind($socket, '1.5', 1024); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } try { socket_bind($socket, '4294967296', 1024); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } try { - socket_connect($socket, (string) VMADDR_CID_LOCAL); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; + socket_connect($socket, (string) VMADDR_CID_ANY); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } try { socket_sendto($socket, 'x', 1, 0, 'host', 1024); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } -var_dump(socket_bind($socket, (string) VMADDR_CID_LOCAL, VMADDR_PORT_ANY)); +var_dump(socket_bind($socket, (string) VMADDR_CID_ANY, VMADDR_PORT_ANY)); socket_close($socket); ?> --EXPECT-- -ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 -ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 -ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between 0 and 4294967295 +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between -2147483648 and 4294967295 +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between -2147483648 and 4294967295 +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between -2147483648 and 4294967295 ValueError: socket_connect(): Argument #3 ($port) cannot be null when the socket type is AF_VSOCK -ValueError: socket_sendto(): Argument #5 ($address) must be a numeric context ID between 0 and 4294967295 +ValueError: socket_sendto(): Argument #5 ($address) must be a numeric context ID between -2147483648 and 4294967295 bool(true) diff --git a/ext/sockets/tests/socket_vsock_loopback.phpt b/ext/sockets/tests/socket_vsock_loopback.phpt index d363b5c6c833..8461a528381f 100644 --- a/ext/sockets/tests/socket_vsock_loopback.phpt +++ b/ext/sockets/tests/socket_vsock_loopback.phpt @@ -7,6 +7,9 @@ sockets if (!defined('AF_VSOCK')) { die('skip AF_VSOCK not available'); } +if (!defined('VMADDR_CID_LOCAL')) { + die('skip no vsock loopback context ID'); +} $socket = @socket_create(AF_VSOCK, SOCK_STREAM, 0); if ($socket === false) { die('skip no vsock transport available'); diff --git a/ext/sockets/tests/socket_vsock_port_range.phpt b/ext/sockets/tests/socket_vsock_port_range.phpt index 2f84acc45e54..3be0ca685ff3 100644 --- a/ext/sockets/tests/socket_vsock_port_range.phpt +++ b/ext/sockets/tests/socket_vsock_port_range.phpt @@ -1,5 +1,5 @@ --TEST-- -AF_VSOCK rejects a port beyond the 32 bit range +AF_VSOCK rejects a context ID or port beyond the 32 bit range --EXTENSIONS-- sockets --SKIPIF-- @@ -19,19 +19,40 @@ if (@socket_create(AF_VSOCK, SOCK_STREAM, 0) === false) { $socket = socket_create(AF_VSOCK, SOCK_STREAM, 0); try { - socket_bind($socket, (string) VMADDR_CID_LOCAL, 4294967296); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; + socket_bind($socket, (string) VMADDR_CID_ANY, 4294967296); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } try { - socket_sendto($socket, 'x', 1, 0, (string) VMADDR_CID_LOCAL, 4294967296); -} catch (ValueError $e) { - echo $e::class, ': ', $e->getMessage(), PHP_EOL; + socket_sendto($socket, 'x', 1, 0, (string) VMADDR_CID_ANY, 4294967296); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} + +try { + socket_bind($socket, (string) VMADDR_CID_ANY, -2147483649); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} + +try { + socket_bind($socket, '-2147483649', 0); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} + +try { + socket_bind($socket, '4294967296', 0); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } socket_close($socket); ?> --EXPECT-- -ValueError: socket_bind(): Argument #3 ($port) must be between 0 and 4294967295 -ValueError: socket_sendto(): Argument #6 ($port) must be between 0 and 4294967295 +ValueError: socket_bind(): Argument #3 ($port) must be between -2147483648 and 4294967295 +ValueError: socket_sendto(): Argument #6 ($port) must be between -2147483648 and 4294967295 +ValueError: socket_bind(): Argument #3 ($port) must be between -2147483648 and 4294967295 +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between -2147483648 and 4294967295 +ValueError: socket_bind(): Argument #2 ($address) must be a numeric context ID between -2147483648 and 4294967295