Skip to content

Fix: bound recursion depth in cJSON_Compare - #1135

Open
xmgzxmgz wants to merge 1 commit into
DaveGamble:masterfrom
xmgzxmgz:fix/compare-depth
Open

xmgzxmgz wants to merge 1 commit into
DaveGamble:masterfrom
xmgzxmgz:fix/compare-depth

Conversation

@xmgzxmgz

Copy link
Copy Markdown

Summary

cJSON_Compare walks nested arrays and objects recursively without any depth
counter. Every other recursive tree walker in the library is bounded, so a
caller has no way to know that comparing two very deep trees can exhaust the
stack. On a 200k-deep tree it crashes.

This is a robustness / consistency fix, not a security report. A caller cannot
reach that depth from JSON text: cJSON_Parse is already capped by
CJSON_NESTING_LIMIT and rejects deeper input (verified below). The tree has to
be built programmatically by the caller, or assembled by repeated JSON Patch
operations on an existing tree.

Related but distinct from #1093 (parser under ASan/-O0) and #995 (merge patch
recursion): this is about cJSON_Compare only.

Repro

Harness: build two identical N-level nested arrays programmatically, then call
cJSON_Compare(a, b, 1). Built with cc -O2 (no sanitizers), 8 MiB stack.

depth 6d9f244 with this patch
1,000 1 1
100,000 1 0
200,000 SIGSEGV (139) 0, exit 0

Nested objects ({"a":{"a":...}}) behave the same: depth 200,000 returns 0 and
exits cleanly after the patch.

Why the existing limit does not apply

Recursion is bounded everywhere else in the library:

cJSON_Compare (cJSON.c:3072) only checks NULL, type equality and the a == b
shortcut, then self-recurses unconditionally at cJSON.c:3137 (array elements)
and cJSON.c:3167 / 3183 (object members, both directions).

compare_json in cJSON_Utils.c:604 has the same shape. I left it alone on
purpose — that file already has an open recursion report (#995) and I would
rather not mix the two discussions.

Patch

  • cJSON_Compare keeps its exact signature and becomes a thin wrapper over a
    new static compare_rec(a, b, case_sensitive, depth).
  • The array branch and both object branches refuse to recurse past
    CJSON_NESTING_LIMIT and return false (trees that deep are reported
    unequal rather than crashing).
  • No header change, no ABI change, no extra allocation, no new public symbol.

The guard is depth > CJSON_NESTING_LIMIT with the root at depth 0, so nothing
that cJSON_Parse can produce is affected. Measured on the same commit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant