Commit 7adcf5a580c for php

commit 7adcf5a580c3b6f2175dd2de053c1d440792ec26
Author: lazerg <lazerg2@gmail.com>
Date:   Mon Oct 26 20:38:02 2026 +0500

    ext/sockets: Handle declared object properties in socket_set_option()

    Read option values through a retained property table and resolve indirect
    slots before conversion.

    Fixes GH-24748
    Closes GH-24749

diff --git a/NEWS b/NEWS
index 4c27156901d..464c723e6aa 100644
--- a/NEWS
+++ b/NEWS
@@ -175,6 +175,10 @@ PHP                                                                        NEWS
   . Fixed bug GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c).
     (Lazizbek Ergashev)

+- Sockets:
+  . Fixed bug GH-24748 (socket_set_option() crashes with declared object
+    properties). (Lazizbek Ergashev)
+
 - Standard:
   . Fixed bug GH-24308 (Request shutdown calls closelog() even when syslog
     was not opened by PHP). (lazerg)
diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c
index 5e70df60726..c1e7ead5546 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 00000000000..84f2f127d00
--- /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--
+<?php
+class Linger {
+    public $l_onoff = 1;
+    public $l_linger = 5;
+}
+
+class Timeout {
+    public $sec = 1;
+    public int $usec;
+}
+
+$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+var_dump(socket_set_option($socket, SOL_SOCKET, SO_LINGER, new Linger()));
+var_dump(socket_get_option($socket, SOL_SOCKET, SO_LINGER)["l_linger"]);
+
+try {
+    socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, new Timeout());
+} catch (ValueError $e) {
+    echo $e->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