/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 | 63.4k | #define speex_decode_func(a, b, c) speex_decode(a, b, c) |
56 | 10.7k | #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.34k | { |
65 | 4.34k | void *st; |
66 | 4.34k | const SpeexMode *mode; |
67 | 4.34k | SpeexHeader *header; |
68 | 4.34k | int modeID; |
69 | 4.34k | SpeexCallback callback; |
70 | | |
71 | 4.34k | header = speex_packet_to_header((char*)op->packet, op->bytes); |
72 | 4.34k | if (!header) |
73 | 282 | { |
74 | 282 | return NULL; |
75 | 282 | } |
76 | 4.06k | if (header->mode >= SPEEX_NB_MODES || header->mode<0) |
77 | 0 | { |
78 | 0 | free(header); |
79 | 0 | return NULL; |
80 | 0 | } |
81 | | |
82 | 4.06k | modeID = header->mode; |
83 | | |
84 | 4.06k | mode = speex_lib_get_mode (modeID); |
85 | | |
86 | 4.06k | if (header->speex_version_id > 1) |
87 | 11 | { |
88 | 11 | free(header); |
89 | 11 | return NULL; |
90 | 11 | } |
91 | | |
92 | 4.05k | if (mode->bitstream_version < header->mode_bitstream_version) |
93 | 41 | { |
94 | 41 | free(header); |
95 | 41 | return NULL; |
96 | 41 | } |
97 | 4.01k | if (mode->bitstream_version > header->mode_bitstream_version) |
98 | 119 | { |
99 | 119 | free(header); |
100 | 119 | return NULL; |
101 | 119 | } |
102 | | |
103 | 3.89k | st = speex_decoder_init(mode); |
104 | 3.89k | if (!st) |
105 | 0 | { |
106 | 0 | free(header); |
107 | 0 | return NULL; |
108 | 0 | } |
109 | 3.89k | speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled); |
110 | 3.89k | speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size); |
111 | 3.89k | 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 | 3.89k | *granule_frame_size = *frame_size; |
118 | | |
119 | 3.89k | if (!*rate) |
120 | 3.89k | *rate = header->rate; |
121 | | |
122 | 3.89k | speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, rate); |
123 | | |
124 | 3.89k | if (header->frames_per_packet < 1 || header->frames_per_packet > 10) |
125 | 159 | { |
126 | 159 | speex_decoder_destroy(st); |
127 | 159 | free(header); |
128 | 159 | return NULL; |
129 | 159 | } |
130 | 3.73k | *nframes = header->frames_per_packet; |
131 | | |
132 | 3.73k | if (*channels==-1) |
133 | 3.73k | *channels = header->nb_channels; |
134 | | |
135 | 3.73k | if (!(*channels==1)) |
136 | 1.94k | { |
137 | 1.94k | *channels = 2; |
138 | 1.94k | callback.callback_id = SPEEX_INBAND_STEREO; |
139 | 1.94k | callback.func = speex_std_stereo_request_handler; |
140 | 1.94k | callback.data = stereo; |
141 | 1.94k | speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback); |
142 | 1.94k | } |
143 | | |
144 | 3.73k | if (header->extra_headers > INT_MAX - 1) |
145 | 2 | { |
146 | 2 | speex_decoder_destroy(st); |
147 | 2 | free(header); |
148 | 2 | return NULL; |
149 | 2 | } |
150 | 3.73k | *extra_headers = header->extra_headers; |
151 | | |
152 | 3.73k | free(header); |
153 | 3.73k | return st; |
154 | 3.73k | } |
155 | | |
156 | | static void cleanup(void *st, SpeexBits *bits, int stream_init, ogg_stream_state *os, ogg_sync_state *oy) |
157 | 5.95k | { |
158 | 5.95k | if (st) |
159 | 3.73k | speex_decoder_destroy(st); |
160 | | |
161 | 5.95k | speex_bits_destroy(bits); |
162 | 5.95k | if (stream_init) |
163 | 5.71k | ogg_stream_clear(os); |
164 | 5.95k | ogg_sync_clear(oy); |
165 | 5.95k | } |
166 | | |
167 | | |
168 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *fuzz_data, size_t fuzz_size) |
169 | 5.96k | { |
170 | 5.96k | output_type output[MAX_FRAME_SIZE]; |
171 | 5.96k | int frame_size=0, granule_frame_size=0; |
172 | 5.96k | void *st=NULL; |
173 | 5.96k | SpeexBits bits; |
174 | 5.96k | int packet_count=0; |
175 | 5.96k | int stream_init = 0; |
176 | 5.96k | ogg_int64_t page_granule=0, last_granule=0; |
177 | 5.96k | int skip_samples=0, page_nb_packets; |
178 | 5.96k | ogg_sync_state oy; |
179 | 5.96k | ogg_page og; |
180 | 5.96k | ogg_packet op; |
181 | 5.96k | ogg_stream_state os; |
182 | 5.96k | int enh_enabled; |
183 | 5.96k | int nframes=2; |
184 | 5.96k | int eos=0; |
185 | 5.96k | SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT; |
186 | 5.96k | int channels=-1; |
187 | 5.96k | int rate=0; |
188 | 5.96k | int extra_headers=0; |
189 | 5.96k | int lookahead; |
190 | 5.96k | int speex_serialno = -1; |
191 | | |
192 | 5.96k | enh_enabled = 1; |
193 | | |
194 | | /*Check fuzz_data meets size requirements*/ |
195 | 5.96k | if (fuzz_size > 4096) |
196 | 11 | return 0; |
197 | | |
198 | | /*Init Ogg data struct*/ |
199 | 5.95k | ogg_sync_init(&oy); |
200 | | |
201 | 5.95k | speex_bits_init(&bits); |
202 | | /*Main decoding loop*/ |
203 | | |
204 | 5.95k | ssize_t bytes_remaining = fuzz_size; |
205 | 12.0k | while (1) |
206 | 12.0k | { |
207 | 12.0k | char *data; |
208 | 12.0k | int j, nb_read; |
209 | | /*Get the ogg buffer for writing*/ |
210 | 12.0k | nb_read = bytes_remaining > 200 ? 200 : bytes_remaining; |
211 | 12.0k | data = ogg_sync_buffer(&oy, nb_read); |
212 | | /*Read bitstream from data*/ |
213 | 12.0k | memcpy(data, fuzz_data, nb_read); |
214 | 12.0k | ogg_sync_wrote(&oy, nb_read); |
215 | 12.0k | bytes_remaining -= nb_read; |
216 | | |
217 | | /*Loop for all complete pages we got (most likely only one)*/ |
218 | 30.7k | while (ogg_sync_pageout(&oy, &og)==1) |
219 | 20.1k | { |
220 | 20.1k | int packet_no; |
221 | | |
222 | 20.1k | if (stream_init == 0) { |
223 | 5.71k | ogg_stream_init(&os, ogg_page_serialno(&og)); |
224 | 5.71k | stream_init = 1; |
225 | 5.71k | } |
226 | 20.1k | if (ogg_page_serialno(&og) != os.serialno) { |
227 | | /* so all streams are read. */ |
228 | 6.71k | ogg_stream_reset_serialno(&os, ogg_page_serialno(&og)); |
229 | 6.71k | } |
230 | | |
231 | | /*Add page to the bitstream*/ |
232 | 20.1k | ogg_stream_pagein(&os, &og); |
233 | 20.1k | page_granule = ogg_page_granulepos(&og); |
234 | 20.1k | page_nb_packets = ogg_page_packets(&og); |
235 | 20.1k | if (page_granule>0 && frame_size && (last_granule > 0 || INT64_MAX + last_granule > page_granule)) |
236 | 3.45k | { |
237 | | /* FIXME: shift the granule values if --force-* is specified */ |
238 | 3.45k | int64_t a = page_nb_packets*granule_frame_size*(int64_t)nframes; |
239 | 3.45k | int64_t b = page_granule - last_granule; |
240 | 3.45k | if (b > a || (INT64_MAX/640 - a < -b) || (a - b) > INT64_MAX/640) |
241 | 393 | { |
242 | 393 | cleanup(st, &bits, stream_init, &os, &oy); |
243 | 393 | return 0; |
244 | 393 | } |
245 | 3.06k | skip_samples = frame_size*(int64_t)(a - b)/granule_frame_size; |
246 | 3.06k | if (skip_samples == INT_MIN) { |
247 | 2 | cleanup(st, &bits, stream_init, &os, &oy); |
248 | 2 | return 0; |
249 | 2 | } |
250 | 3.06k | if (ogg_page_eos(&og)) |
251 | 514 | skip_samples = -skip_samples; |
252 | | /*else if (!ogg_page_bos(&og)) |
253 | | skip_samples = 0;*/ |
254 | 16.6k | } else if (page_granule<-1) { |
255 | 349 | cleanup(st, &bits, stream_init, &os, &oy); |
256 | 349 | return 0; |
257 | 16.3k | } else { |
258 | 16.3k | skip_samples = 0; |
259 | 16.3k | } |
260 | 19.4k | last_granule = page_granule; |
261 | | /*Extract all available packets*/ |
262 | 19.4k | packet_no=0; |
263 | 73.6k | while (!eos && ogg_stream_packetout(&os, &op) == 1) |
264 | 58.5k | { |
265 | 58.5k | if (op.bytes>=5 && !memcmp(op.packet, "Speex", 5)) { |
266 | 6.38k | speex_serialno = os.serialno; |
267 | 6.38k | } |
268 | 58.5k | if (speex_serialno == -1 || os.serialno != speex_serialno) |
269 | 3.65k | break; |
270 | | /*If first packet, process as Speex header*/ |
271 | 54.8k | if (packet_count==0) |
272 | 4.34k | { |
273 | 4.34k | st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, &channels, &stereo, &extra_headers); |
274 | 4.34k | if (!st) |
275 | 614 | { |
276 | 614 | cleanup(st, &bits, stream_init, &os, &oy); |
277 | 614 | return 0; |
278 | 614 | } |
279 | 3.73k | speex_decoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead); |
280 | 3.73k | if (!nframes) |
281 | 0 | nframes=1; |
282 | | |
283 | 50.5k | } else if (packet_count<=1+extra_headers) |
284 | 4.45k | { |
285 | | /* Ignore extra headers */ |
286 | 46.0k | } else { |
287 | 46.0k | packet_no++; |
288 | | |
289 | | /*End of stream condition*/ |
290 | 46.0k | if (op.e_o_s && os.serialno == speex_serialno) /* don't care for anything except speex eos */ |
291 | 805 | eos=1; |
292 | | |
293 | | /*Copy Ogg packet to Speex bitstream*/ |
294 | 46.0k | speex_bits_read_from(&bits, (char*)op.packet, op.bytes); |
295 | 64.5k | for (j=0;j!=nframes;j++) |
296 | 63.4k | { |
297 | 63.4k | int ret; |
298 | | /*Decode frame*/ |
299 | 63.4k | ret = speex_decode_func(st, &bits, output); |
300 | | |
301 | 63.4k | if (ret==-1) |
302 | 13.4k | break; |
303 | 50.0k | if (ret==-2) |
304 | 5.67k | { |
305 | 5.67k | break; |
306 | 5.67k | } |
307 | 44.3k | if (speex_bits_remaining(&bits)<0) |
308 | 25.8k | { |
309 | 25.8k | break; |
310 | 25.8k | } |
311 | 18.4k | if (channels==2) |
312 | 10.7k | speex_decode_stereo_func(output, frame_size, &stereo); |
313 | | |
314 | 18.4k | if (INT_MAX - lookahead > skip_samples) |
315 | 18.4k | { |
316 | 18.4k | int new_frame_size = frame_size; |
317 | 18.4k | if (packet_no == 1 && j==0 && skip_samples > 0) |
318 | 94 | { |
319 | 94 | new_frame_size -= skip_samples+lookahead; |
320 | 94 | } |
321 | 18.4k | if (packet_no == page_nb_packets && skip_samples < 0) |
322 | 4 | { |
323 | 4 | int packet_length = nframes*frame_size+skip_samples+lookahead; |
324 | 4 | new_frame_size = packet_length - j*frame_size; |
325 | 4 | if (new_frame_size<0) |
326 | 4 | new_frame_size = 0; |
327 | 4 | if (new_frame_size>frame_size) |
328 | 0 | new_frame_size = frame_size; |
329 | 4 | } |
330 | 18.4k | } |
331 | 18.4k | } |
332 | 46.0k | } |
333 | 54.2k | packet_count++; |
334 | 54.2k | } |
335 | 19.4k | } |
336 | 10.6k | if (bytes_remaining <= 0) |
337 | 4.59k | break; |
338 | 10.6k | } |
339 | | |
340 | 4.59k | cleanup(st, &bits, stream_init, &os, &oy); |
341 | | |
342 | 4.59k | return 0; |
343 | 5.95k | } |