Coverage Report

Created: 2026-05-30 06:04

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