Skip to content

Commit 292a7f7

Browse files
committed
Fix GH-20583: Stack overflow in http_build_query via deep structures
Closes GH-20590.
1 parent 27f17c3 commit 292a7f7

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ PHP NEWS
5858

5959
- Standard:
6060
. Fix memory leak in array_diff() with custom type checks. (ndossche)
61+
. Fixed bug GH-20583 (Stack overflow in http_build_query
62+
via deep structures). (ndossche)
6163

6264
- Tidy:
6365
. Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche)

ext/standard/http.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
9292
}
9393
}
9494

95+
static zend_always_inline bool php_url_check_stack_limit(void)
96+
{
97+
#ifdef ZEND_CHECK_STACK_LIMIT
98+
return zend_call_stack_overflowed(EG(stack_limit));
99+
#else
100+
return false;
101+
#endif
102+
}
103+
95104
/* {{{ php_url_encode_hash */
96105
PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
97106
const char *num_prefix, size_t num_prefix_len,
@@ -110,6 +119,12 @@ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
110119
return;
111120
}
112121

122+
/* Very deeply structured data could trigger a stack overflow, even without recursion. */
123+
if (UNEXPECTED(php_url_check_stack_limit())) {
124+
zend_throw_error(NULL, "Maximum call stack size reached.");
125+
return;
126+
}
127+
113128
if (!arg_sep) {
114129
arg_sep = zend_ini_str("arg_separator.output", strlen("arg_separator.output"), false);
115130
if (ZSTR_LEN(arg_sep) == 0) {
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.

0 commit comments

Comments
 (0)