diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index e1b350b4045a4..e6e231e2e5e7e 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1871,8 +1871,16 @@ PHP_FUNCTION(socket_set_option) const char l_onoff_key[] = "l_onoff"; const char l_linger_key[] = "l_linger"; - convert_to_array(arg4); - opt_ht = Z_ARRVAL_P(arg4); + if (Z_TYPE_P(arg4) != IS_ARRAY) { + if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) { + zend_argument_type_error(4, "must be of type array when argument #3 ($option) is SO_LINGER, %s given", zend_zval_value_name(arg4)); + RETURN_THROWS(); + } else { + opt_ht = Z_OBJPROP_P(arg4); + } + } else { + opt_ht = Z_ARRVAL_P(arg4); + } if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) { zend_argument_value_error(4, "must have key \"%s\"", l_onoff_key); @@ -1883,11 +1891,21 @@ PHP_FUNCTION(socket_set_option) RETURN_THROWS(); } - convert_to_long(l_onoff); - convert_to_long(l_linger); + zend_long val_lonoff = zval_get_long(l_onoff); + zend_long val_linger = zval_get_long(l_linger); + + if (val_lonoff < 0 || val_lonoff > USHRT_MAX) { + zend_argument_value_error(4, "\"%s\" must be between 0 and %u", l_onoff_key, USHRT_MAX); + RETURN_THROWS(); + } + + if (val_linger < 0 || val_linger > USHRT_MAX) { + zend_argument_value_error(4, "\"%s\" must be between 0 and %d", l_linger, USHRT_MAX); + RETURN_THROWS(); + } - lv.l_onoff = (unsigned short)Z_LVAL_P(l_onoff); - lv.l_linger = (unsigned short)Z_LVAL_P(l_linger); + lv.l_onoff = (unsigned short)val_lonoff; + lv.l_linger = (unsigned short)val_linger; optlen = sizeof(lv); opt_ptr = &lv; @@ -1899,8 +1917,18 @@ PHP_FUNCTION(socket_set_option) const char sec_key[] = "sec"; const char usec_key[] = "usec"; - convert_to_array(arg4); - opt_ht = Z_ARRVAL_P(arg4); + if (Z_TYPE_P(arg4) != IS_ARRAY) { + if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) { + zend_argument_type_error(4, "must be of type array when argument #3 ($option) is %s, %s given", + optname == SO_RCVTIMEO ? "SO_RCVTIMEO" : "SO_SNDTIMEO", + zend_zval_value_name(arg4)); + RETURN_THROWS(); + } else { + opt_ht = Z_OBJPROP_P(arg4); + } + } else { + opt_ht = Z_ARRVAL_P(arg4); + } if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) { zend_argument_value_error(4, "must have key \"%s\"", sec_key); @@ -1911,15 +1939,16 @@ PHP_FUNCTION(socket_set_option) RETURN_THROWS(); } - convert_to_long(sec); - convert_to_long(usec); + zend_long valsec = zval_get_long(sec); + zend_long valusec = zval_get_long(usec); #ifndef PHP_WIN32 - tv.tv_sec = Z_LVAL_P(sec); - tv.tv_usec = Z_LVAL_P(usec); + tv.tv_sec = valsec; + tv.tv_usec = valusec; optlen = sizeof(tv); opt_ptr = &tv; #else - timeout = Z_LVAL_P(sec) * 1000 + Z_LVAL_P(usec) / 1000; + timeout = valsec * 1000 + valusec / 1000; + optlen = sizeof(int); opt_ptr = &timeout; #endif @@ -1971,15 +2000,15 @@ PHP_FUNCTION(socket_set_option) #ifdef SO_ATTACH_REUSEPORT_CBPF case SO_ATTACH_REUSEPORT_CBPF: { - convert_to_long(arg4); + zend_long cbpf_val = zval_get_long(arg4); - if (!Z_LVAL_P(arg4)) { + if (!cbpf_val) { ov = 1; optlen = sizeof(ov); opt_ptr = &ov; optname = SO_DETACH_BPF; } else { - uint32_t k = (uint32_t)Z_LVAL_P(arg4); + uint32_t k = (uint32_t)cbpf_val; static struct sock_filter cbpf[8] = {0}; static struct sock_fprog bpfprog; @@ -2006,8 +2035,7 @@ PHP_FUNCTION(socket_set_option) default: default_case: - convert_to_long(arg4); - ov = Z_LVAL_P(arg4); + ov = zval_get_long(arg4); optlen = sizeof(ov); opt_ptr = &ov; diff --git a/ext/sockets/tests/socket_reuseport_cbpf.phpt b/ext/sockets/tests/socket_reuseport_cbpf.phpt index 1d4824dca1c81..2210c4438f000 100644 --- a/ext/sockets/tests/socket_reuseport_cbpf.phpt +++ b/ext/sockets/tests/socket_reuseport_cbpf.phpt @@ -18,13 +18,20 @@ 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()); +} catch (\TypeError $e) { + echo $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); ?> ---EXPECT-- +--EXPECTF-- bool(true) bool(true) + +Warning: socket_set_option(): Unable to set socket option [2]: No such file or directory in %s on line %d bool(true) bool(true) diff --git a/ext/sockets/tests/socket_set_option_timeo_error.phpt b/ext/sockets/tests/socket_set_option_timeo_error.phpt new file mode 100644 index 0000000000000..1db5e01c36228 --- /dev/null +++ b/ext/sockets/tests/socket_set_option_timeo_error.phpt @@ -0,0 +1,66 @@ +--TEST-- +socket_set_option() with SO_RCVTIMEO/SO_SNDTIMEO/SO_LINGER +--EXTENSIONS-- +sockets +--FILE-- + 1, "usec" => "aaaaa"); +$options_2 = array("sec" => new stdClass(), "usec" => "1"); +$options_3 = array("l_onoff" => "aaaa", "l_linger" => "1"); +$options_4 = array("l_onoff" => "1", "l_linger" => []); +$options_5 = array("l_onoff" => PHP_INT_MAX, "l_linger" => "1"); + +try { + socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, new stdClass); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, $options_1); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, $options_2); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, "not good"); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, "not good neither"); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_3); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_4); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_5); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} +?> +--EXPECTF-- +socket_set_option(): Argument #4 ($value) must have key "sec" + +Warning: Object of class stdClass could not be converted to int in %s on line %d +socket_set_option(): Argument #4 ($value) must be of type array when argument #3 ($option) is SO_RCVTIMEO, string given +socket_set_option(): Argument #4 ($value) must be of type array when argument #3 ($option) is SO_LINGER, string given +socket_set_option(): Argument #4 ($value) "l_onoff" must be between 0 and %d