/* * fuzz_gltf_v3.cc — libFuzzer harness for tinygltf v3 parser. * * Fuzz targets: * - Auto-detect (GLB or JSON) parse from arbitrary bytes * - Exercises JSON parser, GLB header parsing, arena allocator, * error stack, and all glTF entity parsing paths. * * Build (clang with libFuzzer): * clang++ -g -O1 -fsanitize=fuzzer,address,undefined \ * -std=c++17 -fno-rtti -fno-exceptions \ * -I../../.. -o fuzz_gltf_v3 fuzz_gltf_v3.cc * * Run: * ./fuzz_gltf_v3 corpus/ -max_len=65536 * * Seed corpus: place valid .gltf and .glb files in corpus/ */ #define TINYGLTF3_IMPLEMENTATION #include "tiny_gltf_v3.h" #include #include /* Memory budget to prevent OOM during fuzzing */ static const uint64_t FUZZ_MEMORY_BUDGET = 64ULL * 1024 * 1024; /* 64 MB */ static void fuzz_parse_auto(const uint8_t *data, size_t size) { tg3_model model; tg3_error_stack errors; tg3_error_stack_init(&errors); tg3_parse_options opts; tg3_parse_options_init(&opts); opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; tg3_parse_auto(&model, &errors, data, (uint64_t)size, "", 0, &opts); tg3_model_free(&model); tg3_error_stack_free(&errors); } static void fuzz_parse_json(const uint8_t *data, size_t size) { tg3_model model; tg3_error_stack errors; tg3_error_stack_init(&errors); tg3_parse_options opts; tg3_parse_options_init(&opts); opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; tg3_parse(&model, &errors, data, (uint64_t)size, "", 0, &opts); tg3_model_free(&model); tg3_error_stack_free(&errors); } static void fuzz_parse_glb(const uint8_t *data, size_t size) { tg3_model model; tg3_error_stack errors; tg3_error_stack_init(&errors); tg3_parse_options opts; tg3_parse_options_init(&opts); opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; tg3_parse_glb(&model, &errors, data, (uint64_t)size, "", 0, &opts); tg3_model_free(&model); tg3_error_stack_free(&errors); } static void fuzz_parse_float32(const uint8_t *data, size_t size) { tg3_model model; tg3_error_stack errors; tg3_error_stack_init(&errors); tg3_parse_options opts; tg3_parse_options_init(&opts); opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; opts.parse_float32 = 1; tg3_parse_auto(&model, &errors, data, (uint64_t)size, "", 0, &opts); tg3_model_free(&model); tg3_error_stack_free(&errors); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size == 0) return 0; /* Use first byte to select parse path, rest is the payload */ uint8_t selector = data[0] % 4; const uint8_t *payload = data + 1; size_t payload_size = size - 1; switch (selector) { case 0: fuzz_parse_auto(payload, payload_size); break; case 1: fuzz_parse_json(payload, payload_size); break; case 2: fuzz_parse_glb(payload, payload_size); break; case 3: fuzz_parse_float32(payload, payload_size); break; } return 0; }