Commit 0c52780287d for php.net

commit 0c52780287d92d6e7479aa16e5279026b9ed106d
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Sun Jun 14 12:09:36 2026 -0400

    Fix zend_string leak on case-variant duplicate setcookie() options

    php_head_parse_cookie_options_array() matches option keys case
    insensitively, but array keys are case sensitive, so a duplicate
    differing only in case (e.g. "path" and "Path") overwrote the
    previously fetched path/domain/samesite string without releasing it.
    Release any value already stored before fetching the next one.

    Closes GH-22309

diff --git a/ext/standard/head.c b/ext/standard/head.c
index ccef4be16bd..27626e15f2d 100644
--- a/ext/standard/head.c
+++ b/ext/standard/head.c
@@ -205,14 +205,23 @@ static zend_result php_head_parse_cookie_options_array(HashTable *options, zend_
 		if (zend_string_equals_literal_ci(key, "expires")) {
 			*expires = zval_get_long(value);
 		} else if (zend_string_equals_literal_ci(key, "path")) {
+			if (*path) {
+				zend_string_release(*path);
+			}
 			*path = zval_get_string(value);
 		} else if (zend_string_equals_literal_ci(key, "domain")) {
+			if (*domain) {
+				zend_string_release(*domain);
+			}
 			*domain = zval_get_string(value);
 		} else if (zend_string_equals_literal_ci(key, "secure")) {
 			*secure = zval_is_true(value);
 		} else if (zend_string_equals_literal_ci(key, "httponly")) {
 			*httponly = zval_is_true(value);
 		} else if (zend_string_equals_literal_ci(key, "samesite")) {
+			if (*samesite) {
+				zend_string_release(*samesite);
+			}
 			*samesite = zval_get_string(value);
 		} else {
 			zend_value_error("%s(): option \"%s\" is invalid", get_active_function_name(), ZSTR_VAL(key));
diff --git a/ext/standard/tests/network/setcookie_option_case_variant_leak.phpt b/ext/standard/tests/network/setcookie_option_case_variant_leak.phpt
new file mode 100644
index 00000000000..4797d2f259c
--- /dev/null
+++ b/ext/standard/tests/network/setcookie_option_case_variant_leak.phpt
@@ -0,0 +1,15 @@
+--TEST--
+setcookie() does not leak when an option array has case-variant duplicate keys
+--FILE--
+<?php
+$base = memory_get_usage();
+for ($i = 0; $i < 5000; $i++) {
+    @setcookie('name', 'value', ['path' => '/aaaaaaaaaaaaaaaa' . $i, 'Path' => '/bbbbbbbbbbbbbbbb' . $i]);
+    header_remove();
+}
+// Each duplicate-key call leaked the first path string before the fix,
+// growing usage by tens of bytes per iteration (hundreds of KB here).
+var_dump(memory_get_usage() - $base < 50000);
+?>
+--EXPECT--
+bool(true)