diff --git a/NEWS b/NEWS index 994cedc02ad..1911b8bdf5a 100644 --- a/NEWS +++ b/NEWS @@ -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) diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index 4709c0e2be4..869843589d4 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -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; diff --git a/ext/json/tests/gh15168.phpt b/ext/json/tests/gh15168.phpt new file mode 100644 index 00000000000..bb17c0f7166 --- /dev/null +++ b/ext/json/tests/gh15168.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-15168 (stack overflow in json_encode()) +--SKIPIF-- + +--INI-- +zend.max_allowed_stack_size=512K +--FILE-- +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"