ucl_object_validate() crashes if you pass NULL for the err parameter, which the API documents as optional. The internal function ucl_schema_validate() writes to err->code directly in two places (src/ucl_schema.c:994 and :1023) without a NULL guard. The rest of the file uses ucl_schema_create_error() which does check for NULL, so these two spots were just missed. Affects 0.9.4 and earlier. (CWE-476). The two unguarded writes:
Location 1 (ucl_schema.c:994), after anyOf succeeds:
elt = ucl_object_lookup(schema, "anyOf");
if (elt != NULL && elt->type == UCL_ARRAY) {
// ... validation loop ...
if (!ret) {
return false;
}
else {
/* Reset error */
err->code = UCL_SCHEMA_OK; // CRASH if err == NULL
}
}
Location 2 (ucl_schema.c:1023), after not succeeds:
elt = ucl_object_lookup(schema, "not");
if (elt != NULL && elt->type == UCL_OBJECT) {
if (ucl_schema_validate(elt, obj, true, err, root, external_refs)) {
return false;
}
else {
err->code = UCL_SCHEMA_OK; // CRASH if err == NULL
}
}
Reproduction
Found by fuzzing (-fsanitize=fuzzer,address). Minimal reproducer:
#include <ucl.h>
#include <string.h>
int main(void) {
/* Parse a schema with anyOf: first alternative won't match,
* second will, so the anyOf "success" path is taken */
struct ucl_parser *sp = ucl_parser_new(0);
const char *schema_str = "{\"anyOf\": [{\"type\": \"integer\"}, {\"type\": \"string\"}]}";
ucl_parser_add_string(sp, schema_str, strlen(schema_str));
ucl_object_t *schema = ucl_parser_get_object(sp);
ucl_parser_free(sp);
/* Create a string object to validate */
ucl_object_t *obj = ucl_object_fromstring("hello");
/* Validate with NULL err -- crashes at ucl_schema.c:994 */
ucl_object_validate(schema, obj, NULL);
ucl_object_unref(obj);
ucl_object_unref(schema);
return 0;
}
Build and run:
cc -fsanitize=address -g -I include -o repro repro.c build/libucl.a
./repro
# SEGV at ucl_schema.c:994
ASAN output:
==PID==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
==PID==The signal is caused by a WRITE memory access.
#0 in ucl_schema_validate ucl_schema.c:994
#1 in ucl_schema_validate ucl_schema.c:955
#2 in ucl_object_validate_root_ext ucl_schema.c:1095
#3 in ucl_object_validate ucl_schema.c:1071
Suggested Fix
Add NULL guards at both locations:
// Line 994
if (err != NULL) err->code = UCL_SCHEMA_OK;
// Line 1023
if (err != NULL) err->code = UCL_SCHEMA_OK;
This matches the pattern already used by ucl_schema_create_error() throughout the same file. Happy to send a PR if that helps.
ucl_object_validate()crashes if you pass NULL for theerrparameter, which the API documents as optional. The internal functionucl_schema_validate()writes toerr->codedirectly in two places (src/ucl_schema.c:994and:1023) without a NULL guard. The rest of the file usesucl_schema_create_error()which does check for NULL, so these two spots were just missed. Affects 0.9.4 and earlier. (CWE-476). The two unguarded writes:Location 1 (
ucl_schema.c:994), afteranyOfsucceeds:Location 2 (
ucl_schema.c:1023), afternotsucceeds:Reproduction
Found by fuzzing (
-fsanitize=fuzzer,address). Minimal reproducer:Build and run:
cc -fsanitize=address -g -I include -o repro repro.c build/libucl.a ./repro # SEGV at ucl_schema.c:994ASAN output:
Suggested Fix
Add NULL guards at both locations:
This matches the pattern already used by
ucl_schema_create_error()throughout the same file. Happy to send a PR if that helps.