Skip to content

Commit a8a3635

Browse files
committed
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5: Fix json_encode() reporting false "Recursion detected" after OOM for hooked-property objects (#24992)
2 parents e5e5a8a + b7b9c8b commit a8a3635

3 files changed

Lines changed: 35 additions & 1 deletion

File tree

‎NEWS‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ PHP NEWS
5151
. Fixed IntlDateFormatter parse functions truncating an $offset below
5252
INT_MIN. (Marc Bennewitz)
5353

54+
- JSON:
55+
. Fixed json_encode() leaving the recursion guard set on objects with hooked
56+
properties after running out of memory while escaping a property name.
57+
(adapik)
58+
5459
- Opcache:
5560
. Fixed tracing JIT returning a wrapped integer instead of a float on
5661
integer overflow after an integer was compared with a float or

‎ext/json/json_encoder.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,9 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
286286
if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
287287
options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE) {
288288
if (ZEND_HAS_OOM()) {
289-
PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
289+
PHP_JSON_HASH_UNPROTECT_RECURSION(recursion_rc);
290290
zend_release_properties(prop_ht);
291+
zval_ptr_dtor(&tmp);
291292
return FAILURE;
292293
}
293294
if ((options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && buf->s) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
json_encode() OOM while escaping a property name releases the recursion guard of an object with hooked properties
3+
--FILE--
4+
<?php
5+
6+
#[AllowDynamicProperties]
7+
class C {
8+
public string $a { get => str_repeat('v', 100); }
9+
}
10+
11+
$o = new C;
12+
$o->{str_repeat('k', 3 * 1024 * 1024)} = 1;
13+
14+
register_shutdown_function(function () use ($o) {
15+
ini_set('memory_limit', '-1');
16+
$json = json_encode($o);
17+
var_dump($json !== false, json_last_error_msg());
18+
});
19+
20+
// Leave less than the 3 MiB property name needs, so that pre-allocating it
21+
// fails while escaping the key, after the object was marked as being encoded.
22+
ini_set('memory_limit', memory_get_usage(true) + 1024 * 1024);
23+
json_encode($o);
24+
?>
25+
--EXPECTF--
26+
Fatal error: Allowed memory size of %d bytes exhausted %S(tried to allocate %d bytes) in %s on line %d
27+
%Abool(true)
28+
string(8) "No error"

0 commit comments

Comments
 (0)