Skip to content

Commit 53bc437

Browse files
glbessaclaude
andcommitted
Fix GH-24115: CLI server chunked trailers bypass header size limit
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>
1 parent 083e1a6 commit 53bc437

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

‎sapi/cli/php_http_parser.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ static const uint8_t normal_url_char[256] = {
195195
1, 1, 1, 1, 1, 1, 1, 0 };
196196

197197

198-
#define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING))
198+
#define PARSING_HEADER(state) (state <= s_headers_almost_done)
199199

200200

201201
enum header_states

‎sapi/cli/tests/gh24115.phpt‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
GH-24115 (CLI server: chunked request trailers are not limited by the header size limit)
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
?>
7+
--FILE--
8+
<?php
9+
include "php_cli_server.inc";
10+
php_cli_server_start("echo 'OK';");
11+
12+
$host = PHP_CLI_SERVER_HOSTNAME;
13+
$head = "POST / HTTP/1.1\r\nHost: $host\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n4\r\ndata\r\n";
14+
15+
function send(string $request): string {
16+
$fp = php_cli_server_connect();
17+
fwrite($fp, $request);
18+
$response = stream_get_contents($fp);
19+
fclose($fp);
20+
return $response;
21+
}
22+
23+
// A small trailer is still accepted.
24+
$response = send($head . "0\r\nX-Trailer: small\r\n\r\n");
25+
echo "small trailer: ", str_contains($response, "200 OK") ? "200" : "FAIL", "\n";
26+
27+
// A trailer larger than the header size limit is rejected.
28+
$response = send($head . "0\r\nX-Fill: " . str_repeat("A", 100 * 1024) . "\r\n\r\n");
29+
echo "oversized trailer: ", str_contains($response, "200 OK") ? "FAIL" : "rejected", "\n";
30+
31+
// The server keeps serving requests afterwards.
32+
$response = send($head . "0\r\n\r\n");
33+
echo "after rejection: ", str_contains($response, "200 OK") ? "200" : "FAIL", "\n";
34+
?>
35+
--EXPECT--
36+
small trailer: 200
37+
oversized trailer: rejected
38+
after rejection: 200

0 commit comments

Comments
 (0)