Coverage Report

Created: 2026-09-14 06:20

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
17.9k
#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
24.5k
{
39
24.5k
    char *buf = (char*)pj_pool_alloc(pool, len + 1);
40
24.5k
    pj_memcpy(buf, data + offset, len);
41
24.5k
    buf[len] = '\0';
42
24.5k
    return buf;
43
24.5k
}
44
45
static void test_uac_dialog(pj_pool_t *pool, const uint8_t *data, size_t size)
46
6.22k
{
47
6.22k
    pj_ssize_t len1, len2, len3;
48
6.22k
    pj_str_t local_uri, remote_uri, target;
49
6.22k
    pjsip_dialog *dlg;
50
6.22k
    pj_status_t status;
51
6.22k
    pjsip_tx_data *tdata;
52
6.22k
    char *route_data;
53
6.22k
    pj_str_t route_name;
54
6.22k
    pjsip_hdr *hdr;
55
6.22k
    pjsip_route_hdr route_set;
56
6.22k
    pjsip_route_hdr *route_hdr;
57
58
6.22k
    if (size < 3)
59
106
        return;
60
61
    /* Split input data into three URI strings */
62
6.11k
    len1 = (size / 3 > 50) ? 50 : size / 3;
63
6.11k
    len2 = (size / 3 > 50) ? 50 : size / 3;
64
6.11k
    len3 = (size - len1 - len2 > 50) ? 50 : size - len1 - len2;
65
66
6.11k
    local_uri.ptr = alloc_str_from_data(pool, data, 0, len1);
67
6.11k
    local_uri.slen = len1;
68
6.11k
    remote_uri.ptr = alloc_str_from_data(pool, data, len1, len2);
69
6.11k
    remote_uri.slen = len2;
70
6.11k
    target.ptr = alloc_str_from_data(pool, data, len1 + len2, len3);
71
6.11k
    target.slen = len3;
72
73
    /* Create UAC dialog */
74
6.11k
    dlg = NULL;
75
6.11k
    status = pjsip_dlg_create_uac(pjsip_ua_instance(), &local_uri,
76
6.11k
                                   NULL, &remote_uri, &target, &dlg);
77
6.11k
    if (status != PJ_SUCCESS || !dlg)
78
5.92k
        return;
79
80
188
    pjsip_dlg_inc_lock(dlg);
81
82
    /* Parse and set route header from fuzzer data */
83
188
    if (size > 150) {
84
29
        route_data = alloc_str_from_data(pool, data, 150, size - 150);
85
29
        route_name = pj_str("Route");
86
29
        hdr = (pjsip_hdr*)pjsip_parse_hdr(pool, &route_name,
87
29
                                           route_data, size - 150, NULL);
88
29
        if (hdr && hdr->type == PJSIP_H_ROUTE) {
89
15
            route_hdr = (pjsip_route_hdr*)hdr;
90
15
            pj_list_init(&route_set);
91
15
            pj_list_push_back(&route_set, route_hdr);
92
15
            pjsip_dlg_set_route_set(dlg, &route_set);
93
15
        }
94
29
    }
95
96
    /* Create request within dialog */
97
188
    tdata = NULL;
98
188
    status = pjsip_dlg_create_request(dlg, &pjsip_options_method, -1, &tdata);
99
188
    if (status == PJ_SUCCESS && tdata)
100
188
        pjsip_tx_data_dec_ref(tdata);
101
102
    /* Test session management */
103
188
    pjsip_dlg_inc_session(dlg, &dialog_test_mod);
104
188
    pjsip_dlg_dec_session(dlg, &dialog_test_mod);
105
106
188
    pjsip_dlg_dec_lock(dlg);
107
188
}
108
109
static void test_uas_dialog(pj_pool_t *pool, const uint8_t *data, size_t size)
110
6.22k
{
111
6.22k
    char *data_copy;
112
6.22k
    pjsip_parser_err_report err_list;
113
6.22k
    pjsip_msg *msg;
114
6.22k
    pjsip_rx_data rdata;
115
6.22k
    pjsip_dialog *dlg;
116
6.22k
    pj_status_t status;
117
6.22k
    pjsip_tx_data *tdata;
118
6.22k
    pjsip_transaction *tsx;
119
6.22k
    pj_time_val timeout;
120
121
6.22k
    if (size < 1)
122
0
        return;
123
124
    /* Parse input data as SIP message */
125
6.22k
    data_copy = alloc_str_from_data(pool, data, 0, size);
126
6.22k
    pj_list_init(&err_list);
127
128
6.22k
    msg = pjsip_parse_msg(pool, data_copy, size, &err_list);
129
6.22k
    if (!msg || msg->type != PJSIP_REQUEST_MSG)
130
5.17k
        return;
131
132
    /* Setup received message structure */
133
1.05k
    pj_bzero(&rdata, sizeof(rdata));
134
1.05k
    rdata.msg_info.msg = msg;
135
1.05k
    rdata.tp_info.pool = pool;
136
1.05k
    rdata.tp_info.transport = loop_transport;
137
1.05k
    rdata.msg_info.via = (pjsip_via_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
138
1.05k
    rdata.msg_info.from = (pjsip_from_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_FROM, NULL);
139
1.05k
    rdata.msg_info.to = (pjsip_to_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL);
140
1.05k
    rdata.msg_info.cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
141
1.05k
    rdata.msg_info.cid = (pjsip_cid_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CALL_ID, NULL);
142
143
    /* Validate required headers */
144
1.05k
    if (!rdata.msg_info.via || !rdata.msg_info.from || !rdata.msg_info.to ||
145
940
        !rdata.msg_info.cseq || !rdata.msg_info.cid || !rdata.msg_info.cid->id.slen)
146
115
        return;
147
148
    /* Set recvd_param required by PJSIP transport layer */
149
937
    if (rdata.msg_info.via->recvd_param.slen == 0)
150
914
        rdata.msg_info.via->recvd_param = pj_str("127.0.0.1");
151
152
    /* Skip if method doesn't create a dialog */
153
937
    if (!pjsip_method_creates_dialog(&msg->line.req.method))
154
28
        return;
155
156
    /* Skip if To tag already exists */
157
909
    if (rdata.msg_info.to->tag.slen != 0)
158
1
        return;
159
160
    /* Create UAS dialog from incoming request */
161
908
    dlg = NULL;
162
908
    status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(),
163
908
                                                &rdata, NULL, &dlg);
164
908
    if (status != PJ_SUCCESS || !dlg)
165
549
        return;
166
167
    /* Create response within dialog */
168
359
    tdata = NULL;
169
359
    status = pjsip_dlg_create_response(dlg, &rdata, 200, NULL, &tdata);
170
359
    if (status == PJ_SUCCESS && tdata)
171
359
        pjsip_tx_data_dec_ref(tdata);
172
173
    /* Test session management */
174
359
    pjsip_dlg_inc_session(dlg, &dialog_test_mod);
175
359
    pjsip_dlg_dec_session(dlg, &dialog_test_mod);
176
177
    /* Terminate the transaction to prevent memory leak */
178
359
    tsx = pjsip_rdata_get_tsx(&rdata);
179
359
    if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED)
180
359
        pjsip_tsx_terminate(tsx, 500);
181
182
359
    pjsip_dlg_dec_lock(dlg);
183
184
    /* Process events to cleanup terminated transactions */
185
359
    timeout.sec = 0;
186
359
    timeout.msec = 0;
187
359
    pjsip_endpt_handle_events(endpt, &timeout);
188
359
}
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
8.95k
{
244
8.95k
    pj_pool_t *pool;
245
8.95k
    pj_time_val timeout;
246
247
    /* Create pool for this iteration */
248
8.95k
    pool = pjsip_endpt_create_pool(endpt, "dialog-fuzz", POOL_SIZE, POOL_SIZE);
249
8.95k
    if (!pool)
250
0
        return 0;
251
252
    /* Test both UAC and UAS dialog creation */
253
8.95k
    test_uac_dialog(pool, Data, Size);
254
8.95k
    test_uas_dialog(pool, Data, Size);
255
256
    /* Release pool */
257
8.95k
    pjsip_endpt_release_pool(endpt, pool);
258
259
    /* Final cleanup of any pending events */
260
8.95k
    timeout.sec = 0;
261
8.95k
    timeout.msec = 0;
262
8.95k
    pjsip_endpt_handle_events(endpt, &timeout);
263
264
8.95k
    return 0;
265
8.95k
}