Coverage Report

Created: 2026-02-26 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pjsip/tests/fuzz/fuzz-rtp.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
22
#include <pjlib.h>
23
#include <pjmedia.h>
24
#include <pjmedia/rtp.h>
25
#include <pjmedia/jbuf.h>
26
27
/*
28
 * Minimum input length calculation:
29
 *   - Bytes 0-4: Session config (PT + SSRC)
30
 *   - Bytes 5-16: First RTP packet for decode_rtp test (12 bytes)
31
 *   - Bytes 17-28: Second RTP packet for decode_rtp2 test (12 bytes)
32
 *   - Bytes 29-35: Encode test parameters (7 bytes)
33
 *   - Bytes 36+: Multi-packet simulation
34
 *
35
 * Minimum: 5 config + 12 decode1 + 12 decode2 + 7 encode = 36 bytes
36
 */
37
22.9k
#define kMinInputLength 36
38
11.3k
#define kMaxInputLength 5120
39
40
/* For RTP session testing with randomized memory pool */
41
pj_pool_factory *mem;
42
43
/* Fixed Input byte offsets */
44
782
#define OFFSET_PT           0   /* Byte 0: Payload Type (7 bits) */
45
3.12k
#define OFFSET_SSRC         1   /* Bytes 1-4: SSRC (32 bits) */
46
2.34k
#define OFFSET_DECODE1      5   /* Bytes 5-16: First packet for decode_rtp test (12 bytes) */
47
2.34k
#define OFFSET_DECODE2      17  /* Bytes 17-28: Second packet for decode_rtp2 test (12 bytes) */
48
6.25k
#define OFFSET_ENCODE       29  /* Bytes 29-35: Encode parameters (7 bytes) */
49
1.56k
#define OFFSET_MULTI_PKT    36  /* Bytes 36+: Multiple packet simulation */
50
51
/*
52
 * Extract RTP session configuration from fuzzer input
53
 */
54
static void setup_rtp_session_config(const uint8_t *data,
55
                                     uint8_t *pt,
56
                                     uint32_t *ssrc)
57
782
{
58
    /* Payload Type: 7 bits (0-127) */
59
782
    *pt = data[OFFSET_PT] & 0x7F;
60
61
    /* SSRC: 32-bit synchronization source identifier */
62
782
    *ssrc = ((uint32_t)data[OFFSET_SSRC] << 24) |
63
782
            ((uint32_t)data[OFFSET_SSRC + 1] << 16) |
64
782
            ((uint32_t)data[OFFSET_SSRC + 2] << 8) |
65
782
            ((uint32_t)data[OFFSET_SSRC + 3]);
66
782
}
67
68
/*
69
 * Test basic RTP packet decoding (pjmedia_rtp_decode_rtp)
70
 * Tests: header parsing, payload extraction, session state updates
71
 */
72
static void test_decode_basic(pjmedia_rtp_session *session,
73
                              const uint8_t *data, size_t size)
74
782
{
75
782
    pj_status_t status;
76
782
    const pjmedia_rtp_hdr *hdr;
77
782
    const void *payload;
78
782
    unsigned payloadlen;
79
782
    pjmedia_rtp_status seq_st;
80
81
782
    if (size < OFFSET_DECODE1 + 12)
82
0
        return;
83
84
782
    status = pjmedia_rtp_decode_rtp(session, data + OFFSET_DECODE1,
85
782
                                    (int)(size - OFFSET_DECODE1),
86
782
                                    &hdr, &payload, &payloadlen);
87
88
782
    if (status == PJ_SUCCESS && hdr != NULL) {
89
        /* Update session state - tests sequence number tracking */
90
432
        pjmedia_rtp_session_update(session, hdr, &seq_st);
91
92
        /* Test update with check_pt disabled */
93
432
        pjmedia_rtp_session_update2(session, hdr, NULL, PJ_FALSE);
94
432
    }
95
782
}
96
97
/*
98
 * Test extended RTP packet decoding (pjmedia_rtp_decode_rtp2)
99
 * Tests: extension header parsing, detailed header info extraction
100
 */
101
static void test_decode_extended(pjmedia_rtp_session *session,
102
                                 const uint8_t *data, size_t size)
103
782
{
104
782
    pj_status_t status;
105
782
    pjmedia_rtp_dec_hdr dec_hdr;
106
782
    const pjmedia_rtp_hdr *hdr;
107
782
    const void *payload;
108
782
    unsigned payloadlen;
109
782
    pjmedia_rtp_status seq_st;
110
111
782
    if (size < OFFSET_DECODE2 + 12)
112
0
        return;
113
114
782
    status = pjmedia_rtp_decode_rtp2(session, data + OFFSET_DECODE2,
115
782
                                     (int)(size - OFFSET_DECODE2),
116
782
                                     &hdr, &dec_hdr, &payload, &payloadlen);
117
118
782
    if (status == PJ_SUCCESS && hdr != NULL) {
119
380
        pjmedia_rtp_session_update(session, hdr, &seq_st);
120
121
        /* Access extension header if present */
122
380
        if (dec_hdr.ext_hdr && dec_hdr.ext && dec_hdr.ext_len > 0) {
123
39
            pj_ntohs(dec_hdr.ext_hdr->profile_data);
124
39
            pj_ntohs(dec_hdr.ext_hdr->length);
125
39
        }
126
380
    }
127
782
}
128
129
/*
130
 * Test RTP packet encoding (pjmedia_rtp_encode_rtp)
131
 * Tests: header generation, roundtrip encode->decode
132
 */
133
static void test_encode_roundtrip(pjmedia_rtp_session *session,
134
                                  const uint8_t *data, size_t size)
135
782
{
136
782
    pj_status_t status;
137
782
    const void *rtphdr = NULL;
138
782
    int hdrlen = 0;
139
782
    const pjmedia_rtp_hdr *hdr;
140
782
    const void *payload;
141
782
    unsigned payloadlen;
142
143
782
    if (size < OFFSET_ENCODE + 7)
144
0
        return;
145
146
    /* Extract encode parameters from fuzzer input */
147
782
    pj_bool_t marker = (data[OFFSET_ENCODE] & 0x80) ? PJ_TRUE : PJ_FALSE;
148
782
    int pt_encode = data[OFFSET_ENCODE + 1] & 0x7F;
149
782
    int payload_len = data[OFFSET_ENCODE + 2];
150
782
    uint32_t ts = ((uint32_t)data[OFFSET_ENCODE + 3] << 24) |
151
782
                  ((uint32_t)data[OFFSET_ENCODE + 4] << 16) |
152
782
                  ((uint32_t)data[OFFSET_ENCODE + 5] << 8) |
153
782
                  ((uint32_t)data[OFFSET_ENCODE + 6]);
154
155
    /* Encode RTP header */
156
782
    status = pjmedia_rtp_encode_rtp(session, pt_encode, marker,
157
782
                                    payload_len, ts, &rtphdr, &hdrlen);
158
159
782
    if (status == PJ_SUCCESS && rtphdr != NULL && hdrlen > 0) {
160
        /* Roundtrip test: decode what we just encoded */
161
583
        pjmedia_rtp_status seq_st;
162
583
        status = pjmedia_rtp_decode_rtp(session, rtphdr, hdrlen,
163
583
                                        &hdr, &payload, &payloadlen);
164
583
        if (status == PJ_SUCCESS && hdr != NULL) {
165
583
            pjmedia_rtp_session_update(session, hdr, &seq_st);
166
583
        }
167
583
    }
168
782
}
169
170
/*
171
 * Test multiple packet simulation with jitter buffer
172
 * Tests: sequence number handling, packet loss detection, jitter buffer reordering
173
 */
174
static void test_multiple_packets(pjmedia_rtp_session *session,
175
                                  const uint8_t *data, size_t size,
176
                                  pj_pool_t *pool)
177
782
{
178
782
    pj_status_t status;
179
782
    const pjmedia_rtp_hdr *hdr;
180
782
    const void *payload;
181
782
    unsigned payloadlen;
182
782
    pjmedia_rtp_status seq_st;
183
782
    size_t offset = OFFSET_MULTI_PKT;
184
782
    int packet_num = 0;
185
782
    pjmedia_jbuf *jb = NULL;
186
187
782
    if (size <= OFFSET_MULTI_PKT)
188
204
        return;
189
190
    /* Jitter buffer configuration */
191
578
    unsigned frame_size = 160;
192
578
    unsigned ptime = 20;
193
578
    unsigned max_count = 50;
194
578
    unsigned prefetch = 10;
195
196
    /* Create jitter buffer with fixed, realistic parameters */
197
578
    pj_str_t jb_name = pj_str("fuzz-jb");
198
578
    status = pjmedia_jbuf_create(pool, &jb_name, frame_size, ptime, max_count, &jb);
199
578
    if (status != PJ_SUCCESS) {
200
0
        return;
201
0
    }
202
203
    /* Configure jitter buffer prefetch (initial delay) using adaptive mode */
204
578
    pjmedia_jbuf_set_adaptive(jb, prefetch, 5, max_count);
205
206
    /* Process up to 16 packets to test more complex scenarios */
207
3.67k
    while (offset + 1 + 12 < size && packet_num < 16) {
208
        /* First byte: packet size (12-200 bytes) */
209
3.18k
        uint8_t pkt_size = data[offset];
210
3.18k
        if (pkt_size < 12) pkt_size = 12;
211
3.18k
        if (pkt_size > 200) pkt_size = 200;
212
213
3.18k
        offset++;
214
215
3.18k
        if (offset + pkt_size > size)
216
85
            break;
217
218
        /* Decode packet */
219
3.10k
        status = pjmedia_rtp_decode_rtp(session, data + offset, pkt_size,
220
3.10k
                                        &hdr, &payload, &payloadlen);
221
3.10k
        if (status == PJ_SUCCESS && hdr != NULL) {
222
            /* Update RTP session state */
223
1.79k
            pjmedia_rtp_session_update(session, hdr, &seq_st);
224
225
            /* Test different jitter buffer operations based on fuzzer input */
226
1.79k
            int seq = pj_ntohs(hdr->seq);
227
228
            /* Use packet number to decide which JB operation to test */
229
1.79k
            if (payloadlen > 0 && (packet_num & 1)) {
230
                /* Put frame using basic API */
231
541
                pjmedia_jbuf_put_frame(jb, payload, payloadlen, seq);
232
1.25k
            } else if (payloadlen > 0) {
233
                /* Put frame using extended API with bit_info */
234
661
                pj_uint32_t bit_info = payloadlen * 8;
235
661
                pj_bool_t discarded = PJ_FALSE;
236
661
                pjmedia_jbuf_put_frame2(jb, payload, payloadlen, bit_info, seq, &discarded);
237
661
            }
238
239
            /* Periodically test jitter buffer reset */
240
1.79k
            if (packet_num == 5) {
241
89
                pjmedia_jbuf_reset(jb);
242
89
            }
243
244
            /* Test jitter buffer state query */
245
1.79k
            if (packet_num % 4 == 0) {
246
646
                pjmedia_jb_state jb_state;
247
646
                pjmedia_jbuf_get_state(jb, &jb_state);
248
646
            }
249
250
            /* Exercise jitter buffer GET operations to drive dequeue path */
251
1.79k
            if (packet_num > prefetch && packet_num % 3 == 0) {
252
111
                pjmedia_jb_state jb_state;
253
111
                pjmedia_jbuf_get_state(jb, &jb_state);
254
255
                /* Only call GET if there are frames in the buffer */
256
111
                if (jb_state.size > 0) {
257
91
                    char frame_type;
258
91
                    pj_size_t frame_size_out = jb_state.frame_size;
259
91
                    pj_uint32_t bit_info;
260
91
                    pj_uint32_t ts;
261
91
                    int seq_out;
262
91
                    char out_buf[512];
263
264
                    /* Test get_frame3 (most comprehensive API) */
265
91
                    pjmedia_jbuf_get_frame3(jb, out_buf, &frame_size_out,
266
91
                                            &frame_type, &bit_info,
267
91
                                            &ts, &seq_out);
268
91
                }
269
111
            }
270
1.79k
        }
271
272
3.10k
        offset += pkt_size;
273
3.10k
        packet_num++;
274
3.10k
    }
275
276
578
    pjmedia_jbuf_destroy(jb);
277
578
}
278
279
/*
280
 * Main RTP fuzzing test function
281
 */
282
void rtp_test(const uint8_t *data, size_t size)
283
782
{
284
782
    pj_status_t status;
285
782
    pj_pool_t *pool;
286
782
    pjmedia_rtp_session session;
287
782
    uint8_t pt;
288
782
    uint32_t ssrc;
289
290
    /* Extract session configuration from fuzzer input */
291
782
    setup_rtp_session_config(data, &pt, &ssrc);
292
293
    /* Create memory pool */
294
782
    pool = pj_pool_create(mem, "rtp_test", 4000, 4000, NULL);
295
782
    if (!pool)
296
0
        return;
297
298
    /* Initialize RTP session with fuzzer-controlled PT and SSRC */
299
782
    status = pjmedia_rtp_session_init(&session, pt, ssrc);
300
782
    if (status != PJ_SUCCESS) {
301
0
        pj_pool_release(pool);
302
0
        return;
303
0
    }
304
305
    /* Test basic decoding */
306
782
    test_decode_basic(&session, data, size);
307
308
    /* Test extended decoding with header extensions */
309
782
    test_decode_extended(&session, data, size);
310
311
    /* Test encode path and roundtrip */
312
782
    test_encode_roundtrip(&session, data, size);
313
314
    /* Test multiple packet scenarios with jitter buffer */
315
782
    test_multiple_packets(&session, data, size, pool);
316
317
782
    pj_pool_release(pool);
318
782
}
319
320
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
321
11.4k
{
322
11.4k
    pj_caching_pool caching_pool;
323
324
11.4k
    if (Size < kMinInputLength || Size > kMaxInputLength) {
325
365
        return 1;
326
365
    }
327
328
    /* Init */
329
11.1k
    pj_init();
330
11.1k
    pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
331
11.1k
    pj_log_set_level(0);
332
333
    /* Configure the global blocking pool */
334
11.1k
    mem = &caching_pool.factory;
335
336
    /* Fuzz RTP */
337
11.1k
    rtp_test(Data, Size);
338
339
    /* Cleanup */
340
11.1k
    pj_caching_pool_destroy(&caching_pool);
341
342
11.1k
    return 0;
343
11.4k
}