/src/speex/contrib/oss-fuzz/speexdec_fuzzer.cc
Line | Count | Source |
1 | | /* Copyright (C) 2019 Tristan Matthews |
2 | | File: speexdec_fuzzer.cc (based on speexdec.c) |
3 | | |
4 | | Redistribution and use in source and binary forms, with or without |
5 | | modification, are permitted provided that the following conditions |
6 | | are met: |
7 | | |
8 | | - Redistributions of source code must retain the above copyright |
9 | | notice, this list of conditions and the following disclaimer. |
10 | | |
11 | | - Redistributions in binary form must reproduce the above copyright |
12 | | notice, this list of conditions and the following disclaimer in the |
13 | | documentation and/or other materials provided with the distribution. |
14 | | |
15 | | - Neither the name of the Xiph.org Foundation nor the names of its |
16 | | contributors may be used to endorse or promote products derived from |
17 | | this software without specific prior written permission. |
18 | | |
19 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR |
23 | | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
24 | | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
25 | | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
26 | | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
27 | | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
28 | | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
29 | | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | | */ |
31 | | |
32 | | #ifdef HAVE_CONFIG_H |
33 | | # include "config.h" |
34 | | #endif |
35 | | |
36 | | #include <stdio.h> |
37 | | #include <unistd.h> |
38 | | #include <stdlib.h> |
39 | | #include <string.h> |
40 | | |
41 | | #include <ogg/ogg.h> |
42 | | |
43 | | #include <limits.h> |
44 | | #include <math.h> |
45 | | |
46 | | #include <string.h> |
47 | | #include "speex/speex_header.h" |
48 | | #include "speex/speex_stereo.h" |
49 | | #include "speex/speex_callbacks.h" |
50 | | |
51 | | #define MAX_FRAME_SIZE 2000 |
52 | | |
53 | | #ifndef DISABLE_FLOAT_API |
54 | | typedef float output_type; |
55 | 65.7k | #define speex_decode_func(a, b, c) speex_decode(a, b, c) |
56 | 9.67k | #define speex_decode_stereo_func(a, b, c) speex_decode_stereo(a, b, c) |
57 | | #else |
58 | | typedef spx_int16_t output_type; |
59 | | #define speex_decode_func(a, b, c) speex_decode_int(a, b, c) |
60 | | #define speex_decode_stereo_func(a, b, c) speex_decode_stereo_int(a, b, c) |
61 | | #endif |
62 | | |
63 | | static void *process_header(ogg_packet *op, spx_int32_t enh_enabled, spx_int32_t *frame_size, int *granule_frame_size, spx_int32_t *rate, int *nframes, int *channels, SpeexStereoState *stereo, int *extra_headers) |
64 | 4.55k | { |
65 | 4.55k | void *st; |
66 | 4.55k | const SpeexMode *mode; |
67 | 4.55k | SpeexHeader *header; |
68 | 4.55k | int modeID; |
69 | 4.55k | SpeexCallback callback; |
70 | | |
71 | 4.55k | header = speex_packet_to_header((char*)op->packet, op->bytes); |
72 | 4.55k | if (!header) |
73 | 276 | { |
74 | 276 | return NULL; |
75 | 276 | } |
76 | 4.27k | if (header->mode >= SPEEX_NB_MODES || header->mode<0) |
77 | 0 | { |
78 | 0 | free(header); |
79 | 0 | return NULL; |
80 | 0 | } |
81 | | |
82 | 4.27k | modeID = header->mode; |
83 | | |
84 | 4.27k | mode = speex_lib_get_mode (modeID); |
85 | | |
86 | 4.27k | if (header->speex_version_id > 1) |
87 | 15 | { |
88 | 15 | free(header); |
89 | 15 | return NULL; |
90 | 15 | } |
91 | | |
92 | 4.26k | if (mode->bitstream_version < header->mode_bitstream_version) |
93 | 40 | { |
94 | 40 | free(header); |
95 | 40 | return NULL; |
96 | 40 | } |
97 | 4.22k | if (mode->bitstream_version > header->mode_bitstream_version) |
98 | 128 | { |
99 | 128 | free(header); |
100 | 128 | return NULL; |
101 | 128 | } |
102 | | |
103 | 4.09k | st = speex_decoder_init(mode); |
104 | 4.09k | if (!st) |
105 | 0 | { |
106 | 0 | free(header); |
107 | 0 | return NULL; |
108 | 0 | } |
109 | 4.09k | speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled); |
110 | 4.09k | speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size); |
111 | 4.09k | if (*frame_size < 0 || *frame_size > 2*320) |
112 | 0 | { |
113 | 0 | speex_decoder_destroy(st); |
114 | 0 | free(header); |
115 | 0 | return NULL; |
116 | 0 | } |
117 | 4.09k | *granule_frame_size = *frame_size; |
118 | | |
119 | 4.09k | if (!*rate) |
120 | 4.09k | *rate = header->rate; |
121 | | |
122 | 4.09k | speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, rate); |
123 | | |
124 | 4.09k | if (header->frames_per_packet < 1 || header->frames_per_packet > 10) |
125 | 157 | { |
126 | 157 | speex_decoder_destroy(st); |
127 | 157 | free(header); |
128 | 157 | return NULL; |
129 | 157 | } |
130 | 3.93k | *nframes = header->frames_per_packet; |
131 | | |
132 | 3.93k | if (*channels==-1) |
133 | 3.93k | *channels = header->nb_channels; |
134 | | |
135 | 3.93k | if (!(*channels==1)) |
136 | 2.06k | { |
137 | 2.06k | *channels = 2; |
138 | 2.06k | callback.callback_id = SPEEX_INBAND_STEREO; |
139 | 2.06k | callback.func = speex_std_stereo_request_handler; |
140 | 2.06k | callback.data = stereo; |
141 | 2.06k | speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback); |
142 | 2.06k | } |
143 | | |
144 | 3.93k | if (header->extra_headers > INT_MAX - 1) |
145 | 3 | { |
146 | 3 | speex_decoder_destroy(st); |
147 | 3 | free(header); |
148 | 3 | return NULL; |
149 | 3 | } |
150 | 3.93k | *extra_headers = header->extra_headers; |
151 | | |
152 | 3.93k | free(header); |
153 | 3.93k | return st; |
154 | 3.93k | } |
155 | | |
156 | | static void cleanup(void *st, SpeexBits *bits, int stream_init, ogg_stream_state *os, ogg_sync_state *oy) |
157 | 6.13k | { |
158 | 6.13k | if (st) |
159 | 3.93k | speex_decoder_destroy(st); |
160 | | |
161 | 6.13k | speex_bits_destroy(bits); |
162 | 6.13k | if (stream_init) |
163 | 5.90k | ogg_stream_clear(os); |
164 | 6.13k | ogg_sync_clear(oy); |
165 | 6.13k | } |
166 | | |
167 | | |
168 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *fuzz_data, size_t fuzz_size) |
169 | 6.14k | { |
170 | 6.14k | output_type output[MAX_FRAME_SIZE]; |
171 | 6.14k | int frame_size=0, granule_frame_size=0; |
172 | 6.14k | void *st=NULL; |
173 | 6.14k | SpeexBits bits; |
174 | 6.14k | int packet_count=0; |
175 | 6.14k | int stream_init = 0; |
176 | 6.14k | ogg_int64_t page_granule=0, last_granule=0; |
177 | 6.14k | int skip_samples=0, page_nb_packets; |
178 | 6.14k | ogg_sync_state oy; |
179 | 6.14k | ogg_page og; |
180 | 6.14k | ogg_packet op; |
181 | 6.14k | ogg_stream_state os; |
182 | 6.14k | int enh_enabled; |
183 | 6.14k | int nframes=2; |
184 | 6.14k | int eos=0; |
185 | 6.14k | SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT; |
186 | 6.14k | int channels=-1; |
187 | 6.14k | int rate=0; |
188 | 6.14k | int extra_headers=0; |
189 | 6.14k | int lookahead; |
190 | 6.14k | int speex_serialno = -1; |
191 | | |
192 | 6.14k | enh_enabled = 1; |
193 | | |
194 | | /*Check fuzz_data meets size requirements*/ |
195 | 6.14k | if (fuzz_size > 4096) |
196 | 10 | return 0; |
197 | | |
198 | | /*Init Ogg data struct*/ |
199 | 6.13k | ogg_sync_init(&oy); |
200 | | |
201 | 6.13k | speex_bits_init(&bits); |
202 | | /*Main decoding loop*/ |
203 | | |
204 | 6.13k | ssize_t bytes_remaining = fuzz_size; |
205 | 12.1k | while (1) |
206 | 12.1k | { |
207 | 12.1k | char *data; |
208 | 12.1k | int j, nb_read; |
209 | | /*Get the ogg buffer for writing*/ |
210 | 12.1k | nb_read = bytes_remaining > 200 ? 200 : bytes_remaining; |
211 | 12.1k | data = ogg_sync_buffer(&oy, nb_read); |
212 | | /*Read bitstream from data*/ |
213 | 12.1k | memcpy(data, fuzz_data, nb_read); |
214 | 12.1k | ogg_sync_wrote(&oy, nb_read); |
215 | 12.1k | bytes_remaining -= nb_read; |
216 | | |
217 | | /*Loop for all complete pages we got (most likely only one)*/ |
218 | 31.1k | while (ogg_sync_pageout(&oy, &og)==1) |
219 | 20.3k | { |
220 | 20.3k | int packet_no; |
221 | | |
222 | 20.3k | if (stream_init == 0) { |
223 | 5.90k | ogg_stream_init(&os, ogg_page_serialno(&og)); |
224 | 5.90k | stream_init = 1; |
225 | 5.90k | } |
226 | 20.3k | if (ogg_page_serialno(&og) != os.serialno) { |
227 | | /* so all streams are read. */ |
228 | 6.57k | ogg_stream_reset_serialno(&os, ogg_page_serialno(&og)); |
229 | 6.57k | } |
230 | | |
231 | | /*Add page to the bitstream*/ |
232 | 20.3k | ogg_stream_pagein(&os, &og); |
233 | 20.3k | page_granule = ogg_page_granulepos(&og); |
234 | 20.3k | page_nb_packets = ogg_page_packets(&og); |
235 | 20.3k | if (page_granule>0 && frame_size && (last_granule > 0 || INT64_MAX + last_granule > page_granule)) |
236 | 3.39k | { |
237 | | /* FIXME: shift the granule values if --force-* is specified */ |
238 | 3.39k | int64_t a = page_nb_packets*granule_frame_size*(int64_t)nframes; |
239 | 3.39k | int64_t b = page_granule - last_granule; |
240 | 3.39k | if (b > a || (INT64_MAX/640 - a < -b) || (a - b) > INT64_MAX/640) |
241 | 409 | { |
242 | 409 | cleanup(st, &bits, stream_init, &os, &oy); |
243 | 409 | return 0; |
244 | 409 | } |
245 | 2.98k | skip_samples = frame_size*(int64_t)(a - b)/granule_frame_size; |
246 | 2.98k | if (skip_samples == INT_MIN) { |
247 | 3 | cleanup(st, &bits, stream_init, &os, &oy); |
248 | 3 | return 0; |
249 | 3 | } |
250 | 2.98k | if (ogg_page_eos(&og)) |
251 | 541 | skip_samples = -skip_samples; |
252 | | /*else if (!ogg_page_bos(&og)) |
253 | | skip_samples = 0;*/ |
254 | 16.9k | } else if (page_granule<-1) { |
255 | 324 | cleanup(st, &bits, stream_init, &os, &oy); |
256 | 324 | return 0; |
257 | 16.6k | } else { |
258 | 16.6k | skip_samples = 0; |
259 | 16.6k | } |
260 | 19.6k | last_granule = page_granule; |
261 | | /*Extract all available packets*/ |
262 | 19.6k | packet_no=0; |
263 | 75.2k | while (!eos && ogg_stream_packetout(&os, &op) == 1) |
264 | 59.6k | { |
265 | 59.6k | if (op.bytes>=5 && !memcmp(op.packet, "Speex", 5)) { |
266 | 6.54k | speex_serialno = os.serialno; |
267 | 6.54k | } |
268 | 59.6k | if (speex_serialno == -1 || os.serialno != speex_serialno) |
269 | 3.37k | break; |
270 | | /*If first packet, process as Speex header*/ |
271 | 56.2k | if (packet_count==0) |
272 | 4.55k | { |
273 | 4.55k | st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, &channels, &stereo, &extra_headers); |
274 | 4.55k | if (!st) |
275 | 619 | { |
276 | 619 | cleanup(st, &bits, stream_init, &os, &oy); |
277 | 619 | return 0; |
278 | 619 | } |
279 | 3.93k | speex_decoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead); |
280 | 3.93k | if (!nframes) |
281 | 0 | nframes=1; |
282 | | |
283 | 51.6k | } else if (packet_count<=1+extra_headers) |
284 | 2.60k | { |
285 | | /* Ignore extra headers */ |
286 | 49.0k | } else { |
287 | 49.0k | packet_no++; |
288 | | |
289 | | /*End of stream condition*/ |
290 | 49.0k | if (op.e_o_s && os.serialno == speex_serialno) /* don't care for anything except speex eos */ |
291 | 928 | eos=1; |
292 | | |
293 | | /*Copy Ogg packet to Speex bitstream*/ |
294 | 49.0k | speex_bits_read_from(&bits, (char*)op.packet, op.bytes); |
295 | 66.6k | for (j=0;j!=nframes;j++) |
296 | 65.7k | { |
297 | 65.7k | int ret; |
298 | | /*Decode frame*/ |
299 | 65.7k | ret = speex_decode_func(st, &bits, output); |
300 | | |
301 | 65.7k | if (ret==-1) |
302 | 14.8k | break; |
303 | 50.8k | if (ret==-2) |
304 | 6.36k | { |
305 | 6.36k | break; |
306 | 6.36k | } |
307 | 44.4k | if (speex_bits_remaining(&bits)<0) |
308 | 26.8k | { |
309 | 26.8k | break; |
310 | 26.8k | } |
311 | 17.6k | if (channels==2) |
312 | 9.67k | speex_decode_stereo_func(output, frame_size, &stereo); |
313 | | |
314 | 17.6k | if (INT_MAX - lookahead > skip_samples) |
315 | 17.6k | { |
316 | 17.6k | int new_frame_size = frame_size; |
317 | 17.6k | if (packet_no == 1 && j==0 && skip_samples > 0) |
318 | 99 | { |
319 | 99 | new_frame_size -= skip_samples+lookahead; |
320 | 99 | } |
321 | 17.6k | if (packet_no == page_nb_packets && skip_samples < 0) |
322 | 5 | { |
323 | 5 | int packet_length = nframes*frame_size+skip_samples+lookahead; |
324 | 5 | new_frame_size = packet_length - j*frame_size; |
325 | 5 | if (new_frame_size<0) |
326 | 5 | new_frame_size = 0; |
327 | 5 | if (new_frame_size>frame_size) |
328 | 0 | new_frame_size = frame_size; |
329 | 5 | } |
330 | 17.6k | } |
331 | 17.6k | } |
332 | 49.0k | } |
333 | 55.6k | packet_count++; |
334 | 55.6k | } |
335 | 19.6k | } |
336 | 10.7k | if (bytes_remaining <= 0) |
337 | 4.77k | break; |
338 | 10.7k | } |
339 | | |
340 | 4.77k | cleanup(st, &bits, stream_init, &os, &oy); |
341 | | |
342 | 4.77k | return 0; |
343 | 6.13k | } |