Commit 383ff8c63f3 for php.net
commit 383ff8c63f3bfc3b28704dd75055ca8f7a105e50
Author: Jorg Adam Sowa <jorg.sowa@gmail.com>
Date: Mon Apr 20 17:45:06 2026 +0200
ext/session: improve parsing of session.cookie_lifetime (#21704)
When session.cookie_lifetime was set to a value larger than maxcookie,
OnUpdateCookieLifetime returned SUCCESS without updating the internal
long value, causing ini_get() string and PS(cookie_lifetime) to go
out of sync.
We now properly parse the string value of the ini setting and fail when it is not an integer string or is not within the expected range.
diff --git a/ext/session/session.c b/ext/session/session.c
index 14b443bf4c6..96e32ea7043 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -704,12 +704,20 @@ static PHP_INI_MH(OnUpdateCookieLifetime)
#else
const zend_long maxcookie = ZEND_LONG_MAX / 2 - 1;
#endif
- zend_long v = (zend_long)atol(ZSTR_VAL(new_value));
- if (v < 0) {
- php_error_docref(NULL, E_WARNING, "CookieLifetime cannot be negative");
+ zend_long lval = 0;
+ int oflow = 0;
+ uint8_t type = is_numeric_string_ex(ZSTR_VAL(new_value), ZSTR_LEN(new_value), &lval, NULL, false, &oflow, NULL);
+ if (UNEXPECTED(type != IS_LONG)) {
+ if (oflow != 0) {
+ php_error_docref(NULL, E_WARNING, "session.cookie_lifetime must be between 0 and " ZEND_LONG_FMT, maxcookie);
+ } else {
+ php_error_docref(NULL, E_WARNING, "session.cookie_lifetime must be of type int");
+ }
+ return FAILURE;
+ }
+ if (lval < 0 || lval > maxcookie) {
+ php_error_docref(NULL, E_WARNING, "session.cookie_lifetime must be between 0 and " ZEND_LONG_FMT, maxcookie);
return FAILURE;
- } else if (v > maxcookie) {
- return SUCCESS;
}
return OnUpdateLongGEZero(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
diff --git a/ext/session/tests/gh16290.phpt b/ext/session/tests/gh16290.phpt
index d341eb47471..df57b129649 100644
--- a/ext/session/tests/gh16290.phpt
+++ b/ext/session/tests/gh16290.phpt
@@ -6,8 +6,11 @@
<?php include('skipif.inc'); ?>
--FILE--
<?php
+ob_start();
session_set_cookie_params(PHP_INT_MAX, '/', null, false, true);
echo "DONE";
+ob_end_flush();
?>
---EXPECT--
+--EXPECTF--
+Warning: session_set_cookie_params(): session.cookie_lifetime must be between 0 and %d in %s on line %d
DONE
diff --git a/ext/session/tests/session_cookie_lifetime_invalid.phpt b/ext/session/tests/session_cookie_lifetime_invalid.phpt
new file mode 100644
index 00000000000..8665a53447b
--- /dev/null
+++ b/ext/session/tests/session_cookie_lifetime_invalid.phpt
@@ -0,0 +1,55 @@
+--TEST--
+session.cookie_lifetime rejects invalid values
+--EXTENSIONS--
+session
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+ini_set("session.cookie_lifetime", 100);
+
+// Float strings are rejected
+ini_set("session.cookie_lifetime", "1.5");
+var_dump(ini_get("session.cookie_lifetime"));
+
+// Non-numeric strings are rejected
+ini_set("session.cookie_lifetime", "abc");
+var_dump(ini_get("session.cookie_lifetime"));
+
+// Negative values are rejected
+ini_set("session.cookie_lifetime", -1);
+var_dump(ini_get("session.cookie_lifetime"));
+
+// Negative overflow strings are rejected
+ini_set("session.cookie_lifetime", "-99999999999999999999");
+var_dump(ini_get("session.cookie_lifetime"));
+
+// Overflow values are rejected
+ini_set("session.cookie_lifetime", PHP_INT_MAX);
+var_dump(ini_get("session.cookie_lifetime"));
+
+// Valid values still work after rejection
+ini_set("session.cookie_lifetime", 200);
+var_dump(ini_get("session.cookie_lifetime"));
+
+ob_end_flush();
+?>
+--EXPECTF--
+Warning: ini_set(): session.cookie_lifetime must be of type int in %s on line %d
+string(3) "100"
+
+Warning: ini_set(): session.cookie_lifetime must be of type int in %s on line %d
+string(3) "100"
+
+Warning: ini_set(): session.cookie_lifetime must be between 0 and %d in %s on line %d
+string(3) "100"
+
+Warning: ini_set(): session.cookie_lifetime must be between 0 and %d in %s on line %d
+string(3) "100"
+
+Warning: ini_set(): session.cookie_lifetime must be between 0 and %d in %s on line %d
+string(3) "100"
+string(3) "200"
diff --git a/ext/session/tests/session_get_cookie_params_basic.phpt b/ext/session/tests/session_get_cookie_params_basic.phpt
index 1c7cdf189fc..73ffd4c94d1 100644
--- a/ext/session/tests/session_get_cookie_params_basic.phpt
+++ b/ext/session/tests/session_get_cookie_params_basic.phpt
@@ -22,7 +22,7 @@
var_dump(session_get_cookie_params());
var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE));
var_dump(session_get_cookie_params());
-var_dump(session_set_cookie_params(1234567890, "/guff", "foo", TRUE, TRUE));
+var_dump(session_set_cookie_params(1000000000, "/guff", "foo", TRUE, TRUE));
var_dump(session_get_cookie_params());
var_dump(session_set_cookie_params([
"lifetime" => 123,
@@ -40,7 +40,7 @@
echo "Done";
ob_end_flush();
?>
---EXPECTF--
+--EXPECT--
*** Testing session_get_cookie_params() : basic functionality ***
array(7) {
["lifetime"]=>
@@ -78,7 +78,7 @@
bool(true)
array(7) {
["lifetime"]=>
- int(%d)
+ int(1000000000)
["path"]=>
string(5) "/guff"
["domain"]=>
diff --git a/ext/session/tests/session_set_cookie_params_basic.phpt b/ext/session/tests/session_set_cookie_params_basic.phpt
index 386280d1786..27cfd59b183 100644
--- a/ext/session/tests/session_set_cookie_params_basic.phpt
+++ b/ext/session/tests/session_set_cookie_params_basic.phpt
@@ -15,7 +15,7 @@
var_dump(session_start());
var_dump(session_set_cookie_params(1800));
var_dump(session_destroy());
-var_dump(session_set_cookie_params(1234567890));
+var_dump(session_set_cookie_params(1000000000));
echo "Done";
ob_end_flush();
diff --git a/ext/session/tests/session_set_cookie_params_variation1.phpt b/ext/session/tests/session_set_cookie_params_variation1.phpt
index ed0b8dc9755..ce4b98457be 100644
--- a/ext/session/tests/session_set_cookie_params_variation1.phpt
+++ b/ext/session/tests/session_set_cookie_params_variation1.phpt
@@ -24,7 +24,7 @@
var_dump(session_destroy());
var_dump(ini_get("session.cookie_lifetime"));
-var_dump(session_set_cookie_params(1234567890));
+var_dump(session_set_cookie_params(1000000000));
var_dump(ini_get("session.cookie_lifetime"));
echo "Done";
@@ -44,5 +44,5 @@
bool(true)
string(4) "3600"
bool(true)
-string(10) "1234567890"
+string(10) "1000000000"
Done
diff --git a/ext/session/tests/session_set_cookie_params_variation8.phpt b/ext/session/tests/session_set_cookie_params_variation8.phpt
index 891133b5b01..1497ded69d5 100644
--- a/ext/session/tests/session_set_cookie_params_variation8.phpt
+++ b/ext/session/tests/session_set_cookie_params_variation8.phpt
@@ -25,7 +25,7 @@
string(1) "0"
string(1) "0"
-Warning: session_set_cookie_params(): CookieLifetime cannot be negative in %s on line %d
+Warning: session_set_cookie_params(): session.cookie_lifetime must be between 0 and %d in %s on line %d
bool(false)
string(1) "0"
Done