From a24ac172f52e75101913f3946cfa5515f723c99f Mon Sep 17 00:00:00 2001
From: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date: Mon, 9 Sep 2024 15:22:07 +0200
Subject: [PATCH 04/11] Fix GHSA-9pqp-7h25-4f32

multipart/form-data boundaries larger than the read buffer result in erroneous
parsing, which violates data integrity.

Limit boundary size, as allowed by RFC 1521:

    Encapsulation boundaries [...] must be no longer than 70 characters, not
    counting the two leading hyphens.

We correctly parse payloads with boundaries of length up to
FILLUNIT-strlen("\r\n--") bytes, so allow this for BC.

(cherry picked from commit 19b49258d0c5a61398d395d8afde1123e8d161e0)
(cherry picked from commit 2b0daf421c162376892832588eccdfa9a286ed09)
---
 main/rfc1867.c                       |   7 ++
 tests/basic/GHSA-9pqp-7h25-4f32.inc  |   3 +
 tests/basic/GHSA-9pqp-7h25-4f32.phpt | 100 +++++++++++++++++++++++++++
 3 files changed, 110 insertions(+)
 create mode 100644 tests/basic/GHSA-9pqp-7h25-4f32.inc
 create mode 100644 tests/basic/GHSA-9pqp-7h25-4f32.phpt

diff --git a/main/rfc1867.c b/main/rfc1867.c
index 1b212c93325..43ccce120c3 100644
--- a/main/rfc1867.c
+++ b/main/rfc1867.c
@@ -759,6 +759,13 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
 		boundary_len = boundary_end-boundary;
 	}
 
+	/* Boundaries larger than FILLUNIT-strlen("\r\n--") characters lead to
+	 * erroneous parsing */
+	if (boundary_len > FILLUNIT-strlen("\r\n--")) {
+		sapi_module.sapi_error(E_WARNING, "Boundary too large in multipart/form-data POST data");
+		return;
+	}
+
 	/* Initialize the buffer */
 	if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) {
 		sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer");
diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.inc b/tests/basic/GHSA-9pqp-7h25-4f32.inc
new file mode 100644
index 00000000000..adf72a361a2
--- /dev/null
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.inc
@@ -0,0 +1,3 @@
+<?php
+print "Hello world\n";
+var_dump($_POST);
diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
new file mode 100644
index 00000000000..af819163705
--- /dev/null
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
@@ -0,0 +1,100 @@
+--TEST--
+GHSA-9pqp-7h25-4f32
+--SKIPIF--
+<?php
+if (!getenv('TEST_PHP_CGI_EXECUTABLE')) {
+    die("skip php-cgi not available");
+}
+?>
+--FILE--
+<?php
+
+const FILLUNIT = 5 * 1024;
+
+function test($boundaryLen) {
+    printf("Boundary len: %d\n", $boundaryLen);
+
+    $cmd = [
+        getenv('TEST_PHP_CGI_EXECUTABLE'),
+        '-C',
+        '-n',
+        __DIR__ . '/GHSA-9pqp-7h25-4f32.inc',
+    ];
+
+    $boundary = str_repeat('A', $boundaryLen);
+    $body = ""
+        . "--$boundary\r\n"
+        . "Content-Disposition: form-data; name=\"koko\"\r\n"
+        . "\r\n"
+        . "BBB\r\n--" . substr($boundary, 0, -1) . "CCC\r\n"
+        . "--$boundary--\r\n"
+        ;
+
+    $env = array_merge($_ENV, [
+        'REDIRECT_STATUS' => '1',
+        'CONTENT_TYPE' => "multipart/form-data; boundary=$boundary",
+        'CONTENT_LENGTH' => strlen($body),
+        'REQUEST_METHOD' => 'POST',
+        'SCRIPT_FILENAME' => __DIR__ . '/GHSA-9pqp-7h25-4f32.inc',
+    ]);
+
+    $spec = [
+        0 => ['pipe', 'r'],
+        1 => STDOUT,
+        2 => STDOUT,
+    ];
+
+    $pipes = [];
+
+    print "Starting...\n";
+
+    $handle = proc_open($cmd, $spec, $pipes, getcwd(), $env);
+
+    fwrite($pipes[0], $body);
+
+    $status = proc_close($handle);
+
+    print "\n";
+}
+
+for ($offset = -1; $offset <= 1; $offset++) {
+    test(FILLUNIT - strlen("\r\n--") + $offset);
+}
+
+?>
+--EXPECTF--
+Boundary len: 5115
+Starting...
+X-Powered-By: %s
+Content-type: text/html; charset=UTF-8
+
+Hello world
+array(1) {
+  ["koko"]=>
+  string(5124) "BBB
+--AAA%sCCC"
+}
+
+Boundary len: 5116
+Starting...
+X-Powered-By: %s
+Content-type: text/html; charset=UTF-8
+
+Hello world
+array(1) {
+  ["koko"]=>
+  string(5125) "BBB
+--AAA%sCCC"
+}
+
+Boundary len: 5117
+Starting...
+X-Powered-By: %s
+Content-type: text/html; charset=UTF-8
+
+<br />
+<b>Warning</b>:  Boundary too large in multipart/form-data POST data in <b>Unknown</b> on line <b>0</b><br />
+Hello world
+array(0) {
+}
+
-- 
2.46.1

From 2fd1b83817d20523e72bef3ad524cd5797f51acf Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Mon, 23 Sep 2024 18:54:31 +0100
Subject: [PATCH 08/11] Skip GHSA-9pqp-7h25-4f32 test on Windows

(cherry picked from commit c70e25630832fa10d421328eed2b8e1a36af7a64)
(cherry picked from commit c75683864f6e4188439e8ca2adbb05824918be12)
---
 tests/basic/GHSA-9pqp-7h25-4f32.phpt | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
index af819163705..29bcb6557d5 100644
--- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
@@ -5,6 +5,9 @@ GHSA-9pqp-7h25-4f32
 if (!getenv('TEST_PHP_CGI_EXECUTABLE')) {
     die("skip php-cgi not available");
 }
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+    die("skip not for Windows in CI - probably resource issue");
+}
 ?>
 --FILE--
 <?php
-- 
2.46.1

From 29065f33f37f99ba33254cb23c941647bcd7372c Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Thu, 26 Sep 2024 15:49:03 +0200
Subject: [PATCH 11/11] adapt GHSA-9pqp-7h25-4f32 test for 7.x

---
 tests/basic/GHSA-9pqp-7h25-4f32.phpt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
index 29bcb6557d5..a1ead918ff3 100644
--- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
@@ -21,6 +21,7 @@ function test($boundaryLen) {
         getenv('TEST_PHP_CGI_EXECUTABLE'),
         '-C',
         '-n',
+        '-dlog_errors=1',
         __DIR__ . '/GHSA-9pqp-7h25-4f32.inc',
     ];
 
@@ -92,11 +93,10 @@ array(1) {
 
 Boundary len: 5117
 Starting...
+PHP Warning:  Boundary too large in multipart/form-data POST data in Unknown on line 0
 X-Powered-By: %s
 Content-type: text/html; charset=UTF-8
 
-<br />
-<b>Warning</b>:  Boundary too large in multipart/form-data POST data in <b>Unknown</b> on line <b>0</b><br />
 Hello world
 array(0) {
 }
-- 
2.46.1