Repository navigation
Conversation
`top-level-integer-map.schema` round-trips a map of integers, and `top-level-null-array.schema` round-trips `[null, null]`. Neither has an expected failure: several languages model `null` as an untyped value, and several read a top-level map without checking its values' types. PHP skips the array of null with the other top-level array, and Objective-C skips it because the generated item check (`id.class`) does not compile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A top-level map of integers, booleans, doubles or enums stored the value itself as the pointer (which does not compile) and did not check the values' types. Allocate each value as the top-level list does, check the types as the list does, and fail the parse when an element of a top-level list or map cannot be allocated. - Top-level lists of `null` marked elements with `0xDEADBEAF` (elsewhere `0xDEADBEEF`), and the delete functions of top-level lists and maps of `null` freed the marker. Use `0xDEADBEEF`, and delete each element as a class's list or map does. - Enum getters passed `cJSON_GetStringValue(j)`, `NULL` for a value that is not a string, to `strcmp`. Check `cJSON_IsString(j)` instead of `NULL != j`; such a value reads as 0, as an unknown string does. - Pattern checks ignored what `regcomp` returns, so `regexec` and `regfree` ran on a regex that did not compile. Check only patterns that compile; a JSON Schema pattern that is not a POSIX extended regular expression is not checked. - Maps of classes and unions parsed every value twice, deleting the validated value. Store it instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Five fixes in
CJSONRenderer.ts, one for each problem in the issue:top-level getter now adds list and map elements the same way: it
allocates each value, as the list already did
(
int64_t * item = cJSON_malloc(sizeof(*item)); ... hashtable_add(x->value, e->string, item, sizeof(*item));),and checks a map's values' types as it checks a list's. An element that
cannot be allocated now fails the parse in lists and maps instead of
being left out.
null. The marker is now0xDEADBEEF(it was
0xDEADBEAF). The top-level delete function deletes eachelement the way a class's list or map does: nothing for
null,cJSON_Invalid, lists and maps, and a check for the marker around anullable element.
if (cJSON_IsString(j))instead ofif (NULL != j), sostrcmpnever getsNULL. A value that is not astring reads as
0, as a string that is not a case already does.regexecandregfreenow run only ifregcompsucceeded:
regex_t regex; if (0 == regcomp(®ex, "…", REG_EXTENDED)) { if (regexec(…)) { regfree(®ex); …; return NULL; } regfree(®ex); }.of being deleted and parsed again.
A test commit adds two inputs:
test/inputs/schema/top-level-integer-map.schemawith
{ "a": 1, "b": -2 }, andtest/inputs/schema/top-level-null-array.schemawith
[null, null]. They run for every language. Objective-C skips thearray of null, because its generated item check
(
[item isKindOfClass:id.class]) does not compile. That is a separateObjective-C bug. PHP skips it with the other top-level array. Neither input
has an expected-failure sample. Java and Objective-C both accept
{"a": 1, "b": "two"}for a top-level map of integers, so such a samplewould need skips in other languages too. The cJSON map's type check was
checked by hand instead (see below).
Related Issue
Fixes #3552
Motivation and Context
Before this change, a top-level map of integers did not compile, and a
top-level list or map of
nullcrashed when it was deleted. A number inan array of enums crashed in
strcmp. A pattern thatregcompcannotcompile led to
regexecon an uninitializedregex_t. Maps of classesdid twice the work at every level of nesting.
Known limitations that remain:
(
\d, lookahead) are not POSIX extended ones. Such a pattern is now notchecked at all, where before it gave undefined behavior (on macOS,
every value was rejected).
reads as
0, which lists, maps and unions store as if it were a case.Rejecting such values probably means changing the getter (an
out-parameter or a sentinel) and is not part of this change.
Previous Behaviour / Output
(
[null, null], incJSON_freefromcJSON_DeleteTopLevel)(
{"tags": [1]}for an array of enums)New Behaviour / Output
All five inputs from the issue compile, run without AddressSanitizer or
UndefinedBehaviorSanitizer reports and print their input back.
{"tags": [1]}prints{"tags": []}, and{"a": "x"}for a top-levelmap of integers is rejected (exit status 1).
The generated C changes in these places: the enum getters'
ifline,every pattern check, maps of classes and unions, and the getters and
delete functions of top-level lists and maps. No other language's output
changes.
How Has This Been Tested?
Environment: macOS 26.6 (arm64), Node 24.16.0, Apple clang 21. The cjson
fixtures run their program under valgrind, which is not available on
macOS. A wrapper ran the program directly, so the fixtures' leak check
did not run here. The other languages ran with Python 3.12.16 and
mypy 2.4.1, OpenJDK 21.0.11 and Maven 3.10.0
(
MAVEN_ARGS="-Dmaven.compiler.source=8 -Dmaven.compiler.target=8"),Ruby 4.0.6, Swift, Rust (cargo) and Dart.
On this branch:
npm ci,npm run build,npm run lint(passes),npm run test:unit(54 files, 241 tests pass).
QUICKTEST=true FIXTURE=cjson,schema-cjson npm run test:fixtures: all138 runs pass.
QUICKTEST=true FIXTURE=cjson-default,cjson-multi-header,cjson-multi-split npm run test:fixtures:all 3 pass.
schema-typescript,schema-javascript,schema-typescript-zod,schema-typescript-effect-schema,schema-flow,schema-javascript-prop-types,schema-python,schema-ruby,schema-java,schema-java-datetime-legacy,schema-swift,schema-rust,schema-dart,schema-cplusplusandschema-objective-c: all pass (29 runs; Objective-C skips the array ofnull).
test/fixtures/cjson/main.candcc -g -O0 -fsanitize=address,undefined, before (master) and after:the five inputs from the issue, top-level maps of doubles, booleans and
enums, and
{"a": "x"}for the map of integers. Before, the maps do notcompile, the lists and maps of
nulland the enum array crash, and thepattern rejects a valid value. After, all run without sanitizer reports,
and the mistyped map value is rejected.
With only the test commit (the fix not applied):
QUICKTEST=true FIXTURE=schema-cjson npm run test:fixtures -- test/inputs/schema/top-level-integer-map.schemafails to compile (
error: passing 'double' to parameter of incompatible type 'void *').top-level-null-array.schema, the program aborts (exit status 134).Not run here, because the toolchains are not installed: C#, Go, Kotlin,
Scala, Elm, PHP, Elixir, Haskell, Crystal and Pike. The new inputs run in
their CI fixtures too.
Screenshots (if appropriate):
n/a
Written by Claude (an AI assistant made by Anthropic) and posted on behalf of the account owner (EloB). The reproduction, fix and tests were checked by running them as described above.
🤖 Generated with Claude Code