From 47bd91d2bbc1579e916949dd0860f4388a74394c Mon Sep 17 00:00:00 2001 From: XananasX7 Date: Fri, 29 May 2026 04:19:11 +0000 Subject: [PATCH] fuzzing: add cjson_print_roundtrip_fuzzer for serialize/round-trip paths Add a new libFuzzer harness that exercises cJSON's print/serialization code paths, which are not covered by the existing cjson_read_fuzzer: - cJSON_ParseWithLength(): parse with explicit length (catches off-by-one) - cJSON_PrintUnformatted(): serialize parsed trees to compact JSON - cJSON_Print(): formatted serialization - cJSON_PrintBuffered(): buffered print with small prebuf forces realloc - cJSON_Duplicate(): deep copy of parsed trees - Round-trip consistency: parse -> print -> re-parse These paths manipulate heap-allocated string buffers and are reachable whenever a caller serializes a parsed or programmatically built cJSON tree. --- fuzzing/cjson_print_roundtrip_fuzzer.c | 80 ++++++++++++++++++++++++++ fuzzing/ossfuzz.sh | 4 ++ 2 files changed, 84 insertions(+) create mode 100644 fuzzing/cjson_print_roundtrip_fuzzer.c diff --git a/fuzzing/cjson_print_roundtrip_fuzzer.c b/fuzzing/cjson_print_roundtrip_fuzzer.c new file mode 100644 index 00000000..6b65f110 --- /dev/null +++ b/fuzzing/cjson_print_roundtrip_fuzzer.c @@ -0,0 +1,80 @@ +/* + * cjson_print_roundtrip_fuzzer.c + * + * Fuzz harness for cJSON print/serialize paths and parse->print->parse + * round-trip consistency. Exercises: + * - cJSON_ParseWithLength() with arbitrary length mismatch + * - cJSON_PrintUnformatted() serialization of parsed trees + * - cJSON_PrintBuffered() with small prebuf to force reallocation + * - Round-trip: parse -> print -> re-parse (checks serializer output is valid) + * - cJSON_Print() (formatted) on the same tree + * - cJSON_Duplicate() for deep trees + * + * Build: compiled by fuzzing/ossfuzz.sh alongside cjson_read_fuzzer. + */ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../cJSON.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); /* required by C89 */ + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + cJSON *obj = NULL; + cJSON *reparsed = NULL; + cJSON *dup = NULL; + char *unformatted = NULL; + char *formatted = NULL; + char *buffered = NULL; + + if (size == 0) + return 0; + + /* 1. Parse with explicit length (may differ from null-termination) */ + obj = cJSON_ParseWithLength((const char *)data, size); + if (obj == NULL) + return 0; + + /* 2. Print unformatted */ + unformatted = cJSON_PrintUnformatted(obj); + if (unformatted != NULL) { + /* 3. Round-trip: re-parse the printed output */ + reparsed = cJSON_Parse(unformatted); + if (reparsed != NULL) { + cJSON_Delete(reparsed); + } + free(unformatted); + } + + /* 4. Print formatted */ + formatted = cJSON_Print(obj); + if (formatted != NULL) { + free(formatted); + } + + /* 5. PrintBuffered with small prebuf to exercise realloc path */ + buffered = cJSON_PrintBuffered(obj, 16, cJSON_False); + if (buffered != NULL) { + free(buffered); + } + + /* 6. Also exercise cJSON_Duplicate for deep trees */ + dup = cJSON_Duplicate(obj, cJSON_True); + if (dup != NULL) { + cJSON_Delete(dup); + } + + cJSON_Delete(obj); + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/fuzzing/ossfuzz.sh b/fuzzing/ossfuzz.sh index a2da64bf..f331e2bb 100755 --- a/fuzzing/ossfuzz.sh +++ b/fuzzing/ossfuzz.sh @@ -12,6 +12,10 @@ $CXX $CXXFLAGS $SRC/cjson/fuzzing/cjson_read_fuzzer.c -I. \ -o $OUT/cjson_read_fuzzer \ $LIB_FUZZING_ENGINE $SRC/cjson/build/libcjson.a +$CXX $CXXFLAGS $SRC/cjson/fuzzing/cjson_print_roundtrip_fuzzer.c -I. \ + -o $OUT/cjson_print_roundtrip_fuzzer \ + $LIB_FUZZING_ENGINE $SRC/cjson/build/libcjson.a + find $SRC/cjson/fuzzing/inputs -name "*" | \ xargs zip $OUT/cjson_read_fuzzer_seed_corpus.zip