Skip to content

Commit 1279bc6

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix GH-20584: Information Leak of Memory Fix GH-20583: Stack overflow in http_build_query via deep structures
2 parents d13b5eb + 159a75c commit 1279bc6

File tree

5 files changed

+85
-0
lines changed

5 files changed

+85
-0
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ PHP NEWS
5656

5757
- Standard:
5858
. Fix memory leak in array_diff() with custom type checks. (ndossche)
59+
. Fixed bug GH-20583 (Stack overflow in http_build_query
60+
via deep structures). (ndossche)
61+
. Fixed bug GH-20584 (Information Leak of Memory). (ndossche)
5962

6063
- XML:
6164
. Fixed bug GH-20439 (xml_set_default_handler() does not properly handle

ext/standard/http.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
8383
}
8484
}
8585

86+
static zend_always_inline bool php_url_check_stack_limit(void)
87+
{
88+
#ifdef ZEND_CHECK_STACK_LIMIT
89+
return zend_call_stack_overflowed(EG(stack_limit));
90+
#else
91+
return false;
92+
#endif
93+
}
94+
8695
/* {{{ php_url_encode_hash */
8796
PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
8897
const char *num_prefix, size_t num_prefix_len,
@@ -101,6 +110,12 @@ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
101110
return;
102111
}
103112

113+
/* Very deeply structured data could trigger a stack overflow, even without recursion. */
114+
if (UNEXPECTED(php_url_check_stack_limit())) {
115+
zend_throw_error(NULL, "Maximum call stack size reached.");
116+
return;
117+
}
118+
104119
if (!arg_sep) {
105120
arg_sep = PG(arg_separator).output;
106121
if (ZSTR_LEN(arg_sep) == 0) {

ext/standard/image.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_
406406
if (read_now < stream->chunk_size && read_total != length) {
407407
return 0;
408408
}
409+
buffer += read_now;
409410
} while (read_total < length);
410411

411412
return read_total;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-20583 (Stack overflow in http_build_query via deep structures)
3+
--SKIPIF--
4+
<?php
5+
if (ini_get('zend.max_allowed_stack_size') === false) {
6+
die('skip No stack limit support');
7+
}
8+
if (getenv('SKIP_ASAN')) {
9+
die('skip ASAN needs different stack limit setting due to more stack space usage');
10+
}
11+
?>
12+
--INI--
13+
zend.max_allowed_stack_size=512K
14+
--FILE--
15+
<?php
16+
$a = null;
17+
for ($i = 0; $i < 5000; $i++) {
18+
$a = [$i => $a];
19+
}
20+
try {
21+
http_build_query($a, 'p');
22+
} catch (Throwable $e) {
23+
echo $e::class, ": ", $e->getMessage(), "\n";
24+
}
25+
?>
26+
--EXPECT--
27+
Error: Maximum call stack size reached.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
GH-20584 (Information Leak of Memory)
3+
--CREDITS--
4+
Nikita Sveshnikov (Positive Technologies)
5+
--FILE--
6+
<?php
7+
// Minimal PoC: corruption/uninitialized memory leak when reading APP1 via php://filter
8+
$file = __DIR__ . '/gh20584.jpg';
9+
10+
// Make APP1 large enough so it is read in multiple chunks
11+
$chunk = 8192;
12+
$tail = 123;
13+
$payload = str_repeat('A', $chunk) . str_repeat('B', $chunk) . str_repeat('Z',
14+
$tail);
15+
$app1Len = 2 + strlen($payload);
16+
17+
// Minimal JPEG: SOI + APP1 + SOF0(1x1) + EOI
18+
$sof = "\xFF\xC0" . pack('n', 11) . "\x08" . pack('n',1) . pack('n',1) .
19+
"\x01\x11\x00";
20+
$jpeg = "\xFF\xD8" . "\xFF\xE1" . pack('n', $app1Len) . $payload . $sof .
21+
"\xFF\xD9";
22+
file_put_contents($file, $jpeg);
23+
24+
// Read through a filter to enforce multiple reads
25+
$src = 'php://filter/read=string.rot13|string.rot13/resource=' . $file;
26+
$info = null;
27+
@getimagesize($src, $info);
28+
$exp = $payload;
29+
$ret = $info['APP1'];
30+
31+
var_dump($ret === $exp);
32+
33+
?>
34+
--CLEAN--
35+
<?php
36+
@unlink(__DIR__ . '/gh20584.jpg');
37+
?>
38+
--EXPECT--
39+
bool(true)

0 commit comments

Comments
 (0)