Commit 6393bde658d for php

commit 6393bde658d1041ed0b7e9534d0a857fe773669e
Author: Jorg Sowa <jorg.sowa@gmail.com>
Date:   Sun Nov 15 00:03:09 2026 +0100

    ext/session: prevent creation of fixed sid in php_session_rfc1867 (GH-22113)

    Closes GH-22113

diff --git a/NEWS b/NEWS
index a5fa9c4ead9..64c61f8f121 100644
--- a/NEWS
+++ b/NEWS
@@ -104,6 +104,10 @@ PHP                                                                        NEWS
   . Fix trampoline leak when object construction fails for lazy object.
     (ndossche)

+- Session:
+  . Fixed session upload progress to respect session.use_only_cookies.
+    (jorgsowa)
+
 - Standard:
   . Fixed dns_get_record() returning partially built records for malformed
     DNS responses. (Marti2203)
diff --git a/ext/session/session.c b/ext/session/session.c
index 63072b331dd..fc4f201a63d 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -1708,8 +1708,8 @@ PHPAPI zend_result php_session_start(void) /* {{{ */
 	 * Cookies are preferred, because initially cookie and get
 	 * variables will be available.
 	 * URL/POST session ID may be used when use_only_cookies=Off.
-	 * session.use_strice_mode=On prevents session adoption.
-	 * Session based file upload progress uses non-cookie ID.
+	 * session.use_strict_mode=On prevents session adoption.
+	 * Session based file upload progress respects use_only_cookies.
 	 */

 	if (!PS(id)) {
@@ -3316,7 +3316,7 @@ static zend_result php_session_rfc1867_callback(unsigned int event, void *event_
 			if (data->name && data->value && value_len) {
 				size_t name_len = strlen(data->name);

-				if (name_len == progress->sname_len && memcmp(data->name, PS(session_name), name_len) == 0) {
+				if (!PS(use_only_cookies) && name_len == progress->sname_len && memcmp(data->name, PS(session_name), name_len) == 0) {
 					zval_ptr_dtor(&progress->sid);
 					ZVAL_STRINGL(&progress->sid, (*data->value), value_len);
 				} else if (name_len == strlen(PS(rfc1867_name)) && memcmp(data->name, PS(rfc1867_name), name_len + 1) == 0) {
diff --git a/ext/session/tests/rfc1867_sid_post_use_only_cookies.phpt b/ext/session/tests/rfc1867_sid_post_use_only_cookies.phpt
new file mode 100644
index 00000000000..8a22b261369
--- /dev/null
+++ b/ext/session/tests/rfc1867_sid_post_use_only_cookies.phpt
@@ -0,0 +1,34 @@
+--TEST--
+session rfc1867 upload progress ignores session ID from POST data with default session.use_only_cookies
+--INI--
+upload_max_filesize=1024
+session.upload_progress.cleanup=0
+session.upload_progress.freq=0
+--EXTENSIONS--
+session
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--POST_RAW--
+Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737
+-----------------------------20896060251896012921717172737
+Content-Disposition: form-data; name="PHPSESSID"
+
+rfc1867-sid-post-use-only-cookies
+-----------------------------20896060251896012921717172737
+Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
+
+rfc1867_sid_post_use_only_cookies.php
+-----------------------------20896060251896012921717172737
+Content-Disposition: form-data; name="file1"; filename="file1.txt"
+
+1
+-----------------------------20896060251896012921717172737--
+--FILE--
+<?php
+session_id("rfc1867-sid-post-use-only-cookies");
+session_start();
+var_dump(isset($_SESSION["upload_progress_" . basename(__FILE__)]));
+session_destroy();
+?>
+--EXPECT--
+bool(false)
diff --git a/tests/basic/oom_rfc1867_upload_progress.phpt b/tests/basic/oom_rfc1867_upload_progress.phpt
index 77512e33719..5a8fd614c02 100644
--- a/tests/basic/oom_rfc1867_upload_progress.phpt
+++ b/tests/basic/oom_rfc1867_upload_progress.phpt
@@ -22,12 +22,12 @@
     'session.upload_progress.min_freq=0',
 ];

-/* The progress handler only engages once it has seen both the session id and
- * the progress key, so those two parts come first. */
+/* php-cgi inherits $_ENV; the progress handler takes the session id from the cookie. */
+$_ENV['HTTP_COOKIE'] = 'PHPSESSID=abcdef0123456789abcdef0123456789';
+
+/* The progress handler only engages once it has seen the progress key, so that part comes first. */
 function oom_rfc1867_progress_preamble($fh): void
 {
-    oom_rfc1867_part($fh, 'Content-Disposition: form-data; name="PHPSESSID"',
-        'abcdef0123456789abcdef0123456789');
     oom_rfc1867_part($fh, 'Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"', 'k');
 }

@@ -73,8 +73,6 @@ function oom_rfc1867_progress_preamble($fh): void
  * progress key on MULTIPART_EVENT_FORMDATA, so the soft limit is reached
  * inside the handler rather than in the parser. */
 oom_rfc1867_request(function ($fh) {
-    oom_rfc1867_part($fh, 'Content-Disposition: form-data; name="PHPSESSID"',
-        'abcdef0123456789abcdef0123456789');
     fwrite($fh, '--' . OOM_RFC1867_BOUNDARY . "\r\n"
         . "Content-Disposition: form-data; name=\"PHP_SESSION_UPLOAD_PROGRESS\"\r\n\r\n");
     oom_rfc1867_fill($fh, 1024 * 1024);