calog/tests/fuzzParser.c

38 lines
1.7 KiB
C

// fuzzParser.c -- one libFuzzer driver, compiled once per parser. The build selects the parser by
// #include'ing its whole translation unit (so the static entry point is visible) and naming its
// native via -DFUZZ_INCLUDE and -DFUZZ_NATIVE, e.g.
//
// clang -fsanitize=fuzzer,address,undefined -DFUZZ_INCLUDE='"calogJson.c"' \
// -DFUZZ_NATIVE=jsonParseNative ... tests/fuzzParser.c src/value.c src/broker.c ...
//
// Each parser native has the same shape (CalogValueT *args, int32_t argCount, CalogValueT *result,
// void *userData): we wrap the raw fuzz bytes in a binary-safe string as args[0] and drive the
// parser directly, so the fuzzer explores the parser/marshaller, not the script layer. See the
// `make fuzz` target. Build with clang throughout -- libcalog.a is gcc-ASan and cannot be mixed
// with clang's fuzzer/ASan runtime, so the core sources are compiled fresh alongside this file.
#include <stddef.h>
#include <stdint.h>
#include "calog.h"
// The parser under test, pulled in whole so its static native is reachable.
#include FUZZ_INCLUDE
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
CalogValueT args[1];
CalogValueT result;
if (calogValueString(&args[0], (const char *)data, (int64_t)size) != calogOkE) {
return 0;
}
// argCount 1: parsers that take an optional second argument (e.g. csvParse's delimiter) use
// their default, which is exactly the path we want to hammer with arbitrary input.
if (FUZZ_NATIVE(args, 1, &result, NULL) == calogOkE) {
calogValueFree(&result);
} else {
calogValueFree(&result); // a failed parse still initialises result (calogFail path)
}
calogValueFree(&args[0]);
return 0;
}