Commit 1b61d555fb4 for php.net

commit 1b61d555fb4703ce69791d74a4f0532b45942938
Author: David Carlier <devnexen@gmail.com>
Date:   Mon Mar 9 22:03:31 2026 +0000

    ext/soap: Fix wrong cookie options offset calculation, using separator offset instead.

    The cookie option parser uses a wrong offset to start scanning
    attributes, causing cookie values containing substrings like
    "path=" or "domain=" to be falsely matched as attributes.

    close GH-21400

diff --git a/NEWS b/NEWS
index 542447107fa..f4c0f534c36 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,10 @@ PHP                                                                        NEWS
   . Fixed bug GH-21336 (SNMP::setSecurity() undefined behavior with
     NULL arguments). (David Carlier)

+- SOAP:
+  . Fixed Set-Cookie parsing bug wrong offset while scanning attributes.
+    (David Carlier)
+
 - Standard:
   . Fixed bug GH-20906 (Assertion failure when messing up output buffers).
     (ndossche)
diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index 2db45fe49b1..0bec42afbc3 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -1010,23 +1010,23 @@ int make_http_soap_request(zval        *this_ptr,
 		char *sempos = strstr(cookie, ";");
 		if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
 			smart_str name = {0};
-			int cookie_len;
 			zval zcookie;
+			size_t cookie_value_len;

 			if (sempos != NULL) {
-				cookie_len = sempos-(eqpos+1);
+				cookie_value_len = sempos-(eqpos+1);
 			} else {
-				cookie_len = strlen(cookie)-(eqpos-cookie)-1;
+				cookie_value_len = strlen(cookie)-(eqpos-cookie)-1;
 			}

 			smart_str_appendl(&name, cookie, eqpos - cookie);
 			smart_str_0(&name);

 			array_init(&zcookie);
-			add_index_stringl(&zcookie, 0, eqpos + 1, cookie_len);
+			add_index_stringl(&zcookie, 0, eqpos + 1, cookie_value_len);

 			if (sempos != NULL) {
-				char *options = cookie + cookie_len+1;
+				char *options = sempos + 1;
 				while (*options) {
 					while (*options == ' ') {options++;}
 					sempos = strstr(options, ";");
diff --git a/ext/soap/tests/bugs/cookie_parse_options_offset.phpt b/ext/soap/tests/bugs/cookie_parse_options_offset.phpt
new file mode 100644
index 00000000000..988af9d3195
--- /dev/null
+++ b/ext/soap/tests/bugs/cookie_parse_options_offset.phpt
@@ -0,0 +1,61 @@
+--TEST--
+SOAP Set-Cookie option parsing starts at wrong offset due to variable shadowing
+--EXTENSIONS--
+soap
+--SKIPIF--
+<?php
+if (!file_exists(__DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc")) {
+    echo "skip sapi/cli/tests/php_cli_server.inc required but not found";
+}
+?>
+--FILE--
+<?php
+
+include __DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc";
+
+$args = ["-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=" . (substr(PHP_OS, 0, 3) == "WIN" ? "php_" : "") . "soap." . PHP_SHLIB_SUFFIX];
+if (php_ini_loaded_file()) {
+    $args[] = "-c";
+    $args[] = php_ini_loaded_file();
+}
+
+// A 10-char name makes the wrong offset land exactly on the value "path=/evil",
+// falsely matching it as a path attribute.
+$code = <<<'PHP'
+header("Content-Type: text/xml");
+header("Set-Cookie: sessionkey=path=/evil;domain=good.com");
+echo <<<XML
+<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="test-uri">
+  <SOAP-ENV:Body>
+    <ns1:testResponse/>
+  </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+XML;
+PHP;
+
+php_cli_server_start($code, null, $args);
+
+$client = new SoapClient(null, [
+    'location' => 'http://' . PHP_CLI_SERVER_ADDRESS . '/test/endpoint',
+    'uri' => 'test-uri',
+    'trace' => true,
+]);
+
+try {
+    $client->__soapCall("test", []);
+} catch (SoapFault $e) {
+    // Response parsing may fault, cookies are still stored
+}
+
+$cookies = $client->__getCookies();
+
+// path should default to "/test" from the request URI, not "/evil" from the value.
+echo "value: " . $cookies['sessionkey'][0] . "\n";
+echo "path: " . $cookies['sessionkey'][1] . "\n";
+echo "domain: " . $cookies['sessionkey'][2] . "\n";
+?>
+--EXPECT--
+value: path=/evil
+path: /test
+domain: good.com