Skip to content

Fix GH-24115: CLI server chunked trailers bypass header size limit - #25049

Open
glbessa wants to merge 1 commit into
php:PHP-8.4from
glbessa:gh24115-cli-server-trailer-limit
Open

glbessa wants to merge 1 commit into
php:PHP-8.4from
glbessa:gh24115-cli-server-trailer-limit

Conversation

@glbessa

@glbessa glbessa commented Oct 30, 2026

Copy link
Copy Markdown

Fixes GH-24115.

PARSING_HEADER() in sapi/cli/php_http_parser.c excluded the F_TRAILING state, so the bytes of chunked request trailers were never counted in nread, and PHP_HTTP_MAX_HEADER_SIZE did not apply to them. A client could send an unbounded trailer section after the 0\r\n chunk.

The fix drops the F_TRAILING exception, so trailers are counted like regular headers. This is the same change upstream nodejs/http-parser made in 81ca70aec ("Avoid chunk trailer overflow").

Notes:

  • nread is reset at the end of the regular headers, so trailers get their own 80 KB budget. Chunk sizes and chunk data are not counted.
  • At the end of the trailers, nread is not reset before NEW_MESSAGE(). This does not matter today because the CLI server always replies with Connection: close. I can add a reset there if you prefer.

Test: sapi/cli/tests/gh24115.phpt sends a small trailer (200), a 100 KB trailer (rejected), and then checks that the server still answers. The test fails without the fix. I also ran a short randomized check with chunked bodies up to 200 KB and trailers up to 160 KB, and every response matched the limit.

🤖 Generated with Claude Code

PARSING_HEADER() excluded the F_TRAILING state, so bytes of chunked request trailers were never counted against PHP_HTTP_MAX_HEADER_SIZE. Count them like regular headers, as done upstream in nodejs/http-parser 81ca70aec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@@ -0,0 +1,38 @@
--TEST--

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--- a/sapi/cli/tests/gh24115.phpt
+++ b/sapi/cli/tests/gh24115.phpt
@@ -14,7 +14,7 @@ $head = "POST / HTTP/1.1\r\nHost: $host\r\nTransfer-Encoding: chunked\r\nConnect

 function send(string $request): string {
     $fp = php_cli_server_connect();
-    fwrite($fp, $request);
+    @fwrite($fp, $request);
     $response = stream_get_contents($fp);
     fclose($fp);
     return $response;
@@ -26,7 +26,7 @@ echo "small trailer: ", str_contains($response, "200 OK") ? "200" : "FAIL", "\n"

 // A trailer larger than the header size limit is rejected.
 $response = send($head . "0\r\nX-Fill: " . str_repeat("A", 100 * 1024) . "\r\n\r\n");
-echo "oversized trailer: ", str_contains($response, "200 OK") ? "FAIL" : "rejected", "\n";
+echo "oversized trailer: ", $response === '' ? "rejected" : "FAIL", "\n";

 // The server keeps serving requests afterwards.
 $response = send($head . "0\r\n\r\n");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants