From 6b070f1f1968f60e52b1fea647c4f1015411d4b4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:58:40 +0100 Subject: [PATCH] Fix GH-20583: Stack overflow in http_build_query via deep structures --- ext/standard/http.c | 15 +++++++++++ .../tests/http/http_build_query/gh20583.phpt | 27 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 ext/standard/tests/http/http_build_query/gh20583.phpt diff --git a/ext/standard/http.c b/ext/standard/http.c index 1145329a794ff..d79c25c38a0ad 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -92,6 +92,15 @@ static void php_url_encode_scalar(zval *scalar, smart_str *form_str, } } +static zend_always_inline bool php_url_check_stack_limit(void) +{ +#ifdef ZEND_CHECK_STACK_LIMIT + return zend_call_stack_overflowed(EG(stack_limit)); +#else + return false; +#endif +} + /* {{{ php_url_encode_hash */ PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, 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, return; } + /* Very deeply structured data could trigger a stack overflow, even without recursion. */ + if (UNEXPECTED(php_url_check_stack_limit())) { + zend_throw_error(NULL, "Maximum call stack size reached."); + return; + } + if (!arg_sep) { arg_sep = zend_ini_str("arg_separator.output", strlen("arg_separator.output"), false); if (ZSTR_LEN(arg_sep) == 0) { diff --git a/ext/standard/tests/http/http_build_query/gh20583.phpt b/ext/standard/tests/http/http_build_query/gh20583.phpt new file mode 100644 index 0000000000000..c0331a830b1e0 --- /dev/null +++ b/ext/standard/tests/http/http_build_query/gh20583.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-20583 (Stack overflow in http_build_query via deep structures) +--SKIPIF-- + +--INI-- +zend.max_allowed_stack_size=512K +--FILE-- + $a]; +} +try { + http_build_query($a, 'p'); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Error: Maximum call stack size reached.