Skip to content

Commit 817f6d2

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: ext/standard: reject a dechunk chunk size that overflows size_t
2 parents 212db3c + 04f3d28 commit 817f6d2

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

ext/standard/filters.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,19 +1709,26 @@ static size_t php_dechunk(char *buf, size_t len, php_chunked_filter_data *data)
17091709
data->chunk_size = 0;
17101710
case CHUNK_SIZE:
17111711
while (p < end) {
1712+
size_t digit;
1713+
17121714
if (*p >= '0' && *p <= '9') {
1713-
data->chunk_size = (data->chunk_size * 16) + (*p - '0');
1715+
digit = *p - '0';
17141716
} else if (*p >= 'A' && *p <= 'F') {
1715-
data->chunk_size = (data->chunk_size * 16) + (*p - 'A' + 10);
1717+
digit = *p - 'A' + 10;
17161718
} else if (*p >= 'a' && *p <= 'f') {
1717-
data->chunk_size = (data->chunk_size * 16) + (*p - 'a' + 10);
1719+
digit = *p - 'a' + 10;
17181720
} else if (data->state == CHUNK_SIZE_START) {
17191721
data->state = CHUNK_ERROR;
17201722
break;
17211723
} else {
17221724
data->state = CHUNK_SIZE_EXT;
17231725
break;
17241726
}
1727+
if (data->chunk_size > (SIZE_MAX / 16)) {
1728+
data->state = CHUNK_ERROR;
1729+
break;
1730+
}
1731+
data->chunk_size = (data->chunk_size * 16) + digit;
17251732
data->state = CHUNK_SIZE;
17261733
p++;
17271734
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
dechunk filter must reject a chunk size that overflows size_t
3+
--SKIPIF--
4+
<?php
5+
$filters = stream_get_filters();
6+
if(! in_array( "dechunk", $filters )) die( "skip Chunked filter not available." );
7+
?>
8+
--INI--
9+
allow_url_fopen=1
10+
--FILE--
11+
<?php
12+
/* Both sizes exceed SIZE_MAX, so parsing stops and the rest is passed through
13+
raw. The guard trips at SIZE_MAX/16, so how many digits are consumed first
14+
follows the width of size_t: %s covers the leftover run. Unguarded, the
15+
first size wraps to 0, which reads as the terminating chunk and drops the
16+
body; the second wraps to SIZE_MAX and swallows the rest as one chunk. */
17+
$streams = [
18+
"data://text/plain,10000000000000000\nBODYDATA\n0\n",
19+
"data://text/plain,fffffffffffffffff\nBODYDATA\n0\n",
20+
"data://text/plain,5\nhello\n0\n",
21+
];
22+
foreach ($streams as $name) {
23+
$fp = fopen($name, "r");
24+
stream_filter_append($fp, "dechunk", STREAM_FILTER_READ);
25+
var_dump(stream_get_contents($fp));
26+
fclose($fp);
27+
}
28+
?>
29+
--EXPECTF--
30+
string(%d) "%s
31+
BODYDATA
32+
0
33+
"
34+
string(%d) "%s
35+
BODYDATA
36+
0
37+
"
38+
string(5) "hello"

0 commit comments

Comments
 (0)