diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 5e70df60726e..c1e7ead55465 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1881,7 +1881,7 @@ PHP_FUNCTION(socket_set_option) #endif zend_long level, optname; void *opt_ptr; - HashTable *opt_ht; + HashTable *opt_ht, *opt_props = NULL; zval *l_onoff, *l_linger; zval *sec, *usec; @@ -1955,23 +1955,27 @@ PHP_FUNCTION(socket_set_option) 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); + opt_props = zend_get_properties_for(arg4, ZEND_PROP_PURPOSE_ARRAY_CAST); + opt_ht = opt_props ? opt_props : (HashTable *) &zend_empty_array; } } 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) { + if ((l_onoff = zend_hash_str_find_ind(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) { + zend_release_properties(opt_props); zend_argument_value_error(4, "must have key \"%s\"", l_onoff_key); RETURN_THROWS(); } - if ((l_linger = zend_hash_str_find(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == NULL) { + if ((l_linger = zend_hash_str_find_ind(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == NULL) { + zend_release_properties(opt_props); zend_argument_value_error(4, "must have key \"%s\"", l_linger_key); RETURN_THROWS(); } zend_long val_lonoff = zval_get_long(l_onoff); zend_long val_linger = zval_get_long(l_linger); + zend_release_properties(opt_props); 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); @@ -1979,7 +1983,7 @@ PHP_FUNCTION(socket_set_option) } if (val_linger < 0 || val_linger > USHRT_MAX) { - zend_argument_value_error(4, "\"%s\" must be between 0 and %d", l_linger, USHRT_MAX); + zend_argument_value_error(4, "\"%s\" must be between 0 and %d", l_linger_key, USHRT_MAX); RETURN_THROWS(); } @@ -2003,23 +2007,27 @@ PHP_FUNCTION(socket_set_option) zend_zval_value_name(arg4)); RETURN_THROWS(); } else { - opt_ht = Z_OBJPROP_P(arg4); + opt_props = zend_get_properties_for(arg4, ZEND_PROP_PURPOSE_ARRAY_CAST); + opt_ht = opt_props ? opt_props : (HashTable *) &zend_empty_array; } } else { opt_ht = Z_ARRVAL_P(arg4); } - if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) { + if ((sec = zend_hash_str_find_ind(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) { + zend_release_properties(opt_props); zend_argument_value_error(4, "must have key \"%s\"", sec_key); RETURN_THROWS(); } - if ((usec = zend_hash_str_find(opt_ht, usec_key, sizeof(usec_key) - 1)) == NULL) { + if ((usec = zend_hash_str_find_ind(opt_ht, usec_key, sizeof(usec_key) - 1)) == NULL) { + zend_release_properties(opt_props); zend_argument_value_error(4, "must have key \"%s\"", usec_key); RETURN_THROWS(); } zend_long valsec = zval_get_long(sec); zend_long valusec = zval_get_long(usec); + zend_release_properties(opt_props); #ifndef PHP_WIN32 tv.tv_sec = valsec; tv.tv_usec = valusec; diff --git a/ext/sockets/tests/gh24748.phpt b/ext/sockets/tests/gh24748.phpt new file mode 100644 index 000000000000..84f2f127d005 --- /dev/null +++ b/ext/sockets/tests/gh24748.phpt @@ -0,0 +1,64 @@ +--TEST-- +GH-24748 (socket_set_option() crashes when the value is an object with declared properties) +--EXTENSIONS-- +sockets +--FILE-- +getMessage(), "\n"; +} + +$timeout = new Timeout(); +$timeout->usec = 0; +var_dump(socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)); +var_dump(socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)); +var_dump(socket_get_option($socket, SOL_SOCKET, SO_SNDTIMEO)); + +$linger = new stdClass(); +$linger->l_onoff = new stdClass(); +$linger->l_linger = 5; +set_error_handler(function () use ($linger) { + $tmp = (array) $linger; + $linger->extra = 1; + unset($tmp); + return true; +}); +var_dump(socket_set_option($socket, SOL_SOCKET, SO_LINGER, $linger)); +restore_error_handler(); + +try { + socket_set_option($socket, SOL_SOCKET, SO_LINGER, new ArrayObject(['l_onoff' => 1, 'l_linger' => 70000])); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +bool(true) +int(5) +socket_set_option(): Argument #4 ($value) must have key "usec" +bool(true) +bool(true) +array(2) { + ["sec"]=> + int(1) + ["usec"]=> + int(0) +} +bool(true) +socket_set_option(): Argument #4 ($value) "l_linger" must be between 0 and 65535