diff --git a/NEWS b/NEWS index 40393c15fb76..6c615b944baf 100644 --- a/NEWS +++ b/NEWS @@ -77,6 +77,10 @@ PHP NEWS . Fixed grapheme_strrpos() and grapheme_strripos() skipping overlapping matches when using a negative offset. (Weilin Du) +- JSON: + . Fixed json_encode() continuing to encode after an array or object key + failed to encode. (Ilia Alshanetsky) + - Lexbor: . Merge patches lexbor/lexbor@8a14bc0 and lexbor/lexbor@f67ce4b, fixing a heap buffer overflow in :lexbor-contains() parsing and buffer overflows diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index cefee25de90a..e763a7d2052e 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -186,11 +186,15 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options, php_json_pretty_print_indent(buf, options, encoder); if (php_json_escape_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name), - options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE && - (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && - buf->s) { - ZSTR_LEN(buf->s) -= 4; - smart_str_appendl(buf, "\"\"", 2); + options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE) { + if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { + PHP_JSON_HASH_UNPROTECT_RECURSION(obj); + return FAILURE; + } + if (buf->s) { + ZSTR_LEN(buf->s) -= 4; + smart_str_appendl(buf, "\"\"", 2); + } } smart_str_appendc(buf, ':'); @@ -302,11 +306,17 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options, php_json_pretty_print_indent(buf, options, encoder); if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key), - options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE && - (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && - buf->s) { - ZSTR_LEN(buf->s) -= 4; - smart_str_appendl(buf, "\"\"", 2); + options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE) { + if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { + PHP_JSON_HASH_UNPROTECT_RECURSION(recursion_rc); + zend_release_properties(prop_ht); + zval_ptr_dtor(&tmp); + return FAILURE; + } + if (buf->s) { + ZSTR_LEN(buf->s) -= 4; + smart_str_appendl(buf, "\"\"", 2); + } } } else { if (need_comma) { diff --git a/ext/json/tests/json_encode_invalid_key_aborts.phpt b/ext/json/tests/json_encode_invalid_key_aborts.phpt new file mode 100644 index 000000000000..76afc5be43f3 --- /dev/null +++ b/ext/json/tests/json_encode_invalid_key_aborts.phpt @@ -0,0 +1,55 @@ +--TEST-- +json_encode() stops encoding after an object or array key fails to encode +--FILE-- +{"\xff"} = 1; +$dynamic->next = new Value; + +$declared = new Declared; +$declared->next = new Value; + +$cases = [ + 'array' => ["\xff" => 1, 'next' => new Value], + 'array, later NAN' => ["\xff" => 1, 'next' => NAN], + 'dynamic property' => $dynamic, + 'declared property' => $declared, +]; + +foreach ($cases as $name => $value) { + echo "$name:\n"; + var_dump(json_encode($value), json_last_error_msg()); +} + +echo "partial output:\n"; +var_dump(json_encode(["\xff" => 1, 'next' => 2], JSON_PARTIAL_OUTPUT_ON_ERROR), json_last_error_msg()); + +?> +--EXPECT-- +array: +bool(false) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +array, later NAN: +bool(false) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +dynamic property: +bool(false) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +declared property: +bool(false) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" +partial output: +string(15) "{"":1,"next":2}" +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"