Commit 292a7f73baf for php.net

commit 292a7f73baf7c2ce3164feda6e6da779b8056d51
Author: Niels Dossche <7771979+ndossche@users.noreply.github.com>
Date:   Tue Nov 25 18:58:40 2025 +0100

    Fix GH-20583: Stack overflow in http_build_query via deep structures

    Closes GH-20590.

diff --git a/NEWS b/NEWS
index b56302cff0f..b87cba17aad 100644
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,8 @@ PHP                                                                        NEWS

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

 - Tidy:
   . Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche)
diff --git a/ext/standard/http.c b/ext/standard/http.c
index 1145329a794..d79c25c38a0 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 00000000000..c0331a830b1
--- /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--
+<?php
+if (ini_get('zend.max_allowed_stack_size') === false) {
+    die('skip No stack limit support');
+}
+if (getenv('SKIP_ASAN')) {
+    die('skip ASAN needs different stack limit setting due to more stack space usage');
+}
+?>
+--INI--
+zend.max_allowed_stack_size=512K
+--FILE--
+<?php
+$a = null;
+for ($i = 0; $i < 5000; $i++) {
+ $a = [$i => $a];
+}
+try {
+    http_build_query($a, 'p');
+} catch (Throwable $e) {
+    echo $e::class, ": ", $e->getMessage(), "\n";
+}
+?>
+--EXPECT--
+Error: Maximum call stack size reached.