Bug #70275 [Asn]: On recursion error, json_encode can eat up all system memory
| From: | bukka@php.net | Date: | Mon, 16 May 2016 19:10:36 +0000 |
| Subject: | Bug #70275 [Asn]: On recursion error, json_encode can eat up all system memory | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-201139@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=70275&edit=1
ID: 70275
Updated by: bukka@php.net
Reported by: royanee at gmail dot com
Summary: On recursion error, json_encode can eat up all
system memory
Status: Assigned
Type: Bug
Package: JSON related
PHP Version: 5.6.12
Assigned To: bukka
Block user comment: N
Private report: N
New Comment:
This is caused by the fact that parsing is not stopped internally when the first error (recursion in
this case) is found. To fix this we will have to add a return check to all calls in encoder and
allow php_json_encode to fill buffer with incomplete json. Ideally the php_json_encode will be
changed to return value indicating that there is an error or all is ok (FAILURE or SUCCESS) instead
of void. This can't go to bugfixing release and will have to wait for the next minor.
Previous Comments:
------------------------------------------------------------------------
[2015-08-14 23:14:23] royanee at gmail dot com
Description:
------------
When json_encode encounters recursion or other errors it will return false (as long as you are not
using the largely undocumented constant JSON_PARTIAL_OUTPUT_ON_ERROR).
Unfortunately, it waits until after it has processed all of the data to return the error. This was a
problem for me when I was attempting to export an application state in JSON. As I was using the CLI,
it ate up all of the system memory and at best it would have returned false if I could have waited
an eternity for it to finish.
I've included a test script with a nice handful of circular object references.
Test script:
---------------
$max_num_objects = 20;
for ($num_objects = 5; $num_objects <= $max_num_objects; $num_objects++) {
echo PHP_EOL . 'Number of Objects: ' . $num_objects . PHP_EOL;
// Create the objects and link them to each other.
$objects = new stdClass;
for ($i = 0; $i < $num_objects; $i++) {
$objects->$i = new stdClass;
}
for ($i = 0; $i < $num_objects; $i++) {
for ($j = 0; $j < $num_objects; $j++) {
if ($i != $j) {
$objects->$i->$j = $objects->$j;
}
}
}
$starttime = microtime(true);
serialize($objects);
echo '$objects serialize: ' . number_format(microtime(true) - $starttime, 3) .
' seconds' . PHP_EOL;
$starttime = microtime(true);
if (json_encode($objects) === FALSE) {
echo 'JSON Error #' . json_last_error() . ': ' .
json_last_error_msg() . PHP_EOL;
}
echo '$objects json_encode: ' . number_format(microtime(true) - $starttime, 3) .
' seconds' . PHP_EOL;
}
Expected result:
----------------
Finishes in less than a second, ideally in a negligible amount of time. Example:
...
Number of Objects: 10
$objects serialize: 0.000 seconds
JSON Error #6: Recursion detected
$objects json_encode: 0.000 seconds
Actual result:
--------------
...
Number of Objects: 10
$objects serialize: 0.000 seconds
JSON Error #6: Recursion detected
$objects json_encode: 7.227 seconds
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=70275&edit=1