/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 | } |