Coverage Report

Created: 2026-04-29 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pjsip/tests/fuzz/fuzz-sip-dialog.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 <pjlib-util.h>
25
#include <pjsip.h>
26
27
pjsip_endpoint *endpt;
28
pj_caching_pool caching_pool;
29
pjsip_transport *loop_transport;
30
31
10.8k
#define POOL_SIZE 8000
32
2
#define PJSIP_TEST_MEM_SIZE (2*1024*1024)
33
34
static pjsip_module dialog_test_mod;
35
36
static char* alloc_str_from_data(pj_pool_t *pool, const uint8_t *data,
37
                                  size_t offset, size_t len)
38
21.3k
{
39
21.3k
    char *buf = (char*)pj_pool_alloc(pool, len + 1);
40
21.3k
    pj_memcpy(buf, data + offset, len);
41
21.3k
    buf[len] = '\0';
42
21.3k
    return buf;
43
21.3k
}
44
45
static void test_uac_dialog(pj_pool_t *pool, const uint8_t *data, size_t size)
46
5.40k
{
47
5.40k
    pj_ssize_t len1, len2, len3;
48
5.40k
    pj_str_t local_uri, remote_uri, target;
49
5.40k
    pjsip_dialog *dlg;
50
5.40k
    pj_status_t status;
51
5.40k
    pjsip_tx_data *tdata;
52
5.40k
    char *route_data;
53
5.40k
    pj_str_t route_name;
54
5.40k
    pjsip_hdr *hdr;
55
5.40k
    pjsip_route_hdr route_set;
56
5.40k
    pjsip_route_hdr *route_hdr;
57
58
5.40k
    if (size < 3)
59
113
        return;
60
61
    /* Split input data into three URI strings */
62
5.28k
    len1 = (size / 3 > 50) ? 50 : size / 3;
63
5.28k
    len2 = (size / 3 > 50) ? 50 : size / 3;
64
5.28k
    len3 = (size - len1 - len2 > 50) ? 50 : size - len1 - len2;
65
66
5.28k
    local_uri.ptr = alloc_str_from_data(pool, data, 0, len1);
67
5.28k
    local_uri.slen = len1;
68
5.28k
    remote_uri.ptr = alloc_str_from_data(pool, data, len1, len2);
69
5.28k
    remote_uri.slen = len2;
70
5.28k
    target.ptr = alloc_str_from_data(pool, data, len1 + len2, len3);
71
5.28k
    target.slen = len3;
72
73
    /* Create UAC dialog */
74
5.28k
    dlg = NULL;
75
5.28k
    status = pjsip_dlg_create_uac(pjsip_ua_instance(), &local_uri,
76
5.28k
                                   NULL, &remote_uri, &target, &dlg);
77
5.28k
    if (status != PJ_SUCCESS || !dlg)
78
5.12k
        return;
79
80
168
    pjsip_dlg_inc_lock(dlg);
81
82
    /* Parse and set route header from fuzzer data */
83
168
    if (size > 150) {
84
51
        route_data = alloc_str_from_data(pool, data, 150, size - 150);
85
51
        route_name = pj_str("Route");
86
51
        hdr = (pjsip_hdr*)pjsip_parse_hdr(pool, &route_name,
87
51
                                           route_data, size - 150, NULL);
88
51
        if (hdr && hdr->type == PJSIP_H_ROUTE) {
89
27
            route_hdr = (pjsip_route_hdr*)hdr;
90
27
            pj_list_init(&route_set);
91
27
            pj_list_push_back(&route_set, route_hdr);
92
27
            pjsip_dlg_set_route_set(dlg, &route_set);
93
27
        }
94
51
    }
95
96
    /* Create request within dialog */
97
168
    tdata = NULL;
98
168
    status = pjsip_dlg_create_request(dlg, &pjsip_options_method, -1, &tdata);
99
168
    if (status == PJ_SUCCESS && tdata)
100
168
        pjsip_tx_data_dec_ref(tdata);
101
102
    /* Test session management */
103
168
    pjsip_dlg_inc_session(dlg, &dialog_test_mod);
104
168
    pjsip_dlg_dec_session(dlg, &dialog_test_mod);
105
106
168
    pjsip_dlg_dec_lock(dlg);
107
168
}
108
109
static void test_uas_dialog(pj_pool_t *pool, const uint8_t *data, size_t size)
110
5.40k
{
111
5.40k
    char *data_copy;
112
5.40k
    pjsip_parser_err_report err_list;
113
5.40k
    pjsip_msg *msg;
114
5.40k
    pjsip_rx_data rdata;
115
5.40k
    pjsip_dialog *dlg;
116
5.40k
    pj_status_t status;
117
5.40k
    pjsip_tx_data *tdata;
118
5.40k
    pjsip_transaction *tsx;
119
5.40k
    pj_time_val timeout;
120
121
5.40k
    if (size < 1)
122
0
        return;
123
124
    /* Parse input data as SIP message */
125
5.40k
    data_copy = alloc_str_from_data(pool, data, 0, size);
126
5.40k
    pj_list_init(&err_list);
127
128
5.40k
    msg = pjsip_parse_msg(pool, data_copy, size, &err_list);
129
5.40k
    if (!msg || msg->type != PJSIP_REQUEST_MSG)
130
4.36k
        return;
131
132
    /* Setup received message structure */
133
1.03k
    pj_bzero(&rdata, sizeof(rdata));
134
1.03k
    rdata.msg_info.msg = msg;
135
1.03k
    rdata.tp_info.pool = pool;
136
1.03k
    rdata.tp_info.transport = loop_transport;
137
1.03k
    rdata.msg_info.via = (pjsip_via_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
138
1.03k
    rdata.msg_info.from = (pjsip_from_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_FROM, NULL);
139
1.03k
    rdata.msg_info.to = (pjsip_to_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL);
140
1.03k
    rdata.msg_info.cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
141
1.03k
    rdata.msg_info.cid = (pjsip_cid_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CALL_ID, NULL);
142
143
    /* Validate required headers */
144
1.03k
    if (!rdata.msg_info.via || !rdata.msg_info.from || !rdata.msg_info.to ||
145
847
        !rdata.msg_info.cseq || !rdata.msg_info.cid)
146
195
        return;
147
148
    /* Set recvd_param required by PJSIP transport layer */
149
842
    if (rdata.msg_info.via->recvd_param.slen == 0)
150
828
        rdata.msg_info.via->recvd_param = pj_str("127.0.0.1");
151
152
    /* Skip if method doesn't create a dialog */
153
842
    if (!pjsip_method_creates_dialog(&msg->line.req.method))
154
119
        return;
155
156
    /* Skip if To tag already exists */
157
723
    if (rdata.msg_info.to->tag.slen != 0)
158
14
        return;
159
160
    /* Create UAS dialog from incoming request */
161
709
    dlg = NULL;
162
709
    status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(),
163
709
                                                &rdata, NULL, &dlg);
164
709
    if (status != PJ_SUCCESS || !dlg)
165
428
        return;
166
167
    /* Create response within dialog */
168
281
    tdata = NULL;
169
281
    status = pjsip_dlg_create_response(dlg, &rdata, 200, NULL, &tdata);
170
281
    if (status == PJ_SUCCESS && tdata)
171
281
        pjsip_tx_data_dec_ref(tdata);
172
173
    /* Test session management */
174
281
    pjsip_dlg_inc_session(dlg, &dialog_test_mod);
175
281
    pjsip_dlg_dec_session(dlg, &dialog_test_mod);
176
177
    /* Terminate the transaction to prevent memory leak */
178
281
    tsx = pjsip_rdata_get_tsx(&rdata);
179
281
    if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED)
180
281
        pjsip_tsx_terminate(tsx, 500);
181
182
281
    pjsip_dlg_dec_lock(dlg);
183
184
    /* Process events to cleanup terminated transactions */
185
281
    timeout.sec = 0;
186
281
    timeout.msec = 0;
187
281
    pjsip_endpt_handle_events(endpt, &timeout);
188
281
}
189
190
int LLVMFuzzerInitialize(int *argc, char ***argv)
191
2
{
192
2
    pj_status_t status;
193
194
2
    PJ_UNUSED_ARG(argc);
195
2
    PJ_UNUSED_ARG(argv);
196
197
    /* Initialize PJLIB */
198
2
    status = pj_init();
199
2
    if (status != PJ_SUCCESS)
200
0
        return 1;
201
202
    /* Initialize memory pool factory */
203
2
    pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy,
204
2
                         PJSIP_TEST_MEM_SIZE);
205
206
    /* Create SIP endpoint */
207
2
    status = pjsip_endpt_create(&caching_pool.factory, "dialog-fuzzer", &endpt);
208
2
    if (status != PJ_SUCCESS)
209
0
        return 1;
210
211
    /* Initialize transaction layer */
212
2
    status = pjsip_tsx_layer_init_module(endpt);
213
2
    if (status != PJ_SUCCESS)
214
0
        return 1;
215
216
    /* Initialize UA layer */
217
2
    status = pjsip_ua_init_module(endpt, NULL);
218
2
    if (status != PJ_SUCCESS)
219
0
        return 1;
220
221
    /* Initialize loop transport */
222
2
    status = pjsip_loop_start(endpt, &loop_transport);
223
2
    if (status != PJ_SUCCESS)
224
0
        return 1;
225
226
    /* Register dialog test module */
227
2
    pj_bzero(&dialog_test_mod, sizeof(dialog_test_mod));
228
2
    dialog_test_mod.name = pj_str("dialog-test-mod");
229
2
    dialog_test_mod.id = -1;
230
2
    dialog_test_mod.priority = PJSIP_MOD_PRIORITY_APPLICATION;
231
232
2
    status = pjsip_endpt_register_module(endpt, &dialog_test_mod);
233
2
    if (status != PJ_SUCCESS)
234
0
        return 1;
235
236
    /* Disable logging for performance */
237
2
    pj_log_set_level(0);
238
239
2
    return 0;
240
2
}
241
242
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
243
5.40k
{
244
5.40k
    pj_pool_t *pool;
245
5.40k
    pj_time_val timeout;
246
247
    /* Create pool for this iteration */
248
5.40k
    pool = pjsip_endpt_create_pool(endpt, "dialog-fuzz", POOL_SIZE, POOL_SIZE);
249
5.40k
    if (!pool)
250
0
        return 0;
251
252
    /* Test both UAC and UAS dialog creation */
253
5.40k
    test_uac_dialog(pool, Data, Size);
254
5.40k
    test_uas_dialog(pool, Data, Size);
255
256
    /* Release pool */
257
5.40k
    pjsip_endpt_release_pool(endpt, pool);
258
259
    /* Final cleanup of any pending events */
260
5.40k
    timeout.sec = 0;
261
5.40k
    timeout.msec = 0;
262
5.40k
    pjsip_endpt_handle_events(endpt, &timeout);
263
264
5.40k
    return 0;
265
5.40k
}