Coverage Report

Created: 2026-04-12 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/oss-fuzz-fuzzers/libtheora/fuzzer.cpp
Line
Count
Source
1
/* libtheora decoder fuzzer
2
 * 2019 Guido Vranken
3
 */
4
5
#include <cstddef>
6
#include <cstdint>
7
#include <fuzzing/datasource/datasource.hpp>
8
#include <fuzzing/memory.hpp>
9
#include <theora/theoradec.h>
10
11
6.20k
#define THEORA_NUM_HEADER_PACKETS 3
12
13
class TheoraDecoder {
14
    private:
15
        fuzzing::datasource::Datasource& ds;
16
17
        th_setup_info* tsi = nullptr;
18
        th_dec_ctx* ctx = nullptr;
19
        th_info ti;
20
        th_comment tc;
21
22
        bool initialize(void);
23
        void processComments(void) const;
24
        bool decodePacket(void);
25
        void writeImage(const th_ycbcr_buffer& image) const;
26
    public:
27
        TheoraDecoder(fuzzing::datasource::Datasource& ds);
28
        ~TheoraDecoder(void);
29
        void Run(void);
30
};
31
32
TheoraDecoder::TheoraDecoder(fuzzing::datasource::Datasource& ds) :
33
2.05k
    ds(ds)
34
2.05k
{ }
35
36
2.05k
TheoraDecoder::~TheoraDecoder(void) {
37
2.05k
    /* noret */ th_info_clear(&ti);
38
2.05k
    /* noret */ th_comment_clear(&tc);
39
40
2.05k
    if ( ctx != nullptr ) {
41
1.07k
        th_decode_free(ctx);
42
1.07k
    }
43
44
2.05k
    if ( tsi != nullptr ) {
45
33
        th_setup_free(tsi);
46
33
    }
47
2.05k
}
48
49
2.05k
bool TheoraDecoder::initialize(void) {
50
2.05k
    /* noret */ th_info_init(&ti);
51
2.05k
    /* noret */ th_comment_init(&tc);
52
53
6.20k
    for (int i = 0; i < THEORA_NUM_HEADER_PACKETS; i++) {
54
5.09k
        ogg_packet op = { 0 };
55
56
5.09k
        std::vector<uint8_t> packet;
57
        /* Fill packet */
58
5.09k
        {
59
5.09k
            try {
60
5.09k
                packet = ds.GetData(0);
61
5.09k
            } catch ( ... ) {
62
89
                return false;
63
89
            }
64
65
5.00k
            op.packet = packet.data();
66
5.00k
            op.bytes = packet.size();
67
5.00k
            op.b_o_s = 1;
68
5.00k
        }
69
70
5.00k
        if (th_decode_headerin(&ti, &tc, &tsi, &op) < 0) {
71
855
            return false;
72
855
        }
73
5.00k
    }
74
75
1.11k
    /* noret */ processComments();
76
77
    /* Limit picture resolution to prevent OOMs */
78
1.11k
    if ( ti.frame_width > 1024 || ti.frame_height > 1024 ) {
79
27
        return false;
80
27
    }
81
82
1.08k
    ctx = th_decode_alloc(&ti, tsi);
83
1.08k
    if ( ctx == nullptr ) {
84
6
        return false;
85
6
    }
86
87
1.07k
    /* noret */ th_setup_free(tsi);
88
1.07k
    tsi = nullptr;
89
90
1.07k
    return true;
91
1.08k
}
92
93
1.11k
void TheoraDecoder::processComments(void) const {
94
1.33k
    for (int i = 0; i < tc.comments; i++) {
95
219
        if (tc.user_comments[i]) {
96
219
            const int len = tc.comment_lengths[i];
97
219
            fuzzing::memory::memory_test(tc.user_comments[i], len);
98
219
        }
99
219
    }
100
1.11k
}
101
102
1.23k
bool TheoraDecoder::decodePacket(void) {
103
1.23k
    int err;
104
1.23k
    ogg_packet op = { 0 };
105
106
1.23k
    std::vector<uint8_t> packet;
107
108
    /* Fill packet */
109
1.23k
    {
110
1.23k
        memset(&op, 0, sizeof(op));
111
1.23k
        packet = ds.GetData(0);
112
1.23k
        op.packet = packet.data();
113
1.23k
        op.bytes = packet.size();
114
1.23k
        op.granulepos = -1;
115
        /* TODO op.packetno */
116
1.23k
    }
117
118
    /* Decode */
119
1.23k
    {
120
1.23k
        ogg_int64_t granulepos;
121
1.23k
        err = th_decode_packetin(ctx, &op, &granulepos);
122
1.23k
        if (err < 0) {
123
6
            return false;
124
6
        }
125
126
        /* Verify that granulepos has been set */
127
1.23k
        fuzzing::memory::memory_test(granulepos);
128
1.23k
    }
129
130
    /* Write image data */
131
1.23k
    if (err != TH_DUPFRAME) {
132
1.10k
        th_ycbcr_buffer ycbcrbuf;
133
1.10k
        err = th_decode_ycbcr_out(ctx, ycbcrbuf);
134
1.10k
        if (err != 0) {
135
0
            return false;
136
0
        }
137
138
1.10k
        /* noret */ writeImage(ycbcrbuf);
139
1.10k
    }
140
141
1.23k
    return true;
142
1.23k
}
143
144
1.10k
void TheoraDecoder::writeImage(const th_ycbcr_buffer& ycbcrbuf) const {
145
    /* Modelled after examples/player_example.c */
146
147
1.10k
    const th_pixel_fmt px_fmt = ti.pixel_fmt;
148
1.10k
    const int y_offset = (ti.pic_x&~1) + ycbcrbuf[0].stride * (ti.pic_y&~1);
149
1.10k
    const int w = (ti.pic_x+ti.pic_width+1&~1)-(ti.pic_x&~1);
150
1.10k
    const int h = (ti.pic_y+ti.pic_height+1&~1)-(ti.pic_y&~1);
151
152
153
1.10k
    if ( px_fmt == TH_PF_422) {
154
242
        const int uv_offset = (ti.pic_x/2) + (ycbcrbuf[1].stride) * (ti.pic_y);
155
156
18.5k
        for(int i = 0; i < h; i++) {
157
18.2k
            {
158
18.2k
                const uint8_t* in_y = ycbcrbuf[0].data+y_offset+ycbcrbuf[0].stride*i;
159
18.2k
                fuzzing::memory::memory_test(in_y, w);
160
18.2k
            }
161
162
18.2k
            {
163
18.2k
                const uint8_t* in_u = ycbcrbuf[1].data+uv_offset+ycbcrbuf[1].stride*i;
164
18.2k
                const uint8_t* in_v = ycbcrbuf[2].data+uv_offset+ycbcrbuf[2].stride*i;
165
18.2k
                fuzzing::memory::memory_test(in_u, w >> 1);
166
18.2k
                fuzzing::memory::memory_test(in_v, w >> 1);
167
18.2k
            }
168
18.2k
        }
169
858
    } else if (px_fmt==TH_PF_444){
170
29.2k
        for(int i = 0; i < h; i++) {
171
28.9k
            fuzzing::memory::memory_test(ycbcrbuf[0].data+y_offset+ycbcrbuf[0].stride*i, w);
172
28.9k
            fuzzing::memory::memory_test(ycbcrbuf[1].data+y_offset+ycbcrbuf[1].stride*i, w);
173
28.9k
            fuzzing::memory::memory_test(ycbcrbuf[2].data+y_offset+ycbcrbuf[2].stride*i, w);
174
28.9k
        }
175
573
    } else {
176
573
        const int uv_offset = (ti.pic_x/2) + (ycbcrbuf[1].stride) * (ti.pic_y/2);
177
178
29.9k
        for(int i = 0; i < h; i++) {
179
29.3k
            fuzzing::memory::memory_test(ycbcrbuf[0].data+y_offset+ycbcrbuf[0].stride*i, w);
180
29.3k
        }
181
182
15.2k
        for(int i = 0; i < h/2; i++){
183
14.6k
            fuzzing::memory::memory_test(ycbcrbuf[2].data+uv_offset+ycbcrbuf[2].stride*i, w/2);
184
14.6k
            fuzzing::memory::memory_test(ycbcrbuf[1].data+uv_offset+ycbcrbuf[1].stride*i, w/2);
185
14.6k
        }
186
573
    }
187
1.10k
}
188
189
2.05k
void TheoraDecoder::Run(void) {
190
2.05k
    if ( initialize() == false ) {
191
977
        return;
192
977
    }
193
194
1.07k
    try {
195
1.07k
        size_t numDecoded = 0;
196
2.30k
        while ( ++numDecoded < 10 && ds.Get<bool>() == true ) {
197
1.23k
            if ( decodePacket() == false ) {
198
6
                break;
199
6
            }
200
1.23k
        }
201
1.07k
    } catch ( ... ) { }
202
1.07k
}
203
204
2
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
205
2
    return 0;
206
2
}
207
208
2.05k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
209
2.05k
    fuzzing::datasource::Datasource ds(data, size);
210
2.05k
    TheoraDecoder decoder(ds);
211
212
2.05k
    decoder.Run();
213
214
2.05k
    return 0;
215
2.05k
}