Coverage Report

Created: 2026-03-12 06:59

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
32
/* Codec configuration structure */
33
typedef struct {
34
    pjmedia_codec **codec_ptr;
35
    const char *name;
36
    size_t min_frame_size;
37
    size_t pcm_frame_size;
38
} codec_config_t;
39
40
/* Global state for persistent fuzzing */
41
static pj_caching_pool caching_pool;
42
static pjmedia_endpt *endpt = NULL;
43
static pj_pool_t *pool = NULL;
44
static pjmedia_codec_mgr *codec_mgr = NULL;
45
static int initialized = 0;
46
47
/* Codec instances */
48
static pjmedia_codec *codec_pcma = NULL;
49
static pjmedia_codec *codec_pcmu = NULL;
50
static pjmedia_codec *codec_g722 = NULL;
51
static pjmedia_codec *codec_gsm = NULL;
52
static pjmedia_codec *codec_speex = NULL;
53
static pjmedia_codec *codec_ilbc = NULL;
54
static pjmedia_codec *codec_l16_8k = NULL;
55
static pjmedia_codec *codec_l16_16k = NULL;
56
57
/* Codec configurations array */
58
static codec_config_t codec_configs[] = {
59
    {&codec_pcma,     "G.711 A-Law", 80,  160},
60
    {&codec_pcmu,     "G.711 U-Law", 80,  160},
61
    {&codec_g722,     "G.722",       80,  320},
62
    {&codec_gsm,      "GSM",         33,  320},
63
    {&codec_speex,    "Speex",       10,  320},
64
    {&codec_ilbc,     "iLBC",        38,  480},
65
    {&codec_l16_8k,   "L16 8kHz",    320, 320},
66
    {&codec_l16_16k,  "L16 16kHz",   640, 640},
67
};
68
69
0
#define NUM_CODECS (sizeof(codec_configs) / sizeof(codec_configs[0]))
70
71
/* Codec ID strings for initialization */
72
static const char* codec_id_strings[] = {
73
    "PCMA/8000/1",
74
    "PCMU/8000/1",
75
    "G722/16000/1",
76
    "GSM/8000/1",
77
    "speex/8000/1",
78
    "iLBC/8000/1",
79
    "L16/8000/1",
80
    "L16/16000/1"
81
};
82
83
/* Helper function to allocate and open a codec */
84
static void alloc_codec(const char *id_str, pjmedia_codec **codec_ptr)
85
0
{
86
0
    pj_status_t status;
87
0
    pjmedia_codec_param param;
88
0
    const pjmedia_codec_info *codec_info;
89
0
    unsigned count = 1;
90
0
    pj_str_t codec_id = pj_str((char*)id_str);
91
92
0
    status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, &codec_id,
93
0
                                                  &count, &codec_info, NULL);
94
0
    if (status == PJ_SUCCESS && count > 0) {
95
0
        status = pjmedia_codec_mgr_get_default_param(codec_mgr, codec_info, &param);
96
0
        if (status == PJ_SUCCESS) {
97
0
            status = pjmedia_codec_mgr_alloc_codec(codec_mgr, codec_info, codec_ptr);
98
0
            if (status == PJ_SUCCESS && *codec_ptr) {
99
0
                pjmedia_codec_open(*codec_ptr, &param);
100
0
            }
101
0
        }
102
0
    }
103
0
}
104
105
static int init_codecs(void)
106
0
{
107
0
    pj_status_t status;
108
0
    size_t i;
109
110
0
    if (initialized) {
111
0
        return 0;
112
0
    }
113
114
    /* Initialize PJLIB */
115
0
    status = pj_init();
116
0
    if (status != PJ_SUCCESS) {
117
0
        return -1;
118
0
    }
119
120
0
    pj_log_set_level(0);
121
122
    /* Initialize caching pool */
123
0
    pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
124
125
    /* Create memory pool */
126
0
    pool = pj_pool_create(&caching_pool.factory, "fuzzer", 4000, 4000, NULL);
127
0
    if (!pool) {
128
0
        return -1;
129
0
    }
130
131
    /* Create media endpoint */
132
0
    status = pjmedia_endpt_create(&caching_pool.factory, NULL, 1, &endpt);
133
0
    if (status != PJ_SUCCESS) {
134
0
        return -1;
135
0
    }
136
137
    /* Get codec manager */
138
0
    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
139
0
    if (!codec_mgr) {
140
0
        return -1;
141
0
    }
142
143
    /* Register all codecs */
144
0
    pjmedia_codec_g711_init(endpt);
145
0
    pjmedia_codec_g722_init(endpt);
146
0
    pjmedia_codec_gsm_init(endpt);
147
0
    pjmedia_codec_speex_init(endpt, 0, -1, -1);
148
0
    pjmedia_codec_ilbc_init(endpt, 30);
149
0
    pjmedia_codec_l16_init(endpt, 0);
150
151
    /* Allocate all codecs using the configuration array */
152
0
    for (i = 0; i < NUM_CODECS; i++) {
153
0
        alloc_codec(codec_id_strings[i], codec_configs[i].codec_ptr);
154
0
    }
155
156
0
    initialized = 1;
157
0
    return 0;
158
0
}
159
160
/* Helper function to test codec encode/decode cycle */
161
static void test_codec_cycle(pjmedia_codec *codec, const uint8_t *Data, size_t Size,
162
                              size_t min_frame_size, size_t pcm_frame_size)
163
0
{
164
0
    pj_status_t status;
165
0
    pjmedia_frame input_frame, output_frame;
166
0
    pj_int16_t pcm_buffer[4096] = {0};
167
0
    pj_uint8_t encoded_buffer[2048];
168
169
0
    if (!codec || Size < min_frame_size) return;
170
171
    /* Initialise pjmedia_frame */
172
0
    pj_bzero(&input_frame, sizeof(input_frame));
173
0
    pj_bzero(&output_frame, sizeof(output_frame));
174
175
    /* Test decode */
176
0
    input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
177
0
    input_frame.buf = (void*)Data;
178
0
    input_frame.size = min_frame_size;
179
0
    input_frame.timestamp.u64 = 0;
180
181
0
    output_frame.buf = pcm_buffer;
182
0
    output_frame.size = sizeof(pcm_buffer);
183
184
0
    status = pjmedia_codec_decode(codec, &input_frame, sizeof(pcm_buffer), &output_frame);
185
186
    /* If decode succeeded, try to encode the output back */
187
0
    if (status == PJ_SUCCESS && output_frame.type == PJMEDIA_FRAME_TYPE_AUDIO) {
188
0
        input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
189
0
        input_frame.buf = output_frame.buf;
190
0
        input_frame.size = output_frame.size;
191
0
        input_frame.timestamp.u64 = 0;
192
193
0
        output_frame.buf = encoded_buffer;
194
0
        output_frame.size = sizeof(encoded_buffer);
195
196
0
        pjmedia_codec_encode(codec, &input_frame, sizeof(encoded_buffer), &output_frame);
197
0
    }
198
199
    /* Test encode with fuzzer input as PCM - use proper frame size */
200
0
    if (Size >= pcm_frame_size && pcm_frame_size <= sizeof(pcm_buffer)) {
201
0
        memcpy(pcm_buffer, Data, pcm_frame_size);
202
203
0
        input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
204
0
        input_frame.buf = pcm_buffer;
205
0
        input_frame.size = pcm_frame_size;
206
0
        input_frame.timestamp.u64 = 0;
207
208
0
        output_frame.buf = encoded_buffer;
209
0
        output_frame.size = sizeof(encoded_buffer);
210
211
0
        pjmedia_codec_encode(codec, &input_frame, sizeof(encoded_buffer), &output_frame);
212
0
    }
213
214
    /* Test parse */
215
0
    if (codec->op->parse) {
216
0
        pjmedia_frame frames[256];
217
0
        unsigned frame_cnt = 256;
218
219
0
        input_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
220
0
        input_frame.buf = (void*)Data;
221
0
        input_frame.size = Size;
222
0
        input_frame.timestamp.u64 = 0;
223
224
0
        status = pjmedia_codec_parse(codec, (void*)Data, Size, &input_frame.timestamp,
225
0
                                      &frame_cnt, frames);
226
0
        (void)status;
227
0
    }
228
229
    /* Test PLC if supported */
230
0
    if (codec->op->recover) {
231
0
        output_frame.buf = pcm_buffer;
232
0
        output_frame.size = sizeof(pcm_buffer);
233
234
0
        pjmedia_codec_recover(codec, sizeof(pcm_buffer), &output_frame);
235
0
    }
236
0
}
237
238
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
239
{
240
    const uint8_t *current_data;
241
    size_t remaining_size;
242
    size_t i;
243
    codec_config_t *config;
244
    pjmedia_codec *codec;
245
    size_t chunk_size;
246
    size_t consumed;
247
    pj_int16_t pcm_val;
248
    pj_uint8_t alaw;
249
    pj_uint8_t ulaw;
250
251
    /* Initialize codecs on first run */
252
    if (init_codecs() != 0) {
253
        return 0;
254
    }
255
256
    current_data = Data;
257
    remaining_size = Size;
258
259
    /* Test each codec sequentially with non-overlapping chunks of data */
260
    for (i = 0; i < NUM_CODECS; i++) {
261
        config = &codec_configs[i];
262
        codec = *(config->codec_ptr);
263
264
        /* Skip if codec not available or not enough data */
265
        if (!codec || remaining_size < config->min_frame_size) {
266
            continue;
267
        }
268
269
        /* Calculate chunk size for this codec (at least min_frame_size, max pcm_frame_size) */
270
        chunk_size = config->pcm_frame_size;
271
        if (chunk_size > remaining_size) {
272
            chunk_size = remaining_size;
273
        }
274
275
        /* Test this codec with current data chunk */
276
        test_codec_cycle(codec, current_data, chunk_size,
277
                        config->min_frame_size, config->pcm_frame_size);
278
279
        /* Advance to next chunk of data */
280
        consumed = config->min_frame_size;
281
        if (consumed > remaining_size) {
282
            consumed = remaining_size;
283
        }
284
        current_data += consumed;
285
        remaining_size -= consumed;
286
287
        /* Stop if we've run out of data */
288
        if (remaining_size < 10) {
289
            break;
290
        }
291
    }
292
293
    /* Test low-level G.711 conversion functions with remaining data */
294
    if (remaining_size > 1) {
295
        for (i = 0; i + 1 < remaining_size && i < 256; i += 2) {
296
            pcm_val = (pj_int16_t)((current_data[i] << 8) | current_data[i+1]);
297
298
            alaw = pjmedia_linear2alaw(pcm_val);
299
            ulaw = pjmedia_linear2ulaw(pcm_val);
300
            pcm_val = pjmedia_alaw2linear(alaw);
301
            pcm_val = pjmedia_ulaw2linear(ulaw);
302
            ulaw = pjmedia_alaw2ulaw(current_data[i]);
303
            alaw = pjmedia_ulaw2alaw(current_data[i]);
304
        }
305
    }
306
307
    return 0;
308
}