Description
I found a reproducible stack overflow in the JSON schema validator on the current master branch.
A schema containing a cyclic $ref, such as {"$ref":"#"}, causes ucl_schema_validate() to recursively validate the same schema without a recursion limit or reference-cycle detection. This eventually exhausts the stack and crashes.
Version
Tested on current master:
ed8617c565083c81939cfb99f1594af1cb539850
Environment
- OS: Linux x86_64
- Compiler: clang with AddressSanitizer and UndefinedBehaviorSanitizer
- Sanitizers: AddressSanitizer + UndefinedBehaviorSanitizer
Reproduction
Minimal schema:
Any valid document is enough to trigger the recursion, for example:
The attached PoC zip contains:
N1_schema_only.txt: the 12-byte schema {"$ref":"#"}.
N1_schema_selfref_fuzzer_input.bin: the fuzzer input with a 1-byte selector followed by the schema.
verify_schema.c: a standalone reproducer using ucl_object_validate().
Example build and run:
cd libucl
clang -fsanitize=address,undefined -fno-omit-frame-pointer -g -O1 \
-I ./include -I ./src -I ./uthash -I ./klib \
verify_schema.c build-fuzz/libucl.a -lm -lrt -o verify_schema
ASAN_OPTIONS=detect_leaks=0 ./verify_schema 1
The same root cause can also be triggered by an indirect $ref cycle:
{"$ref":"#/definitions/a","definitions":{"a":{"$ref":"#/definitions/a"}}}
The standalone reproducer includes this as:
ASAN_OPTIONS=detect_leaks=0 ./verify_schema 2
ASAN output
ERROR: AddressSanitizer: stack-overflow
#0 __interceptor_strlen
#1 ucl_object_lookup src/ucl_util.c:2677
#2 ucl_schema_validate src/ucl_schema.c:962
#3 ucl_schema_validate src/ucl_schema.c:1040
#4 ucl_schema_validate src/ucl_schema.c:1040
#5 ucl_schema_validate src/ucl_schema.c:1040
...
The repeated frame is:
ucl_schema_validate src/ucl_schema.c:1040
Root cause hypothesis
ucl_schema_validate() resolves $ref and then recursively calls itself:
elt = ucl_object_lookup(schema, "$ref");
if (elt != NULL) {
ref_root = root;
cur = ucl_schema_resolve_ref(root, ucl_object_tostring(elt),
err, external_refs, &ref_root);
if (cur == NULL) {
return false;
}
if (!ucl_schema_validate(cur, obj, try_array, err, ref_root,
external_refs)) {
return false;
}
}
For {"$ref":"#"}, ucl_schema_resolve_ref() returns the root schema itself. The validator then recursively validates the same schema and object again, without consuming document depth and without detecting the $ref cycle.
Notes
This is a possible denial-of-service issue when schemas can be provided by an untrusted or semi-trusted source. A recursion depth limit or $ref cycle detection would prevent this class of crash.
N1_schema_ref_cycle_poc.zip
Description
I found a reproducible stack overflow in the JSON schema validator on the current
masterbranch.A schema containing a cyclic
$ref, such as{"$ref":"#"}, causesucl_schema_validate()to recursively validate the same schema without a recursion limit or reference-cycle detection. This eventually exhausts the stack and crashes.Version
Tested on current
master:Environment
Reproduction
Minimal schema:
{"$ref":"#"}Any valid document is enough to trigger the recursion, for example:
{"a":1}The attached PoC zip contains:
N1_schema_only.txt: the 12-byte schema{"$ref":"#"}.N1_schema_selfref_fuzzer_input.bin: the fuzzer input with a 1-byte selector followed by the schema.verify_schema.c: a standalone reproducer usingucl_object_validate().Example build and run:
cd libucl clang -fsanitize=address,undefined -fno-omit-frame-pointer -g -O1 \ -I ./include -I ./src -I ./uthash -I ./klib \ verify_schema.c build-fuzz/libucl.a -lm -lrt -o verify_schema ASAN_OPTIONS=detect_leaks=0 ./verify_schema 1The same root cause can also be triggered by an indirect
$refcycle:{"$ref":"#/definitions/a","definitions":{"a":{"$ref":"#/definitions/a"}}}The standalone reproducer includes this as:
ASAN output
The repeated frame is:
Root cause hypothesis
ucl_schema_validate()resolves$refand then recursively calls itself:For
{"$ref":"#"},ucl_schema_resolve_ref()returns the root schema itself. The validator then recursively validates the same schema and object again, without consuming document depth and without detecting the$refcycle.Notes
This is a possible denial-of-service issue when schemas can be provided by an untrusted or semi-trusted source. A recursion depth limit or
$refcycle detection would prevent this class of crash.N1_schema_ref_cycle_poc.zip