Coverage Report

Created: 2026-08-31 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}