Coverage Report

Created: 2026-06-13 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pjsip/tests/fuzz/fuzz-sdes.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 <pjlib-util.h>
24
#include <pjmedia.h>
25
#include <pjmedia/transport_srtp.h>
26
#include <pjmedia/transport_loop.h>
27
#include <pjmedia/sdp.h>
28
29
5.27k
#define kMinInputLength 100
30
2.62k
#define kMaxInputLength 5120
31
32
static pj_pool_factory *mem;
33
34
static void fuzz_sdes(const uint8_t *data, size_t size)
35
2.62k
{
36
2.62k
    pjmedia_endpt *endpt = NULL;
37
2.62k
    pj_pool_t *pool = NULL, *sdp_pool = NULL;
38
2.62k
    pj_status_t status;
39
2.62k
    pjmedia_transport *loop_tp = NULL, *srtp_tp = NULL;
40
2.62k
    pjmedia_transport *loop_tp2 = NULL, *srtp_tp2 = NULL;
41
2.62k
    pjmedia_srtp_setting srtp_opt;
42
2.62k
    pjmedia_sdp_session *local_sdp = NULL, *remote_sdp = NULL;
43
2.62k
    char *sdp_str = NULL;
44
45
2.62k
    status = pjmedia_endpt_create(mem, NULL, 1, &endpt);
46
2.62k
    if (status != PJ_SUCCESS)
47
0
        return;
48
49
2.62k
    status = pjmedia_srtp_init_lib(endpt);
50
2.62k
    if (status != PJ_SUCCESS)
51
0
        goto cleanup;
52
53
2.62k
    pool = pj_pool_create(mem, "sdes", 2000, 1000, NULL);
54
2.62k
    if (!pool)
55
0
        goto cleanup;
56
57
2.62k
    sdp_pool = pj_pool_create(mem, "sdp", 2000, 1000, NULL);
58
2.62k
    if (!sdp_pool)
59
0
        goto cleanup;
60
61
2.62k
    status = pjmedia_transport_loop_create(endpt, &loop_tp);
62
2.62k
    if (status != PJ_SUCCESS)
63
0
        goto cleanup;
64
65
2.62k
    pjmedia_srtp_setting_default(&srtp_opt);
66
2.62k
    srtp_opt.use = PJMEDIA_SRTP_OPTIONAL;
67
68
2.62k
    status = pjmedia_transport_srtp_create(endpt, loop_tp, &srtp_opt, &srtp_tp);
69
2.62k
    if (status != PJ_SUCCESS)
70
0
        goto cleanup;
71
72
2.62k
    sdp_str = (char*)pj_pool_alloc(pool, size + 1);
73
2.62k
    if (!sdp_str)
74
0
        goto cleanup;
75
76
2.62k
    pj_memcpy(sdp_str, data, size);
77
2.62k
    sdp_str[size] = '\0';
78
79
2.62k
    if (size < 3 || sdp_str[0] != 'v' || sdp_str[1] != '=')
80
21
        goto cleanup;
81
82
2.60k
    status = pjmedia_sdp_parse(sdp_pool, sdp_str, size, &remote_sdp);
83
2.60k
    if (status != PJ_SUCCESS)
84
661
        goto cleanup;
85
86
1.93k
    status = pjmedia_sdp_validate(remote_sdp);
87
1.93k
    if (status != PJ_SUCCESS)
88
931
        goto cleanup;
89
90
    /* Create minimal local SDP */
91
1.00k
    local_sdp = PJ_POOL_ZALLOC_T(sdp_pool, pjmedia_sdp_session);
92
1.00k
    local_sdp->origin.user = pj_str("pjsip");
93
1.00k
    local_sdp->origin.version = local_sdp->origin.id = 1;
94
1.00k
    local_sdp->origin.net_type = pj_str("IN");
95
1.00k
    local_sdp->origin.addr_type = pj_str("IP4");
96
1.00k
    local_sdp->origin.addr = pj_str("127.0.0.1");
97
1.00k
    local_sdp->name = pj_str("pjmedia");
98
1.00k
    local_sdp->time.start = 0;
99
1.00k
    local_sdp->time.stop = 0;
100
101
1.00k
    if (remote_sdp->media_count > 0) {
102
993
        pjmedia_sdp_media *m;
103
993
        pj_uint32_t rem_proto = 0;
104
105
993
        m = PJ_POOL_ZALLOC_T(sdp_pool, pjmedia_sdp_media);
106
993
        m->desc.media = pj_str("audio");
107
993
        m->desc.port = 4000;
108
993
        m->desc.port_count = 1;
109
110
        /* Match remote transport protocol to properly test SDES negotiation */
111
993
        rem_proto = pjmedia_sdp_transport_get_proto(&remote_sdp->media[0]->desc.transport);
112
993
        if (rem_proto == PJMEDIA_TP_PROTO_RTP_SAVP) {
113
648
            m->desc.transport = pj_str("RTP/SAVP");
114
648
        } else if (rem_proto == (PJMEDIA_TP_PROTO_RTP_SAVP | PJMEDIA_TP_PROFILE_RTCP_FB)) {
115
1
            m->desc.transport = pj_str("RTP/SAVPF");
116
344
        } else {
117
344
            m->desc.transport = pj_str("RTP/AVP");
118
344
        }
119
120
993
        m->desc.fmt_count = 1;
121
993
        m->desc.fmt[0] = pj_str("0");
122
123
993
        local_sdp->media[local_sdp->media_count++] = m;
124
125
        /* Test as answerer: process remote offer with crypto attributes */
126
993
        status = pjmedia_transport_media_create(srtp_tp, sdp_pool, 0,
127
993
                                                remote_sdp, 0);
128
993
        if (status == PJ_SUCCESS) {
129
            /* This triggers parse_attr_crypto() for answerer role */
130
745
            status = pjmedia_transport_encode_sdp(srtp_tp, sdp_pool,
131
745
                                                  local_sdp, remote_sdp, 0);
132
133
            /* Also test media_start to complete the negotiation */
134
745
            if (status == PJ_SUCCESS) {
135
166
                pjmedia_transport_media_start(srtp_tp, sdp_pool,
136
166
                                              local_sdp, remote_sdp, 0);
137
166
            }
138
745
        }
139
140
        /* Test as offerer: generate crypto offer, then process answer */
141
993
        status = pjmedia_transport_loop_create(endpt, &loop_tp2);
142
993
        if (status != PJ_SUCCESS)
143
0
            goto cleanup;
144
145
993
        status = pjmedia_transport_srtp_create(endpt, loop_tp2, &srtp_opt,
146
993
                                               &srtp_tp2);
147
993
        if (status != PJ_SUCCESS)
148
0
            goto cleanup;
149
150
993
        status = pjmedia_transport_media_create(srtp_tp2, sdp_pool, 0,
151
993
                                                NULL, 0);
152
993
        if (status == PJ_SUCCESS) {
153
993
            pjmedia_sdp_session *offer_sdp;
154
155
            /* Create a local offer */
156
993
            offer_sdp = pjmedia_sdp_session_clone(sdp_pool, local_sdp);
157
993
            if (offer_sdp && offer_sdp->media_count > 0) {
158
993
                offer_sdp->media[0]->desc.transport = pj_str("RTP/SAVP");
159
160
                /* Generate offer with crypto (offerer role) */
161
993
                status = pjmedia_transport_encode_sdp(srtp_tp2, sdp_pool,
162
993
                                                      offer_sdp, NULL, 0);
163
164
993
                if (status == PJ_SUCCESS) {
165
                    /* Process remote answer */
166
993
                    pjmedia_transport_media_start(srtp_tp2, sdp_pool,
167
993
                                                  offer_sdp, remote_sdp, 0);
168
993
                }
169
993
            }
170
993
        }
171
993
    }
172
173
2.62k
cleanup:
174
2.62k
    if (srtp_tp2)
175
993
        pjmedia_transport_close(srtp_tp2);
176
1.62k
    else if (loop_tp2)
177
0
        pjmedia_transport_close(loop_tp2);
178
2.62k
    if (srtp_tp)
179
2.62k
        pjmedia_transport_close(srtp_tp);
180
0
    else if (loop_tp)
181
0
        pjmedia_transport_close(loop_tp);
182
2.62k
    if (sdp_pool)
183
2.62k
        pj_pool_release(sdp_pool);
184
2.62k
    if (pool)
185
2.62k
        pj_pool_release(pool);
186
2.62k
    if (endpt)
187
2.62k
        pjmedia_endpt_destroy(endpt);
188
2.62k
}
189
190
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
191
2.63k
{
192
2.63k
    pj_caching_pool caching_pool;
193
2.63k
    pj_status_t status;
194
195
2.63k
    if (Size < kMinInputLength || Size > kMaxInputLength) {
196
17
        return 1;
197
17
    }
198
199
2.62k
    status = pj_init();
200
2.62k
    if (status != PJ_SUCCESS)
201
0
        return 0;
202
203
2.62k
    pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
204
2.62k
    pj_log_set_level(0);
205
206
2.62k
    mem = &caching_pool.factory;
207
208
    /* Fuzz SRTP SDES */
209
2.62k
    fuzz_sdes(Data, Size);
210
211
2.62k
    pj_caching_pool_destroy(&caching_pool);
212
213
2.62k
    return 0;
214
2.62k
}