Coverage Report

Created: 2026-10-28 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pjsip/tests/fuzz/fuzz-audio.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2026 Teluu Inc. (http://www.teluu.com)
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 2 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 */
18
#include <stdio.h>
19
#include <stdint.h>
20
#include <stdlib.h>
21
#include <string.h>
22
23
#include <pjlib.h>
24
#include <pjmedia.h>
25
#include <pjmedia-codec.h>
26
#include <pjmedia-codec/g722.h>
27
#include <pjmedia-codec/gsm.h>
28
#include <pjmedia-codec/speex.h>
29
#include <pjmedia-codec/ilbc.h>
30
#include <pjmedia-codec/l16.h>
31
#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC != 0)
32
#  include <pjmedia-codec/opus.h>
33
#endif
34
35
/* Codec configuration structure */
36
typedef struct {
37
    pjmedia_codec **codec_ptr;
38
    const char *name;
39
    size_t min_frame_size;
40
    size_t pcm_frame_size;
41
    unsigned channel_cnt;
42
} codec_config_t;
43
44
/* Global state for persistent fuzzing */
45
static pj_caching_pool caching_pool;
46
static pjmedia_endpt *endpt = NULL;
47
static pj_pool_t *pool = NULL;
48
static pjmedia_codec_mgr *codec_mgr = NULL;
49
static int initialized = 0;
50
51
/* Codec instances */
52
static pjmedia_codec *codec_pcma = NULL;
53
static pjmedia_codec *codec_pcmu = NULL;
54
static pjmedia_codec *codec_g722 = NULL;
55
static pjmedia_codec *codec_gsm = NULL;
56
static pjmedia_codec *codec_speex = NULL;
57
static pjmedia_codec *codec_ilbc = NULL;
58
static pjmedia_codec *codec_l16_8k = NULL;
59
static pjmedia_codec *codec_l16_16k = NULL;
60
static pjmedia_codec *codec_l16_48k = NULL;
61
#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC != 0)
62
static pjmedia_codec *codec_opus = NULL;
63
#endif
64
65
/* Codec configurations array */
66
static codec_config_t codec_configs[] = {
67
    {&codec_pcma,     "G.711 A-Law", 80,  160,  1},
68
    {&codec_pcmu,     "G.711 U-Law", 80,  160,  1},
69
    {&codec_g722,     "G.722",       80,  320,  1},
70
    {&codec_gsm,      "GSM",         33,  320,  1},
71
    {&codec_speex,    "Speex",       10,  320,  1},
72
    {&codec_ilbc,     "iLBC",        38,  480,  1},
73
    {&codec_l16_8k,   "L16 8kHz",    320, 320,  1},
74
    {&codec_l16_16k,  "L16 16kHz",   640, 640,  1},
75
    {&codec_l16_48k,  "L16 48kHz",   1920, 1920, 1},
76
#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC != 0)
77
    /* Opus stereo @ 48 kHz: 20 ms frame = 960 samples * 2 ch * 2 bytes = 3840 */
78
    {&codec_opus,     "Opus",        10,  3840, 2},
79
#endif
80
};
81
82
10.5k
#define NUM_CODECS (sizeof(codec_configs) / sizeof(codec_configs[0]))
83
84
/* Codec ID strings for initialization */
85
static const char* codec_id_strings[] = {
86
    "PCMA/8000/1",
87
    "PCMU/8000/1",
88
    "G722/16000/1",
89
    "GSM/8000/1",
90
    "speex/8000/1",
91
    "iLBC/8000/1",
92
    "L16/8000/1",
93
    "L16/16000/1",
94
    "L16/48000/1",
95
#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC != 0)
96
    "opus/48000/2"
97
#endif
98
};
99
100
/* Clock rates for each codec (must match codec_id_strings order) */
101
static const unsigned codec_clock_rates[] = {
102
    8000,   /* PCMA */
103
    8000,   /* PCMU */
104
    16000,  /* G.722 */
105
    8000,   /* GSM */
106
    8000,   /* Speex */
107
    8000,   /* iLBC */
108
    8000,   /* L16 8kHz */
109
    16000,  /* L16 16kHz */
110
    48000,  /* L16 48kHz */
111
#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC != 0)
112
    48000   /* Opus */
113
#endif
114
};
115
116
/* Helper function to allocate and open a codec */
117
static void alloc_codec(const char *id_str, pjmedia_codec **codec_ptr)
118
10
{
119
10
    pj_status_t status;
120
10
    pjmedia_codec_param param;
121
10
    const pjmedia_codec_info *codec_info;
122
10
    unsigned count = 1;
123
10
    pj_str_t codec_id = pj_str((char*)id_str);
124
125
10
    status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, &codec_id,
126
10
                                                  &count, &codec_info, NULL);
127
10
    if (status == PJ_SUCCESS && count > 0) {
128
7
        status = pjmedia_codec_mgr_get_default_param(codec_mgr, codec_info, &param);
129
7
        if (status == PJ_SUCCESS) {
130
7
            param.setting.plc = 1;
131
7
            status = pjmedia_codec_mgr_alloc_codec(codec_mgr, codec_info, codec_ptr);
132
7
            if (status == PJ_SUCCESS && *codec_ptr) {
133
7
                pjmedia_codec_open(*codec_ptr, &param);
134
7
            }
135
7
        }
136
7
    }
137
10
}
138
139
static int init_codecs(void)
140
1.48k
{
141
1.48k
    pj_status_t status;
142
1.48k
    size_t i;
143
144
1.48k
    if (initialized) {
145
1.48k
        return 0;
146
1.48k
    }
147
148
    /* Initialize PJLIB */
149
1
    status = pj_init();
150
1
    if (status != PJ_SUCCESS) {
151
0
        return -1;
152
0
    }
153
154
1
    pj_log_set_level(0);
155
156
    /* Initialize caching pool */
157
1
    pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
158
159
    /* Create memory pool */
160
1
    pool = pj_pool_create(&caching_pool.factory, "fuzzer", 4000, 4000, NULL);
161
1
    if (!pool) {
162
0
        return -1;
163
0
    }
164
165
    /* Create media endpoint */
166
1
    status = pjmedia_endpt_create(&caching_pool.factory, NULL, 1, &endpt);
167
1
    if (status != PJ_SUCCESS) {
168
0
        return -1;
169
0
    }
170
171
    /* Get codec manager */
172
1
    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
173
1
    if (!codec_mgr) {
174
0
        return -1;
175
0
    }
176
177
    /* Register all codecs */
178
1
    pjmedia_codec_g711_init(endpt);
179
1
    pjmedia_codec_g722_init(endpt);
180
1
    pjmedia_codec_gsm_init(endpt);
181
1
    pjmedia_codec_speex_init(endpt, 0, -1, -1);
182
1
    pjmedia_codec_ilbc_init(endpt, 30);
183
1
    pjmedia_codec_l16_init(endpt, 0);
184
1
#if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC != 0)
185
1
    pjmedia_codec_opus_init(endpt);
186
187
    /* The Opus factory is registered as stereo (per RFC 7587: clock rate
188
     * 48000, 2 channels), but its default codec param is mono since
189
     * opus_cfg.channel_cnt defaults to 1. Without overriding it here,
190
     * alloc_codec() would open Opus as mono via the default param, which
191
     * contradicts the "opus/48000/2" id and the 3840-byte (20 ms @ 48 kHz
192
     * stereo) PCM frame size, leaving the stereo encode/decode paths
193
     * unexercised. Configure 2 channels so Opus actually opens in stereo.
194
     *
195
     * The stereo override is only applied when every query/config step
196
     * succeeds. If any step fails, Opus stays at its factory (mono) default,
197
     * so we downgrade the harness Opus channel count and PCM frame size to
198
     * match how the codec will be opened (while still looking it up by the
199
     * registered "opus/48000/2" ID).
200
     */
201
1
    {
202
1
        pjmedia_codec_param opus_param;
203
1
        pjmedia_codec_opus_config opus_cfg;
204
1
        const pjmedia_codec_info *opus_info;
205
1
        unsigned opus_cnt = 1;
206
1
        pj_str_t opus_id = pj_str("opus/48000/2");
207
1
        pj_bool_t stereo_ok = PJ_FALSE;
208
        /* Opus is the last entry in codec_configs[] (see the array above). */
209
1
        codec_config_t *opus_config = &codec_configs[NUM_CODECS - 1];
210
211
1
        if (pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, &opus_id, &opus_cnt,
212
1
                                                &opus_info, NULL) == PJ_SUCCESS &&
213
1
            opus_cnt > 0 &&
214
1
            pjmedia_codec_mgr_get_default_param(codec_mgr, opus_info,
215
1
                                                &opus_param) == PJ_SUCCESS &&
216
1
            pjmedia_codec_opus_get_config(&opus_cfg) == PJ_SUCCESS)
217
1
        {
218
1
            opus_cfg.channel_cnt = 2;
219
1
            if (pjmedia_codec_opus_set_default_param(&opus_cfg, &opus_param)
220
1
                    == PJ_SUCCESS)
221
1
            {
222
1
                stereo_ok = PJ_TRUE;
223
1
            }
224
1
        }
225
226
1
        if (!stereo_ok) {
227
            /* Mono fallback: 20 ms @ 48 kHz mono = 960 samples * 2 bytes. */
228
0
            opus_config->channel_cnt = 1;
229
0
            opus_config->pcm_frame_size = 1920;
230
0
        }
231
1
    }
232
1
#endif
233
234
    /* Allocate all codecs using the configuration array */
235
11
    for (i = 0; i < NUM_CODECS; i++) {
236
10
        alloc_codec(codec_id_strings[i], codec_configs[i].codec_ptr);
237
10
    }
238
239
1
    initialized = 1;
240
1
    return 0;
241
1
}
242
243
/* Helper function to test codec encode/decode cycle */
244
static void test_codec_cycle(pjmedia_codec *codec, const uint8_t *Data, size_t Size,
245
                              size_t min_frame_size, size_t pcm_frame_size,
246
                              unsigned clock_rate, unsigned channel_cnt)
247
5.76k
{
248
5.76k
    pj_status_t status;
249
5.76k
    pjmedia_frame input_frame, output_frame;
250
5.76k
    pj_int16_t pcm_buffer[4096] = {0};
251
5.76k
    pj_uint8_t encoded_buffer[2048];
252
5.76k
    pjmedia_codec_param param;
253
254
5.76k
    if (!codec || Size < min_frame_size) return;
255
256
    /* Initialise pjmedia_frame */
257
5.76k
    pj_bzero(&input_frame, sizeof(input_frame));
258
5.76k
    pj_bzero(&output_frame, sizeof(output_frame));
259
260
    /* Test runtime parameter modification (tests VAD toggling, keep PLC enabled) */
261
5.76k
    if (Size >= min_frame_size + 2) {
262
5.06k
        pj_bzero(&param, sizeof(param));
263
5.06k
        param.info.avg_bps = 128000;
264
5.06k
        param.info.clock_rate = clock_rate;
265
        /* Use the codec's actual channel count (e.g. 2 for Opus) rather than
266
         * forcing mono, so the modify path does not contradict how the codec
267
         * was opened and the stereo paths stay exercised. */
268
5.06k
        param.info.channel_cnt = channel_cnt;
269
5.06k
        param.info.frm_ptime = 20;
270
5.06k
        param.info.pcm_bits_per_sample = 16;
271
5.06k
        param.setting.vad = (Data[min_frame_size] & 0x01) ? 1 : 0;
272
5.06k
        param.setting.plc = 1;
273
5.06k
        if (codec->op->modify) {
274
5.06k
            pjmedia_codec_modify(codec, &param);
275
5.06k
        }
276
5.06k
    }
277
278
    /* Test decode */
279
5.76k
    input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
280
5.76k
    input_frame.buf = (void*)Data;
281
5.76k
    input_frame.size = min_frame_size;
282
5.76k
    input_frame.timestamp.u64 = 0;
283
284
5.76k
    output_frame.buf = pcm_buffer;
285
5.76k
    output_frame.size = sizeof(pcm_buffer);
286
287
5.76k
    status = pjmedia_codec_decode(codec, &input_frame, sizeof(pcm_buffer), &output_frame);
288
289
    /* If decode succeeded, try to encode the output back */
290
5.76k
    if (status == PJ_SUCCESS && output_frame.type == PJMEDIA_FRAME_TYPE_AUDIO) {
291
4.70k
        input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
292
4.70k
        input_frame.buf = output_frame.buf;
293
4.70k
        input_frame.size = output_frame.size;
294
4.70k
        input_frame.timestamp.u64 = 0;
295
296
4.70k
        output_frame.buf = encoded_buffer;
297
4.70k
        output_frame.size = sizeof(encoded_buffer);
298
299
4.70k
        pjmedia_codec_encode(codec, &input_frame, sizeof(encoded_buffer), &output_frame);
300
4.70k
    }
301
302
    /* Test encode with fuzzer input as PCM - use proper frame size */
303
5.76k
    if (Size >= pcm_frame_size && pcm_frame_size <= sizeof(pcm_buffer)) {
304
3.05k
        memcpy(pcm_buffer, Data, pcm_frame_size);
305
306
3.05k
        input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
307
3.05k
        input_frame.buf = pcm_buffer;
308
3.05k
        input_frame.size = pcm_frame_size;
309
3.05k
        input_frame.timestamp.u64 = 0;
310
311
3.05k
        output_frame.buf = encoded_buffer;
312
3.05k
        output_frame.size = sizeof(encoded_buffer);
313
314
3.05k
        pjmedia_codec_encode(codec, &input_frame, sizeof(encoded_buffer), &output_frame);
315
3.05k
    }
316
317
    /* Test parse */
318
5.76k
    if (codec->op->parse) {
319
5.76k
        pjmedia_frame frames[256];
320
5.76k
        unsigned frame_cnt = 256;
321
322
5.76k
        input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
323
5.76k
        input_frame.buf = (void*)Data;
324
5.76k
        input_frame.size = Size;
325
5.76k
        input_frame.timestamp.u64 = 0;
326
327
5.76k
        status = pjmedia_codec_parse(codec, (void*)Data, Size, &input_frame.timestamp,
328
5.76k
                                      &frame_cnt, frames);
329
5.76k
        (void)status;
330
5.76k
    }
331
332
    /* Test PLC if function exists */
333
5.76k
    if (codec->op->recover) {
334
5.76k
        output_frame.buf = pcm_buffer;
335
5.76k
        output_frame.size = sizeof(pcm_buffer);
336
337
        /* Wrap in error handling to avoid asserts in codecs with incomplete PLC */
338
5.76k
        status = pjmedia_codec_recover(codec, sizeof(pcm_buffer), &output_frame);
339
5.76k
        if (status == PJ_SUCCESS) {
340
5.44k
            output_frame.buf = pcm_buffer;
341
5.44k
            output_frame.size = sizeof(pcm_buffer);
342
5.44k
            pjmedia_codec_recover(codec, sizeof(pcm_buffer), &output_frame);
343
5.44k
        }
344
5.76k
    }
345
5.76k
}
346
347
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
348
1.48k
{
349
1.48k
    const uint8_t *current_data;
350
1.48k
    size_t remaining_size;
351
1.48k
    size_t i;
352
1.48k
    codec_config_t *config;
353
1.48k
    pjmedia_codec *codec;
354
1.48k
    size_t chunk_size;
355
1.48k
    size_t consumed;
356
1.48k
    pj_int16_t pcm_val;
357
1.48k
    pj_uint8_t alaw;
358
1.48k
    pj_uint8_t ulaw;
359
360
    /* Initialize codecs on first run */
361
1.48k
    if (init_codecs() != 0) {
362
0
        return 0;
363
0
    }
364
365
1.48k
    current_data = Data;
366
1.48k
    remaining_size = Size;
367
368
    /* Test each codec sequentially with non-overlapping chunks of data */
369
10.5k
    for (i = 0; i < NUM_CODECS; i++) {
370
9.95k
        config = &codec_configs[i];
371
9.95k
        codec = *(config->codec_ptr);
372
373
        /* Skip if codec not available or not enough data */
374
9.95k
        if (!codec || remaining_size < config->min_frame_size) {
375
4.19k
            continue;
376
4.19k
        }
377
378
        /* Calculate chunk size for this codec (at least min_frame_size, max pcm_frame_size) */
379
5.76k
        chunk_size = config->pcm_frame_size;
380
5.76k
        if (chunk_size > remaining_size) {
381
2.70k
            chunk_size = remaining_size;
382
2.70k
        }
383
384
        /* Test this codec with current data chunk */
385
5.76k
        test_codec_cycle(codec, current_data, chunk_size,
386
5.76k
                        config->min_frame_size, config->pcm_frame_size,
387
5.76k
                        codec_clock_rates[i], config->channel_cnt);
388
389
        /* Advance to next chunk of data */
390
5.76k
        consumed = config->min_frame_size;
391
5.76k
        if (consumed > remaining_size) {
392
0
            consumed = remaining_size;
393
0
        }
394
5.76k
        current_data += consumed;
395
5.76k
        remaining_size -= consumed;
396
397
        /* Stop if we've run out of data */
398
5.76k
        if (remaining_size < 10) {
399
891
            break;
400
891
        }
401
5.76k
    }
402
403
    /* Test low-level G.711 conversion functions with remaining data */
404
1.48k
    if (remaining_size > 1) {
405
58.2k
        for (i = 0; i + 1 < remaining_size && i < 256; i += 2) {
406
57.4k
            pcm_val = (pj_int16_t)((current_data[i] << 8) | current_data[i+1]);
407
408
57.4k
            alaw = pjmedia_linear2alaw(pcm_val);
409
57.4k
            ulaw = pjmedia_linear2ulaw(pcm_val);
410
57.4k
            pcm_val = pjmedia_alaw2linear(alaw);
411
57.4k
            pcm_val = pjmedia_ulaw2linear(ulaw);
412
57.4k
            ulaw = pjmedia_alaw2ulaw(current_data[i]);
413
57.4k
            alaw = pjmedia_ulaw2alaw(current_data[i]);
414
57.4k
        }
415
785
    }
416
417
1.48k
    return 0;
418
1.48k
}