/src/tinygltf/tests/v3/fuzzer/fuzz_gltf_v3.cc
Line | Count | Source |
1 | | /* |
2 | | * fuzz_gltf_v3.cc — libFuzzer harness for tinygltf v3 parser. |
3 | | * |
4 | | * Fuzz targets: |
5 | | * - Auto-detect (GLB or JSON) parse from arbitrary bytes |
6 | | * - Exercises JSON parser, GLB header parsing, arena allocator, |
7 | | * error stack, and all glTF entity parsing paths. |
8 | | * |
9 | | * Build (clang with libFuzzer): |
10 | | * clang++ -g -O1 -fsanitize=fuzzer,address,undefined \ |
11 | | * -std=c++17 -fno-rtti -fno-exceptions \ |
12 | | * -I../../.. -o fuzz_gltf_v3 fuzz_gltf_v3.cc |
13 | | * |
14 | | * Run: |
15 | | * ./fuzz_gltf_v3 corpus/ -max_len=65536 |
16 | | * |
17 | | * Seed corpus: place valid .gltf and .glb files in corpus/ |
18 | | */ |
19 | | |
20 | | #define TINYGLTF3_IMPLEMENTATION |
21 | | #include "tiny_gltf_v3.h" |
22 | | |
23 | | #include <cstdint> |
24 | | #include <cstddef> |
25 | | |
26 | | /* Memory budget to prevent OOM during fuzzing */ |
27 | | static const uint64_t FUZZ_MEMORY_BUDGET = 64ULL * 1024 * 1024; /* 64 MB */ |
28 | | |
29 | 381 | static void fuzz_parse_auto(const uint8_t *data, size_t size) { |
30 | 381 | tg3_model model; |
31 | 381 | tg3_error_stack errors; |
32 | 381 | tg3_error_stack_init(&errors); |
33 | | |
34 | 381 | tg3_parse_options opts; |
35 | 381 | tg3_parse_options_init(&opts); |
36 | 381 | opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; |
37 | | |
38 | 381 | tg3_parse_auto(&model, &errors, data, (uint64_t)size, |
39 | 381 | "", 0, &opts); |
40 | | |
41 | 381 | tg3_model_free(&model); |
42 | 381 | tg3_error_stack_free(&errors); |
43 | 381 | } |
44 | | |
45 | 17 | static void fuzz_parse_json(const uint8_t *data, size_t size) { |
46 | 17 | tg3_model model; |
47 | 17 | tg3_error_stack errors; |
48 | 17 | tg3_error_stack_init(&errors); |
49 | | |
50 | 17 | tg3_parse_options opts; |
51 | 17 | tg3_parse_options_init(&opts); |
52 | 17 | opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; |
53 | | |
54 | 17 | tg3_parse(&model, &errors, data, (uint64_t)size, |
55 | 17 | "", 0, &opts); |
56 | | |
57 | 17 | tg3_model_free(&model); |
58 | 17 | tg3_error_stack_free(&errors); |
59 | 17 | } |
60 | | |
61 | 10 | static void fuzz_parse_glb(const uint8_t *data, size_t size) { |
62 | 10 | tg3_model model; |
63 | 10 | tg3_error_stack errors; |
64 | 10 | tg3_error_stack_init(&errors); |
65 | | |
66 | 10 | tg3_parse_options opts; |
67 | 10 | tg3_parse_options_init(&opts); |
68 | 10 | opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; |
69 | | |
70 | 10 | tg3_parse_glb(&model, &errors, data, (uint64_t)size, |
71 | 10 | "", 0, &opts); |
72 | | |
73 | 10 | tg3_model_free(&model); |
74 | 10 | tg3_error_stack_free(&errors); |
75 | 10 | } |
76 | | |
77 | 85 | static void fuzz_parse_float32(const uint8_t *data, size_t size) { |
78 | 85 | tg3_model model; |
79 | 85 | tg3_error_stack errors; |
80 | 85 | tg3_error_stack_init(&errors); |
81 | | |
82 | 85 | tg3_parse_options opts; |
83 | 85 | tg3_parse_options_init(&opts); |
84 | 85 | opts.memory.memory_budget = FUZZ_MEMORY_BUDGET; |
85 | 85 | opts.parse_float32 = 1; |
86 | | |
87 | 85 | tg3_parse_auto(&model, &errors, data, (uint64_t)size, |
88 | 85 | "", 0, &opts); |
89 | | |
90 | 85 | tg3_model_free(&model); |
91 | 85 | tg3_error_stack_free(&errors); |
92 | 85 | } |
93 | | |
94 | 567 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
95 | 567 | if (size == 0) return 0; |
96 | | |
97 | | /* Use first byte to select parse path, rest is the payload */ |
98 | 567 | uint8_t selector = data[0] % 4; |
99 | 567 | const uint8_t *payload = data + 1; |
100 | 567 | size_t payload_size = size - 1; |
101 | | |
102 | 567 | switch (selector) { |
103 | 385 | case 0: fuzz_parse_auto(payload, payload_size); break; |
104 | 63 | case 1: fuzz_parse_json(payload, payload_size); break; |
105 | 10 | case 2: fuzz_parse_glb(payload, payload_size); break; |
106 | 109 | case 3: fuzz_parse_float32(payload, payload_size); break; |
107 | 567 | } |
108 | | |
109 | 567 | return 0; |
110 | 567 | } |