Commit 48d588d2530 for php.net

commit 48d588d2530163071db9f0a01c5aa44a23dd6723
Author: Jorg Adam Sowa <jorg.sowa@gmail.com>
Date:   Mon May 18 13:50:52 2026 +0200

    ext/session: reject null bytes in session.cookie_path, session.cookie_domain, session.cache_limiter (#22074)

    Reject null bytes in `session.cookie_path`, `session.cookie_domain`, `session.cache_limiter`.

    - `cookie_path` / `cookie_domain`: When the session cookie was sent, `php_session_send_cookie()` built the Set-Cookie header including the raw null byte. The SAPI layer (sapi_header_op) emitted a generic `E_WARNING: Header may not contain NUL bytes` and returned FAILURE, but `php_session_send_cookie()` did not check that return value and returned SUCCESS — so the session cookie was silently dropped while `session_start()` still returned true.
    - `cache_limiter`: The limiter name is looked up via `strcasecmp(lim->name, ZSTR_VAL(PS(cache_limiter)))`, which stops at the null byte, so "nocache\0evil" would silently behave as "nocache". The null byte was effectively ignored but still accepted without complaint.

diff --git a/NEWS b/NEWS
index f86892d0212..239becd194f 100644
--- a/NEWS
+++ b/NEWS
@@ -154,6 +154,8 @@ PHP                                                                        NEWS
 - Session:
   . Fixed bug 71162 (updateTimestamp never called when session data is empty).
     (Girgias)
+  . Null bytes in session.cookie_path, session.cookie_domain, and
+    session.cache_limiter are now rejected with a warning. (jorgsowa)

 - Soap:
   . Soap::__setCookie() when cookie name is a digit is now not stored and
diff --git a/UPGRADING b/UPGRADING
index 5aa617b99fd..1bba53274c1 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -75,6 +75,11 @@ PHP 8.6 UPGRADE NOTES
     argument value is passed.

 - Session:
+  . Setting session.cookie_path, session.cookie_domain, or session.cache_limiter
+    to a value containing null bytes now emits a warning and leaves the setting
+    unchanged. Previously, null bytes were silently accepted: for cookie_path and
+    cookie_domain this caused the SAPI to drop the Set-Cookie header; for
+    cache_limiter the value was silently truncated at the null byte.
   . A ValueError is not thrown if $name is a string containing null bytes in
     session_module_name().
   . session_encode() now returns an empty string instead of false for empty
diff --git a/ext/session/session.c b/ext/session/session.c
index 3985925ca2b..aa6cfb311af 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -734,6 +734,13 @@ static PHP_INI_MH(OnUpdateSessionStr)
 	SESSION_CHECK_ACTIVE_STATE;
 	SESSION_CHECK_OUTPUT_STATE;

+	if (new_value && zend_str_has_nul_byte(new_value)) {
+		if (stage != ZEND_INI_STAGE_DEACTIVATE) {
+			php_error_docref(NULL, E_WARNING, "\"%s\" must not contain null bytes", ZSTR_VAL(entry->name));
+		}
+		return FAILURE;
+	}
+
 	return OnUpdateStr(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
 }

diff --git a/ext/session/tests/session_str_settings_null_byte.phpt b/ext/session/tests/session_str_settings_null_byte.phpt
new file mode 100644
index 00000000000..693ec697160
--- /dev/null
+++ b/ext/session/tests/session_str_settings_null_byte.phpt
@@ -0,0 +1,37 @@
+--TEST--
+session.cookie_path, session.cookie_domain, and session.cache_limiter must not contain null bytes
+--EXTENSIONS--
+session
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+var_dump(ini_set('session.cookie_path', "/path\0evil"));
+var_dump(ini_set('session.cookie_domain', "example\0evil.com"));
+var_dump(ini_set('session.cache_limiter', "nocache\0evil"));
+
+var_dump(session_set_cookie_params(0, "/path\0evil"));
+var_dump(session_set_cookie_params(0, null, "example\0evil.com"));
+
+ob_end_flush();
+echo "Done";
+?>
+--EXPECTF--
+Warning: ini_set(): "session.cookie_path" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: ini_set(): "session.cookie_domain" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: ini_set(): "session.cache_limiter" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: session_set_cookie_params(): "session.cookie_path" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: session_set_cookie_params(): "session.cookie_domain" must not contain null bytes in %s on line %d
+bool(false)
+Done