// 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 #include #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; }