Coverage Report

Created: 2025-12-31 06:21

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
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
}