Commit e463914fb2f for php.net
commit e463914fb2f273ecc49f7ac0a7b089c9bcd835fa
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Tue Jun 16 14:38:31 2026 -0400
Fix socket_sendmsg() sending wrong fd for Socket objects in SCM_RIGHTS
The Socket-object branch of from_zval_write_fd_array_aux() indexed the fd
array with the 1-based loop counter (iarr[i]) instead of iarr[i - 1] like
the resource branch, leaving slot 0 zeroed so a single Socket delivered
fd 0 (stdin) to the receiver. Broken since the 8.0 resource-to-object
conversion.
Closes GH-22338
diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c
index a419a20c873..eb4e58b3988 100644
--- a/ext/sockets/conversions.c
+++ b/ext/sockets/conversions.c
@@ -1385,7 +1385,7 @@ static void from_zval_write_fd_array_aux(zval *elem, unsigned i, void **args, se
return;
}
- iarr[i] = sock->bsd_socket;
+ iarr[i - 1] = sock->bsd_socket;
return;
}
diff --git a/ext/sockets/tests/socket_cmsg_rights.phpt b/ext/sockets/tests/socket_cmsg_rights.phpt
index 1794eaf767a..8bfbcd57157 100644
--- a/ext/sockets/tests/socket_cmsg_rights.phpt
+++ b/ext/sockets/tests/socket_cmsg_rights.phpt
@@ -58,7 +58,7 @@
if ($control["level"] == SOL_SOCKET &&
$control["type"] == SCM_RIGHTS) {
foreach ($control["data"] as $resource) {
- if (!is_resource($resource)) {
+ if (!is_resource($resource) && !($resource instanceof Socket)) {
echo "FAIL RES\n";
var_dump($data);
exit;
diff --git a/ext/sockets/tests/socket_sendmsg_scm_rights_object.phpt b/ext/sockets/tests/socket_sendmsg_scm_rights_object.phpt
new file mode 100644
index 00000000000..907ea70ac16
--- /dev/null
+++ b/ext/sockets/tests/socket_sendmsg_scm_rights_object.phpt
@@ -0,0 +1,77 @@
+--TEST--
+socket_sendmsg(): SCM_RIGHTS transfers Socket objects with the correct fds
+--EXTENSIONS--
+sockets
+--SKIPIF--
+<?php
+if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
+ die('skip not for Microsoft Windows');
+}
+if (strtolower(substr(PHP_OS, 0, 3)) == 'aix') {
+ die('skip not for AIX');
+}
+if (!defined('SCM_RIGHTS')) {
+ die('skip SCM_RIGHTS not available');
+}
+?>
+--FILE--
+<?php
+$dir = sys_get_temp_dir();
+$rpath = $dir . "/socket_sendmsg_scm_rights_object_r.sock";
+$ppath1 = $dir . "/socket_sendmsg_scm_rights_object_p1.sock";
+$ppath2 = $dir . "/socket_sendmsg_scm_rights_object_p2.sock";
+@unlink($rpath);
+@unlink($ppath1);
+@unlink($ppath2);
+
+$recv = socket_create(AF_UNIX, SOCK_DGRAM, 0);
+socket_bind($recv, $rpath);
+
+$send = socket_create(AF_UNIX, SOCK_DGRAM, 0);
+socket_connect($send, $rpath);
+
+// Two Socket objects in a single SCM_RIGHTS message, each bound to a known
+// path so the received fds can be identified and ordered via getsockname().
+$payload1 = socket_create(AF_UNIX, SOCK_DGRAM, 0);
+socket_bind($payload1, $ppath1);
+$payload2 = socket_create(AF_UNIX, SOCK_DGRAM, 0);
+socket_bind($payload2, $ppath2);
+
+socket_sendmsg($send, [
+ 'iov' => ['x'],
+ 'control' => [[
+ 'level' => SOL_SOCKET,
+ 'type' => SCM_RIGHTS,
+ 'data' => [$payload1, $payload2],
+ ]],
+], 0);
+
+$data = [
+ 'name' => [],
+ 'buffer_size' => 64,
+ 'controllen' => socket_cmsg_space(SOL_SOCKET, SCM_RIGHTS, 2),
+];
+socket_recvmsg($recv, $data, 0);
+
+$got = $data['control'][0]['data'];
+var_dump(count($got));
+var_dump($got[0] instanceof Socket);
+var_dump($got[1] instanceof Socket);
+socket_getsockname($got[0], $addr0);
+socket_getsockname($got[1], $addr1);
+var_dump($addr0 === $ppath1);
+var_dump($addr1 === $ppath2);
+?>
+--CLEAN--
+<?php
+$dir = sys_get_temp_dir();
+@unlink($dir . "/socket_sendmsg_scm_rights_object_r.sock");
+@unlink($dir . "/socket_sendmsg_scm_rights_object_p1.sock");
+@unlink($dir . "/socket_sendmsg_scm_rights_object_p2.sock");
+?>
+--EXPECT--
+int(2)
+bool(true)
+bool(true)
+bool(true)
+bool(true)