Skip to content

Check json_validate() in the JSON fuzzer - #24723

Open
yumasansansan wants to merge 2 commits into
php:masterfrom
yumasansansan:fuzzer-json-depth-validate
Open

yumasansansan wants to merge 2 commits into
php:masterfrom
yumasansansan:fuzzer-json-depth-validate

Conversation

@yumasansansan

@yumasansansan yumasansansan commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

json_validate() (php_json_validate_ex()) runs the same scanner and grammar as json_decode() with a separate set of parser methods that build no values, and was not covered by any fuzzer. For JSON_OBJECT_AS_ARRAY (with and without JSON_INVALID_UTF8_IGNORE), decoding fails exactly when validation fails, apart from running out of memory, which cannot happen in this fuzzer. Decoding to objects is excluded, as it additionally rejects property names starting with a NUL byte. The fuzzer now asserts that both agree.

The fuzzer keeps its depth of 10, which is lower than the default of 512, so that deeply nested inputs fail quickly and the depth limit is reached with small inputs. This is now documented in a comment.

Replaying the checked-in sapi/fuzzer/corpus/json (no fuzzing): edge coverage 1211 → 1252, and the assertion holds for every seed. The input format is unchanged.

The fuzzer used a depth of 10 rather than json_decode()'s default of
512, so every input nesting deeper than that failed early.
json_validate() uses its own parser methods and was not fuzzed at all.
Check that it agrees with decoding to arrays, which, unlike decoding to
objects, does not reject property names starting with a NUL byte.
@ndossche

Copy link
Copy Markdown
Member

Some possibilities for the lower limit:

  • Gain more exec/s by failing more quickly with deeply nested data
  • Test depth limit edge case handling quicker

@yumasansansan

Copy link
Copy Markdown
Contributor Author

Good points, I measured it. Time per input for the same input run 3000 times (ASan build):

Input depth 10 depth 512
flat JSON 132 µs 131 µs
array nested 500 levels 76 µs 4955 µs
4000 times [ 80 µs 704 µs

On the checked-in corpus, the higher depth only adds 5 edges (1252 → 1257), while json_validate() adds 41 (1211 → 1252). So I went back to a depth of 10, now with a comment explaining why, and json_validate() uses the same depth. I updated the title and the description.

@yumasansansan yumasansansan changed the title Use the default depth in the JSON fuzzer and check json_validate() Check json_validate() in the JSON fuzzer Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants