Coverage Report

Created: 2026-09-01 07:15

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