From ce7ecaa3deefdf956bf9803ad6537a544105019d Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 14 Jun 2026 12:09:36 -0400 Subject: [PATCH] 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. --- ext/standard/head.c | 9 +++++++++ .../setcookie_option_case_variant_leak.phpt | 15 +++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 ext/standard/tests/network/setcookie_option_case_variant_leak.phpt diff --git a/ext/standard/head.c b/ext/standard/head.c index ccef4be16bdf..27626e15f2d3 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 000000000000..4797d2f259c9 --- /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-- + '/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)