Coverage Report

Created: 2024-06-18 06:29

/src/oss-fuzz-fuzzers/libtheora/fuzzer.cpp
Line
Count
Source (jump to first uncovered line)
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.48k
#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
    ds(ds)
34
2.17k
{ }
35
36
2.17k
TheoraDecoder::~TheoraDecoder(void) {
37
2.17k
    /* noret */ th_info_clear(&ti);
38
2.17k
    /* noret */ th_comment_clear(&tc);
39
40
2.17k
    if ( ctx != nullptr ) {
41
1.07k
        th_decode_free(ctx);
42
1.07k
    }
43
44
2.17k
    if ( tsi != nullptr ) {
45
40
        th_setup_free(tsi);
46
40
    }
47
2.17k
}
48
49
2.17k
bool TheoraDecoder::initialize(void) {
50
2.17k
    /* noret */ th_info_init(&ti);
51
2.17k
    /* noret */ th_comment_init(&tc);
52
53
6.48k
    for (int i = 0; i < THEORA_NUM_HEADER_PACKETS; i++) {
54
5.36k
        ogg_packet op = { 0 };
55
56
5.36k
        std::vector<uint8_t> packet;
57
        /* Fill packet */
58
5.36k
        {
59
5.36k
            try {
60
5.36k
                packet = ds.GetData(0);
61
5.36k
            } catch ( ... ) {
62
128
                return false;
63
128
            }
64
65
5.23k
            op.packet = packet.data();
66
5.23k
            op.bytes = packet.size();
67
5.23k
            op.b_o_s = 1;
68
5.23k
        }
69
70
5.23k
        if (th_decode_headerin(&ti, &tc, &tsi, &op) < 0) {
71
926
            return false;
72
926
        }
73
5.23k
    }
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
36
        return false;
80
36
    }
81
82
1.08k
    ctx = th_decode_alloc(&ti, tsi);
83
1.08k
    if ( ctx == nullptr ) {
84
4
        return false;
85
4
    }
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
2.26k
    for (int i = 0; i < tc.comments; i++) {
95
1.15k
        if (tc.user_comments[i]) {
96
1.15k
            const int len = tc.comment_lengths[i];
97
1.15k
            fuzzing::memory::memory_test(tc.user_comments[i], len);
98
1.15k
        }
99
1.15k
    }
100
1.11k
}
101
102
1.22k
bool TheoraDecoder::decodePacket(void) {
103
1.22k
    int err;
104
1.22k
    ogg_packet op = { 0 };
105
106
1.22k
    std::vector<uint8_t> packet;
107
108
    /* Fill packet */
109
1.22k
    {
110
1.22k
        memset(&op, 0, sizeof(op));
111
1.22k
        packet = ds.GetData(0);
112
1.22k
        op.packet = packet.data();
113
1.22k
        op.bytes = packet.size();
114
1.22k
        op.granulepos = -1;
115
        /* TODO op.packetno */
116
1.22k
    }
117
118
    /* Decode */
119
1.22k
    {
120
1.22k
        ogg_int64_t granulepos;
121
1.22k
        err = th_decode_packetin(ctx, &op, &granulepos);
122
1.22k
        if (err < 0) {
123
6
            return false;
124
6
        }
125
126
        /* Verify that granulepos has been set */
127
1.21k
        fuzzing::memory::memory_test(granulepos);
128
1.21k
    }
129
130
    /* Write image data */
131
1.21k
    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.21k
    return true;
142
1.21k
}
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.frame_width+1&~1)-(ti.pic_x&~1);
150
1.10k
    const int h = (ti.pic_y+ti.frame_height+1&~1)-(ti.pic_y&~1);
151
152
153
1.10k
    if ( px_fmt == TH_PF_422) {
154
214
        const int uv_offset = (ti.pic_x/2) + (ycbcrbuf[1].stride) * (ti.pic_y);
155
156
102k
        for(int i = 0; i < h; i++) {
157
102k
            {
158
102k
                const uint8_t* in_y = ycbcrbuf[0].data+y_offset+ycbcrbuf[0].stride*i;
159
102k
                fuzzing::memory::memory_test(in_y, w);
160
102k
            }
161
162
102k
            {
163
102k
                const uint8_t* in_u = ycbcrbuf[1].data+uv_offset+ycbcrbuf[1].stride*i;
164
102k
                const uint8_t* in_v = ycbcrbuf[2].data+uv_offset+ycbcrbuf[2].stride*i;
165
102k
                fuzzing::memory::memory_test(in_u, w >> 1);
166
102k
                fuzzing::memory::memory_test(in_v, w >> 1);
167
102k
            }
168
102k
        }
169
892
    } else {
170
892
        const int uv_offset = (ti.pic_x/2) + (ycbcrbuf[1].stride) * (ti.pic_y/2);
171
172
393k
        for(int i = 0; i < h; i++) {
173
392k
            fuzzing::memory::memory_test(ycbcrbuf[0].data+y_offset+ycbcrbuf[0].stride*i, w);
174
392k
        }
175
176
197k
        for(int i = 0; i < h/2; i++){
177
196k
            fuzzing::memory::memory_test(ycbcrbuf[2].data+uv_offset+ycbcrbuf[2].stride*i, w/2);
178
196k
            fuzzing::memory::memory_test(ycbcrbuf[1].data+uv_offset+ycbcrbuf[1].stride*i, w/2);
179
196k
        }
180
892
    }
181
1.10k
}
182
183
2.17k
void TheoraDecoder::Run(void) {
184
2.17k
    if ( initialize() == false ) {
185
1.09k
        return;
186
1.09k
    }
187
188
1.07k
    try {
189
1.07k
        size_t numDecoded = 0;
190
2.29k
        while ( ++numDecoded < 10 && ds.Get<bool>() == true ) {
191
1.22k
            if ( decodePacket() == false ) {
192
6
                break;
193
6
            }
194
1.22k
        }
195
1.07k
    } catch ( ... ) { }
196
1.07k
}
197
198
2
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
199
2
    return 0;
200
2
}
201
202
2.17k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
203
2.17k
    fuzzing::datasource::Datasource ds(data, size);
204
2.17k
    TheoraDecoder decoder(ds);
205
206
2.17k
    decoder.Run();
207
208
2.17k
    return 0;
209
2.17k
}