From d770856942469672ab5a01b6e764564c34719ffa Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 14 Aug 2026 15:38:43 +0100 Subject: [PATCH] ext/sockets: CBPF reuseport CBPF filter improvements/strengthening. A zero SO_ATTACH_REUSEPORT_CBPF value detached via SO_DETACH_BPF, an alias of SO_DETACH_FILTER, leaving the reuseport program attached. SO_DETACH_REUSEPORT_BPF is used and exported now, the value is restricted to integers and the level is validated. --- ext/sockets/sockets.c | 15 +++++++- ext/sockets/sockets.stub.php | 7 ++++ ext/sockets/sockets_arginfo.h | 5 ++- ext/sockets/tests/socket_reuseport_cbpf.phpt | 9 ++--- .../tests/socket_reuseport_cbpf_detach.phpt | 38 +++++++++++++++++++ 5 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 ext/sockets/tests/socket_reuseport_cbpf_detach.phpt diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index c4c6f224dcfa..66df65657988 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -2341,13 +2341,26 @@ PHP_FUNCTION(socket_set_option) #ifdef SO_ATTACH_REUSEPORT_CBPF case SO_ATTACH_REUSEPORT_CBPF: { + if (level != SOL_SOCKET) { + php_error_docref(NULL, E_WARNING, "Invalid level"); + RETURN_FALSE; + } + if (Z_TYPE_P(arg4) != IS_LONG) { + zend_argument_type_error(4, "must be of type int when argument #3 ($option) is SO_ATTACH_REUSEPORT_CBPF, %s given", zend_zval_value_name(arg4)); + RETURN_THROWS(); + } zend_long cbpf_val = zval_get_long(arg4); if (!cbpf_val) { +#ifdef SO_DETACH_REUSEPORT_BPF ov = 1; optlen = sizeof(ov); opt_ptr = &ov; - optname = SO_DETACH_BPF; + optname = SO_DETACH_REUSEPORT_BPF; +#else + php_error_docref(NULL, E_WARNING, "Detaching a reuseport CBPF filter is unsupported"); + RETURN_FALSE; +#endif } else { uint32_t k = (uint32_t)cbpf_val; diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 56b2ac07e868..8dec5f150005 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -1898,6 +1898,13 @@ */ const SO_ATTACH_REUSEPORT_CBPF = UNKNOWN; #endif +#if defined(SO_DETACH_REUSEPORT_BPF) +/** + * @var int + * @cvalue SO_DETACH_REUSEPORT_BPF + */ +const SO_DETACH_REUSEPORT_BPF = UNKNOWN; +#endif #if defined(SO_DETACH_FILTER) /** * @var int diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 2592cb740865..e91ee0a0e394 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: 5e71ef16f2121bd6c75794673d0e0a394759ff8b */ + * Stub hash: 3e496ebadbd9c303b771e54d831947b964eec537 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -990,6 +990,9 @@ static void register_sockets_symbols(int module_number) #if defined(SO_ATTACH_REUSEPORT_CBPF) REGISTER_LONG_CONSTANT("SO_ATTACH_REUSEPORT_CBPF", SO_ATTACH_REUSEPORT_CBPF, CONST_PERSISTENT); #endif +#if defined(SO_DETACH_REUSEPORT_BPF) + REGISTER_LONG_CONSTANT("SO_DETACH_REUSEPORT_BPF", SO_DETACH_REUSEPORT_BPF, CONST_PERSISTENT); +#endif #if defined(SO_DETACH_FILTER) REGISTER_LONG_CONSTANT("SO_DETACH_FILTER", SO_DETACH_FILTER, CONST_PERSISTENT); #endif diff --git a/ext/sockets/tests/socket_reuseport_cbpf.phpt b/ext/sockets/tests/socket_reuseport_cbpf.phpt index 2210c4438f00..8a96e694a3d9 100644 --- a/ext/sockets/tests/socket_reuseport_cbpf.phpt +++ b/ext/sockets/tests/socket_reuseport_cbpf.phpt @@ -19,19 +19,18 @@ if (!$socket) { var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEADDR, true)); var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEPORT, true)); try { - socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, array()); + socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, []); } catch (\TypeError $e) { - echo $e->getMessage() . PHP_EOL; + echo $e::class, ': ', $e->getMessage(), PHP_EOL; } var_dump(socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SKF_AD_CPU)); var_dump(socket_bind($socket, '0.0.0.0')); socket_listen($socket); socket_close($socket); ?> ---EXPECTF-- +--EXPECT-- bool(true) bool(true) - -Warning: socket_set_option(): Unable to set socket option [2]: No such file or directory in %s on line %d +TypeError: socket_set_option(): Argument #4 ($value) must be of type int when argument #3 ($option) is SO_ATTACH_REUSEPORT_CBPF, array given bool(true) bool(true) diff --git a/ext/sockets/tests/socket_reuseport_cbpf_detach.phpt b/ext/sockets/tests/socket_reuseport_cbpf_detach.phpt new file mode 100644 index 000000000000..bd1608a132d5 --- /dev/null +++ b/ext/sockets/tests/socket_reuseport_cbpf_detach.phpt @@ -0,0 +1,38 @@ +--TEST-- +socket_set_option() attach/detach round trip for reuseport CBPF filters +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) + +Warning: socket_set_option(): Unable to set socket option [%d]: %s in %s on line %d +bool(false) +bool(true) +bool(true)