1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-15168: stack overflow in json_encode()

The JSON encoder is recursive, and it's far from easy to make it
iterative. Add a cheap stack limit check to prevent a segfault.
This uses the PHP_JSON_ERROR_DEPTH error code that already talks about
the stack depth. Previously this was only used for the $depth argument.

Closes GH-16059.
This commit is contained in:
Niels Dossche
2024-09-25 17:57:55 +02:00
parent d828308095
commit a551b99b2c
3 changed files with 55 additions and 0 deletions

3
NEWS
View File

@@ -15,6 +15,9 @@ PHP NEWS
. Fixed bug GH-16039 (Segmentation fault (access null pointer) in
ext/dom/parentnode/tree.c). (nielsdos)
- JSON:
. Fixed bug GH-15168 (stack overflow in json_encode()). (nielsdos)
- LDAP:
. Fixed bug GH-16032 (Various NULL pointer dereferencements in
ldap_modify_batch()). (Girgias)

View File

@@ -31,6 +31,15 @@
static const char digits[] = "0123456789abcdef";
static zend_always_inline bool php_json_check_stack_limit(void)
{
#ifdef ZEND_CHECK_STACK_LIMIT
return zend_call_stack_overflowed(EG(stack_limit));
#else
return false;
#endif
}
static int php_json_determine_array_type(zval *val) /* {{{ */
{
zend_array *myht = Z_ARRVAL_P(val);
@@ -115,6 +124,14 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
int i, r, need_comma = 0;
HashTable *myht, *prop_ht;
if (php_json_check_stack_limit()) {
encoder->error_code = PHP_JSON_ERROR_DEPTH;
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
return FAILURE;
}
if (Z_TYPE_P(val) == IS_ARRAY) {
myht = Z_ARRVAL_P(val);
prop_ht = NULL;

View File

@@ -0,0 +1,35 @@
--TEST--
GH-15168 (stack overflow in json_encode())
--SKIPIF--
<?php
if (ini_get('zend.max_allowed_stack_size') === false) {
die('skip No stack limit support');
}
?>
--INI--
zend.max_allowed_stack_size=512K
--FILE--
<?php
class Node
{
public $next;
}
$firstNode = new Node();
$node = $firstNode;
for ($i = 0; $i < 30000; $i++) {
$newNode = new Node();
$node->next = $newNode;
$node = $newNode;
}
var_dump(json_encode($firstNode, depth: 500000));
var_dump(json_last_error());
var_dump(json_last_error_msg());
?>
--EXPECT--
bool(false)
int(1)
string(28) "Maximum stack depth exceeded"