Commit 1ef1dd53bb1 for php
commit 1ef1dd53bb11dbdc5a65f79d3dd1144cc38c4008
Author: cuishuang <imcusg@gmail.com>
Date: Fri Oct 30 22:23:55 2026 +0100
Fix json_encode() depth range check before narrowing
json_encode() accepts $depth as a zend_long but stores it in the
encoder's int max_depth field. On 64-bit builds, values outside the int
range were silently narrowed, so a too large depth could wrap to a small
one and a too small depth could wrap to INT_MAX.
Check the value with ZEND_LONG_EXCEEDS_INT() and throw a ValueError when
it cannot be represented as an int.
Closes GH-23589
diff --git a/NEWS b/NEWS
index 433aa7d0297..63621c3ee7f 100644
--- a/NEWS
+++ b/NEWS
@@ -110,6 +110,8 @@ PHP NEWS
. Fixed json_encode() leaving the recursion guard set on objects with hooked
properties after running out of memory while escaping a property name.
(adapik)
+ . Fixed json_encode() silently narrowing $depth values outside the int range,
+ which wrapped to an unrelated depth. (cuishuang)
- LDAP:
. Fix crash in ldap_exop_sync() when $response_data is omitted. (lacatoire)
diff --git a/ext/json/json.c b/ext/json/json.c
index 9f91d39594e..0b42187491f 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -232,6 +232,11 @@ PHP_FUNCTION(json_encode)
Z_PARAM_LONG(depth)
ZEND_PARSE_PARAMETERS_END();
+ if (ZEND_LONG_EXCEEDS_INT(depth)) {
+ zend_argument_value_error(3, "must be between %d and %d", INT_MIN, INT_MAX);
+ RETURN_THROWS();
+ }
+
php_json_encode_init(&encoder);
encoder.max_depth = (int)depth;
php_json_encode_zval(&buf, parameter, (int)options, &encoder);
diff --git a/ext/json/tests/json_encode_depth.phpt b/ext/json/tests/json_encode_depth.phpt
new file mode 100644
index 00000000000..075228da472
--- /dev/null
+++ b/ext/json/tests/json_encode_depth.phpt
@@ -0,0 +1,19 @@
+--TEST--
+json_encode() rejects depth values outside the encoder range
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?>
+--FILE--
+<?php
+
+foreach ([2147483648, -2147483649] as $depth) {
+ try {
+ json_encode([], depth: $depth);
+ } catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+ }
+}
+
+?>
+--EXPECT--
+ValueError: json_encode(): Argument #3 ($depth) must be between -2147483648 and 2147483647
+ValueError: json_encode(): Argument #3 ($depth) must be between -2147483648 and 2147483647