Coverage Report

Created: 2026-06-10 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/coturn/fuzzing/FuzzStun.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: BSD-3-Clause
3
 *
4
 * https://opensource.org/license/bsd-3-clause
5
 *
6
 * Multi-harness libFuzzer entry point for server-side STUN parsing.
7
 *
8
 * Every iteration runs all sub-harnesses in sequence on the same input:
9
 * RFC 5769 integrity checks, multi-SHA/credential integrity, attribute
10
 * iteration, attribute serialization, and legacy (pre-RFC 5389) STUN
11
 * detection. Keeping everything behind a single binary allows the
12
 * upstream OSS-Fuzz build recipe to stay unchanged.
13
 */
14
15
#include <stdbool.h>
16
#include <stdint.h>
17
#include <stdio.h>
18
#include <string.h>
19
20
#include "apputils.h"
21
#include "ns_turn_ioaddr.h"
22
#include "ns_turn_msg.h"
23
#include "ns_turn_msg_defs.h"
24
#include "ns_turn_utils.h"
25
#include "stun_buffer.h"
26
27
0
static uint8_t fuzz_byte(const uint8_t *Data, size_t Size, size_t idx) { return Size ? Data[idx % Size] : 0; }
28
29
0
static uint16_t fuzz_u16(const uint8_t *Data, size_t Size, size_t idx) {
30
0
  return (uint16_t)(((uint16_t)fuzz_byte(Data, Size, idx) << 8) | (uint16_t)fuzz_byte(Data, Size, idx + 1));
31
0
}
32
33
0
static void fuzz_printable_string(const uint8_t *Data, size_t Size, size_t idx, uint8_t *out, size_t out_size) {
34
0
  if (!out_size) {
35
0
    return;
36
0
  }
37
38
0
  const size_t max_len = out_size - 1;
39
0
  const size_t len = max_len ? (size_t)(fuzz_byte(Data, Size, idx) % (max_len + 1)) : 0;
40
0
  for (size_t i = 0; i < len; ++i) {
41
0
    out[i] = (uint8_t)(33 + (fuzz_byte(Data, Size, idx + 1 + i) % 94));
42
0
  }
43
0
  out[len] = 0;
44
0
}
45
46
/* ------------------------------------------------------------------ */
47
/* Integrity: SHA1 short-term + SHA256 long-term (original FuzzStun). */
48
/* ------------------------------------------------------------------ */
49
0
static void harness_integrity_sha1(const uint8_t *Data, size_t Size) {
50
0
  if (Size < 10 || Size > 5120) {
51
0
    return;
52
0
  }
53
54
0
  stun_is_command_message_full_check_str((uint8_t *)Data, Size, 1, NULL);
55
56
0
  uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser";
57
0
  uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm";
58
0
  uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt";
59
60
0
  stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, (uint8_t *)Data, Size, uname, realm, upwd,
61
0
                                   SHATYPE_SHA1);
62
0
  stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, (uint8_t *)Data, Size, uname, realm, upwd,
63
0
                                   SHATYPE_SHA256);
64
0
}
65
66
/* ------------------------------------------------------------------ */
67
/* Integrity across all SHA types + credential modes (FuzzStunIntegrity). */
68
/* ------------------------------------------------------------------ */
69
0
static void harness_integrity_multi(const uint8_t *Data, size_t Size) {
70
0
  if (Size < STUN_HEADER_LENGTH || Size > 5120) {
71
0
    return;
72
0
  }
73
74
0
  uint8_t buf[5120];
75
0
  uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser";
76
0
  uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm";
77
0
  uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt";
78
79
0
  static const SHATYPE sha_types[] = {SHATYPE_SHA1, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512};
80
0
  const size_t num_sha = sizeof(sha_types) / sizeof(sha_types[0]);
81
82
0
  for (size_t s = 0; s < num_sha; s++) {
83
0
    memcpy(buf, Data, Size);
84
0
    stun_is_command_message_full_check_str(buf, Size, 1, NULL);
85
0
    stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, Size, uname, realm, upwd, sha_types[s]);
86
87
0
    memcpy(buf, Data, Size);
88
0
    stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, Size, uname, realm, upwd, sha_types[s]);
89
0
  }
90
0
}
91
92
/* ------------------------------------------------------------------ */
93
/* Attribute TLV iteration + typed extraction (FuzzStunAttrIter).     */
94
/* ------------------------------------------------------------------ */
95
static const uint16_t kAllAttrTypes[] = {
96
    STUN_ATTRIBUTE_MAPPED_ADDRESS,
97
    OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS,
98
    STUN_ATTRIBUTE_CHANGE_REQUEST,
99
    OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS,
100
    OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS,
101
    STUN_ATTRIBUTE_USERNAME,
102
    OLD_STUN_ATTRIBUTE_PASSWORD,
103
    STUN_ATTRIBUTE_MESSAGE_INTEGRITY,
104
    STUN_ATTRIBUTE_ERROR_CODE,
105
    STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
106
    OLD_STUN_ATTRIBUTE_REFLECTED_FROM,
107
    STUN_ATTRIBUTE_CHANNEL_NUMBER,
108
    STUN_ATTRIBUTE_LIFETIME,
109
    STUN_ATTRIBUTE_BANDWIDTH,
110
    STUN_ATTRIBUTE_XOR_PEER_ADDRESS,
111
    STUN_ATTRIBUTE_DATA,
112
    STUN_ATTRIBUTE_REALM,
113
    STUN_ATTRIBUTE_NONCE,
114
    STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,
115
    STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY,
116
    STUN_ATTRIBUTE_EVEN_PORT,
117
    STUN_ATTRIBUTE_REQUESTED_TRANSPORT,
118
    STUN_ATTRIBUTE_DONT_FRAGMENT,
119
    STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN,
120
    STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
121
    OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
122
    STUN_ATTRIBUTE_TIMER_VAL,
123
    STUN_ATTRIBUTE_RESERVATION_TOKEN,
124
    STUN_ATTRIBUTE_PRIORITY,
125
    STUN_ATTRIBUTE_PADDING,
126
    STUN_ATTRIBUTE_RESPONSE_PORT,
127
    STUN_ATTRIBUTE_CONNECTION_ID,
128
    STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY,
129
    STUN_ATTRIBUTE_ADDRESS_ERROR_CODE,
130
    STUN_ATTRIBUTE_SOFTWARE,
131
    STUN_ATTRIBUTE_ALTERNATE_SERVER,
132
    STUN_ATTRIBUTE_FINGERPRINT,
133
    STUN_ATTRIBUTE_ICE_CONTROLLED,
134
    STUN_ATTRIBUTE_RESPONSE_ORIGIN,
135
    STUN_ATTRIBUTE_OTHER_ADDRESS,
136
    STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION,
137
    STUN_ATTRIBUTE_ORIGIN,
138
    STUN_ATTRIBUTE_MOBILITY_TICKET,
139
    STUN_ATTRIBUTE_NEW_BANDWIDTH,
140
};
141
142
static const uint16_t kAddrAttrs[] = {
143
    STUN_ATTRIBUTE_MAPPED_ADDRESS,   STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,  OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
144
    STUN_ATTRIBUTE_XOR_PEER_ADDRESS, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, STUN_ATTRIBUTE_ALTERNATE_SERVER,
145
    STUN_ATTRIBUTE_RESPONSE_ORIGIN,  STUN_ATTRIBUTE_OTHER_ADDRESS,
146
};
147
148
0
static void harness_attr_iter(const uint8_t *Data, size_t Size) {
149
0
  if (Size < STUN_HEADER_LENGTH || Size > 8192) {
150
0
    return;
151
0
  }
152
153
0
  uint8_t buf[8192];
154
0
  memcpy(buf, Data, Size);
155
156
0
  if (!stun_is_command_message_str(buf, Size)) {
157
0
    return;
158
0
  }
159
160
0
  stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
161
0
  while (sar) {
162
0
    (void)stun_attr_get_type(sar);
163
0
    (void)stun_attr_get_len(sar);
164
0
    (void)stun_attr_get_value(sar);
165
0
    (void)stun_attr_is_addr(sar);
166
0
    sar = stun_attr_get_next_str(buf, Size, sar);
167
0
  }
168
169
0
  ioa_addr addr;
170
0
  const size_t num_addr_attrs = sizeof(kAddrAttrs) / sizeof(kAddrAttrs[0]);
171
0
  for (size_t i = 0; i < num_addr_attrs; i++) {
172
0
    sar = stun_attr_get_first_by_type_str(buf, Size, kAddrAttrs[i]);
173
0
    if (sar) {
174
0
      memset(&addr, 0, sizeof(addr));
175
0
      stun_attr_get_addr_str(buf, Size, sar, &addr, NULL);
176
0
    }
177
0
    memset(&addr, 0, sizeof(addr));
178
0
    stun_attr_get_first_addr_str(buf, Size, kAddrAttrs[i], &addr, NULL);
179
0
  }
180
181
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANNEL_NUMBER);
182
0
  if (sar) {
183
0
    (void)stun_attr_get_channel_number(sar);
184
0
  }
185
0
  (void)stun_attr_get_first_channel_number_str(buf, Size);
186
187
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY);
188
0
  if (sar) {
189
0
    (void)stun_get_requested_address_family(sar);
190
0
  }
191
192
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY);
193
0
  if (sar) {
194
0
    (void)stun_get_requested_address_family(sar);
195
0
  }
196
197
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_EVEN_PORT);
198
0
  if (sar) {
199
0
    (void)stun_attr_get_even_port(sar);
200
0
  }
201
202
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_BANDWIDTH);
203
0
  if (sar) {
204
0
    (void)stun_attr_get_bandwidth(sar);
205
0
  }
206
207
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_NEW_BANDWIDTH);
208
0
  if (sar) {
209
0
    (void)stun_attr_get_bandwidth(sar);
210
0
  }
211
212
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESERVATION_TOKEN);
213
0
  if (sar) {
214
0
    (void)stun_attr_get_reservation_token_value(sar);
215
0
  }
216
217
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANGE_REQUEST);
218
0
  if (sar) {
219
0
    bool change_ip = false, change_port = false;
220
0
    stun_attr_get_change_request_str(sar, &change_ip, &change_port);
221
0
  }
222
223
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESPONSE_PORT);
224
0
  if (sar) {
225
0
    (void)stun_attr_get_response_port_str(sar);
226
0
  }
227
228
0
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_PADDING);
229
0
  if (sar) {
230
0
    (void)stun_attr_get_padding_len_str(sar);
231
0
  }
232
233
0
  {
234
0
    int err_code = 0;
235
0
    uint8_t err_msg[1024] = {0};
236
0
    stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
237
0
  }
238
0
  {
239
0
    int err_code = 0;
240
0
    uint8_t err_msg[1024] = {0};
241
0
    uint8_t chal_realm[STUN_MAX_REALM_SIZE + 1] = {0};
242
0
    uint8_t chal_nonce[STUN_MAX_NONCE_SIZE + 1] = {0};
243
0
    uint8_t server_name[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
244
0
    bool oauth = false;
245
0
    stun_is_challenge_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg), chal_realm, chal_nonce, server_name,
246
0
                                   &oauth);
247
0
  }
248
249
0
  const size_t num_all_attrs = sizeof(kAllAttrTypes) / sizeof(kAllAttrTypes[0]);
250
0
  for (size_t i = 0; i < num_all_attrs; i++) {
251
0
    sar = stun_attr_get_first_by_type_str(buf, Size, kAllAttrTypes[i]);
252
0
    if (sar) {
253
0
      (void)stun_attr_get_type(sar);
254
0
      (void)stun_attr_get_len(sar);
255
0
      (void)stun_attr_get_value(sar);
256
0
    }
257
0
  }
258
0
}
259
260
/* ------------------------------------------------------------------ */
261
/* Attribute serialization / append paths (FuzzStunAttrAdd).          */
262
/* ------------------------------------------------------------------ */
263
0
static void harness_attr_add(const uint8_t *Data, size_t Size) {
264
0
  if (Size < STUN_HEADER_LENGTH || Size > 4096) {
265
0
    return;
266
0
  }
267
268
0
  uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0};
269
0
  memcpy(buf, Data, Size);
270
0
  size_t len = Size;
271
272
0
  if (!stun_is_command_message_str(buf, len)) {
273
0
    return;
274
0
  }
275
276
0
  uint8_t test_uname[] = "fuzzuser@fuzz.realm";
277
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_USERNAME, test_uname, (int)(sizeof(test_uname) - 1));
278
279
0
  uint8_t test_realm[] = "fuzz.realm";
280
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, test_realm, (int)(sizeof(test_realm) - 1));
281
282
0
  uint8_t test_nonce[] = "fuzznonce0123456789abcdef";
283
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, test_nonce, (int)(sizeof(test_nonce) - 1));
284
285
0
  uint8_t test_sw[] = "coturn-fuzz/1.0";
286
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_SOFTWARE, test_sw, (int)(sizeof(test_sw) - 1));
287
288
0
  uint8_t lifetime_val[4] = {0x00, 0x00, 0x02, 0x58};
289
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_LIFETIME, lifetime_val, 4);
290
291
0
  uint8_t transport_val[4] = {STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, 0x00, 0x00, 0x00};
292
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_TRANSPORT, transport_val, 4);
293
294
0
  uint8_t af_val[4] = {STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4, 0x00, 0x00, 0x00};
295
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, af_val, 4);
296
297
0
  uint8_t even_port_val[1] = {0x80};
298
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_EVEN_PORT, even_port_val, 1);
299
300
0
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0);
301
302
0
  stun_attr_add_channel_number_str(buf, &len, 0x4001);
303
304
0
  stun_attr_add_bandwidth_str(buf, &len, 1000000);
305
306
0
  ioa_addr addr4 = {0};
307
0
  addr4.s4.sin_family = AF_INET;
308
0
  addr4.s4.sin_port = htons(12345);
309
0
  addr4.s4.sin_addr.s_addr = htonl(0xC0A80001);
310
0
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr4);
311
0
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &addr4);
312
0
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr4);
313
0
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_MAPPED_ADDRESS, &addr4);
314
315
0
  ioa_addr addr6 = {0};
316
0
  addr6.s6.sin6_family = AF_INET6;
317
0
  addr6.s6.sin6_port = htons(54321);
318
0
  addr6.s6.sin6_addr.s6_addr[15] = 1;
319
0
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr6);
320
0
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr6);
321
322
0
  stun_attr_add_address_error_code(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6, 440);
323
324
0
  stun_attr_add_change_request_str(buf, &len, true, true);
325
0
  stun_attr_add_response_port_str(buf, &len, 3479);
326
0
  stun_attr_add_padding_str(buf, &len, 64);
327
328
0
  if (Size > STUN_HEADER_LENGTH + 4) {
329
0
    int data_len = (int)(Size - STUN_HEADER_LENGTH);
330
0
    if (data_len > 1024) {
331
0
      data_len = 1024;
332
0
    }
333
0
    stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DATA, Data + STUN_HEADER_LENGTH, data_len);
334
0
  }
335
336
0
  stun_attr_add_fingerprint_str(buf, &len);
337
0
}
338
339
/* ------------------------------------------------------------------ */
340
/* Legacy (pre-RFC 5389) STUN detection (FuzzOldStun).                */
341
/* ------------------------------------------------------------------ */
342
0
static void harness_old_stun(const uint8_t *Data, size_t Size) {
343
0
  if (Size < STUN_HEADER_LENGTH || Size > 5120) {
344
0
    return;
345
0
  }
346
347
0
  uint8_t buf[5120];
348
0
  memcpy(buf, Data, Size);
349
350
0
  uint32_t cookie = 0;
351
0
  bool is_old = old_stun_is_command_message_str(buf, Size, &cookie);
352
0
  (void)stun_is_command_message_str(buf, Size);
353
354
0
  if (is_old) {
355
0
    (void)stun_get_msg_type_str(buf, Size);
356
0
    (void)stun_get_method_str(buf, Size);
357
358
0
    stun_is_request_str(buf, Size);
359
0
    stun_is_indication_str(buf, Size);
360
0
    stun_is_success_response_str(buf, Size);
361
362
0
    int err_code = 0;
363
0
    uint8_t err_msg[256] = {0};
364
0
    stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
365
366
0
    int fp_present = 0;
367
0
    stun_is_command_message_full_check_str(buf, Size, 1, &fp_present);
368
0
    stun_is_command_message_full_check_str(buf, Size, 0, &fp_present);
369
370
0
    stun_is_binding_request_str(buf, Size, 0);
371
0
    stun_is_binding_response_str(buf, Size);
372
373
0
    stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
374
0
    while (sar) {
375
0
      (void)stun_attr_get_type(sar);
376
0
      (void)stun_attr_get_len(sar);
377
0
      sar = stun_attr_get_next_str(buf, Size, sar);
378
0
    }
379
0
  }
380
0
}
381
382
/* ------------------------------------------------------------------ */
383
/* Challenge-response builder (FuzzStunChallengeResponse).            */
384
/*                                                                    */
385
/* stun_is_challenge_response_str only descends into its three inner  */
386
/* stun_attr_get_first_by_type_str calls when:                        */
387
/*   1) the message is an error response,                             */
388
/*   2) err_code is 401 OR 438,                                       */
389
/*   3) a REALM attribute is present,                                 */
390
/*   4) a NONCE attribute is present.                                 */
391
/* The OAuth branch additionally requires THIRD-PARTY-AUTHORIZATION.  */
392
/*                                                                    */
393
/* The fuzzer-driven harness in harness_attr_iter calls the function  */
394
/* every iteration but the conjunction of conditions is too specific  */
395
/* for libFuzzer to discover from binary mutation alone — OSS-Fuzz    */
396
/* introspector flags 9 unreached callsites under                     */
397
/* stun_attr_get_first_by_type_str gated on this function.            */
398
/*                                                                    */
399
/* Build six deterministic message variants on every iteration so     */
400
/* every internal branch is exercised regardless of the input bytes.  */
401
/* Realm / nonce / server-name lengths are derived from fuzz bytes so */
402
/* each iteration is still meaningfully distinct.                     */
403
/* ------------------------------------------------------------------ */
404
0
static void harness_challenge_response_builder(const uint8_t *Data, size_t Size) {
405
0
  if (!Size) {
406
0
    return;
407
0
  }
408
409
0
  static const uint16_t kMethods[] = {
410
0
      STUN_METHOD_ALLOCATE,
411
0
      STUN_METHOD_BINDING,
412
0
      STUN_METHOD_REFRESH,
413
0
      STUN_METHOD_CHANNEL_BIND,
414
0
  };
415
0
  const uint16_t method = kMethods[Data[0] % (sizeof(kMethods) / sizeof(kMethods[0]))];
416
417
  /* Length of each variable-length string is taken from a single fuzz byte
418
   * so libFuzzer still varies the inputs across iterations. Floors keep the
419
   * attribute non-empty so the inner copies in stun_is_challenge_response_str
420
   * actually populate the output buffers. */
421
0
  const size_t realm_len = 1 + (Size > 1 ? (Data[1] % 16) : 0);
422
0
  const size_t nonce_len = 1 + (Size > 2 ? (Data[2] % 16) : 0);
423
0
  const size_t server_len = 1 + (Size > 3 ? (Data[3] % 16) : 0);
424
425
0
  uint8_t realm_buf[STUN_MAX_REALM_SIZE + 1] = {0};
426
0
  uint8_t nonce_buf[STUN_MAX_NONCE_SIZE + 1] = {0};
427
0
  uint8_t server_buf[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
428
0
  for (size_t i = 0; i < realm_len; ++i) {
429
0
    realm_buf[i] = (uint8_t)('a' + (i % 26));
430
0
  }
431
0
  for (size_t i = 0; i < nonce_len; ++i) {
432
0
    nonce_buf[i] = (uint8_t)('0' + (i % 10));
433
0
  }
434
0
  for (size_t i = 0; i < server_len; ++i) {
435
0
    server_buf[i] = (uint8_t)('A' + (i % 26));
436
0
  }
437
438
0
  stun_tid tid = {0};
439
0
  for (size_t i = 0; i < STUN_TID_SIZE; ++i) {
440
0
    tid.tsx_id[i] = (uint8_t)(Size > i + 4 ? Data[i + 4] : (uint8_t)i);
441
0
  }
442
443
  /* Each variant builds a fresh error response and runs it through the
444
   * predicate so the inner attribute lookups fire. */
445
0
  static const struct {
446
0
    uint16_t err_code;
447
0
    bool include_realm;
448
0
    bool include_nonce;
449
0
    bool include_third_party_auth;
450
0
  } kVariants[] = {
451
0
      {401, true, true, false},   /* canonical 401 challenge: REALM + NONCE */
452
0
      {401, true, true, true},    /* same + THIRD-PARTY-AUTHORIZATION (OAuth branch) */
453
0
      {438, true, true, false},   /* covers the (*err_code) == 438 disjunct */
454
0
      {401, true, false, false},  /* REALM present, NONCE missing — negative inner path */
455
0
      {401, false, false, false}, /* err_code matches but no REALM — outer negative path */
456
0
      {400, true, true, false},   /* err_code does not match 401/438 — early-out path */
457
0
  };
458
459
0
  for (size_t v = 0; v < sizeof(kVariants) / sizeof(kVariants[0]); ++v) {
460
0
    uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0};
461
0
    size_t len = 0;
462
0
    stun_init_error_response_str(method, buf, &len, kVariants[v].err_code, (const uint8_t *)"unauthorized", &tid, true);
463
464
0
    if (kVariants[v].include_realm) {
465
0
      stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, realm_buf, (int)realm_len);
466
0
    }
467
0
    if (kVariants[v].include_third_party_auth) {
468
0
      stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION, server_buf, (int)server_len);
469
0
    }
470
0
    if (kVariants[v].include_nonce) {
471
0
      stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, nonce_buf, (int)nonce_len);
472
0
    }
473
474
0
    int err_code = 0;
475
0
    uint8_t err_msg[1024] = {0};
476
0
    uint8_t out_realm[STUN_MAX_REALM_SIZE + 1] = {0};
477
0
    uint8_t out_nonce[STUN_MAX_NONCE_SIZE + 1] = {0};
478
0
    uint8_t out_server[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
479
0
    bool oauth = false;
480
0
    (void)stun_is_challenge_response_str(buf, len, &err_code, err_msg, sizeof(err_msg), out_realm, out_nonce,
481
0
                                         out_server, &oauth);
482
483
    /* Also exercise the NULL-oauth-pointer branch, which is reachable from
484
     * other call sites in the codebase. */
485
0
    (void)stun_is_challenge_response_str(buf, len, &err_code, err_msg, sizeof(err_msg), out_realm, out_nonce,
486
0
                                         out_server, NULL);
487
0
  }
488
0
}
489
490
/* ------------------------------------------------------------------ */
491
/* Address parser coverage for make_ioa_addr_from_full_string.        */
492
/* ------------------------------------------------------------------ */
493
0
static void harness_full_addr_parser(const uint8_t *Data, size_t Size) {
494
0
  ioa_addr addr;
495
0
  const uint16_t default_port = fuzz_u16(Data, Size, 0);
496
0
  const uint16_t explicit_port = fuzz_u16(Data, Size, 2);
497
498
0
  char ipv4_plain[MAX_IOA_ADDR_STRING];
499
0
  char ipv4_with_port[MAX_IOA_ADDR_STRING];
500
0
  char ipv6_bracketed[MAX_IOA_ADDR_STRING];
501
0
  char ipv6_with_port[MAX_IOA_ADDR_STRING];
502
0
  char ipv4_spaced[MAX_IOA_ADDR_STRING];
503
504
0
  snprintf(ipv4_plain, sizeof(ipv4_plain), "%u.%u.%u.%u", fuzz_byte(Data, Size, 4), fuzz_byte(Data, Size, 5),
505
0
           fuzz_byte(Data, Size, 6), fuzz_byte(Data, Size, 7));
506
0
  snprintf(ipv4_with_port, sizeof(ipv4_with_port), "%u.%u.%u.%u:%u", fuzz_byte(Data, Size, 8), fuzz_byte(Data, Size, 9),
507
0
           fuzz_byte(Data, Size, 10), fuzz_byte(Data, Size, 11), explicit_port);
508
0
  snprintf(ipv6_bracketed, sizeof(ipv6_bracketed), "[2001:db8:%x:%x::%x]", fuzz_u16(Data, Size, 12),
509
0
           fuzz_u16(Data, Size, 14), fuzz_u16(Data, Size, 16));
510
0
  snprintf(ipv6_with_port, sizeof(ipv6_with_port), "[2001:db8:%x:%x::%x]:%u", fuzz_u16(Data, Size, 18),
511
0
           fuzz_u16(Data, Size, 20), fuzz_u16(Data, Size, 22), explicit_port);
512
0
  snprintf(ipv4_spaced, sizeof(ipv4_spaced), "  %u.%u.%u.%u:%u", fuzz_byte(Data, Size, 24), fuzz_byte(Data, Size, 25),
513
0
           fuzz_byte(Data, Size, 26), fuzz_byte(Data, Size, 27), explicit_port);
514
515
0
  static const char *kFixedInputs[] = {
516
0
      "", "0.0.0.0", "127.0.0.1:3478", "192.0.2.1:", "[::1]", "[::1]:5349", "[2001:db8::1]  ", "::1",
517
0
  };
518
519
0
  for (size_t i = 0; i < sizeof(kFixedInputs) / sizeof(kFixedInputs[0]); ++i) {
520
0
    memset(&addr, 0, sizeof(addr));
521
0
    (void)make_ioa_addr_from_full_string((const uint8_t *)kFixedInputs[i], default_port, &addr);
522
0
  }
523
524
0
  const char *generated[] = {ipv4_plain, ipv4_with_port, ipv6_bracketed, ipv6_with_port, ipv4_spaced};
525
0
  for (size_t i = 0; i < sizeof(generated) / sizeof(generated[0]); ++i) {
526
0
    memset(&addr, 0, sizeof(addr));
527
0
    (void)make_ioa_addr_from_full_string((const uint8_t *)generated[i], default_port, &addr);
528
0
  }
529
530
0
  (void)make_ioa_addr_from_full_string((const uint8_t *)ipv4_plain, default_port, NULL);
531
0
}
532
533
/* ------------------------------------------------------------------ */
534
/* Direct MESSAGE-INTEGRITY append helpers.                           */
535
/* ------------------------------------------------------------------ */
536
0
static void harness_integrity_attr_add(const uint8_t *Data, size_t Size) {
537
0
  static const SHATYPE kShaTypes[] = {SHATYPE_SHA1, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512};
538
0
  const SHATYPE shatype = kShaTypes[fuzz_byte(Data, Size, 0) % (sizeof(kShaTypes) / sizeof(kShaTypes[0]))];
539
540
0
  uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = {0};
541
0
  uint8_t realm[STUN_MAX_REALM_SIZE + 1] = {0};
542
0
  uint8_t nonce[STUN_MAX_NONCE_SIZE + 1] = {0};
543
0
  uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = {0};
544
0
  password_t pwd = {0};
545
0
  hmackey_t key = {0};
546
547
0
  fuzz_printable_string(Data, Size, 1, uname, sizeof(uname));
548
0
  fuzz_printable_string(Data, Size, 1 + sizeof(uname), realm, sizeof(realm));
549
0
  fuzz_printable_string(Data, Size, 1 + sizeof(uname) + sizeof(realm), nonce, sizeof(nonce));
550
0
  fuzz_printable_string(Data, Size, 1 + sizeof(uname) + sizeof(realm) + sizeof(nonce), upwd, sizeof(upwd));
551
0
  fuzz_printable_string(Data, Size, 1 + sizeof(uname) + sizeof(realm) + sizeof(nonce) + sizeof(upwd), pwd, sizeof(pwd));
552
553
0
  if (!uname[0]) {
554
0
    memcpy(uname, "fuzzuser", sizeof("fuzzuser"));
555
0
  }
556
0
  if (!realm[0]) {
557
0
    memcpy(realm, "fuzz.realm", sizeof("fuzz.realm"));
558
0
  }
559
0
  if (!nonce[0]) {
560
0
    memcpy(nonce, "fuzznonce", sizeof("fuzznonce"));
561
0
  }
562
0
  if (!upwd[0]) {
563
0
    memcpy(upwd, "fuzzpassword", sizeof("fuzzpassword"));
564
0
  }
565
0
  if (!pwd[0]) {
566
0
    memcpy(pwd, "shortterm", sizeof("shortterm"));
567
0
  }
568
569
0
  for (size_t i = 0; i < sizeof(key); ++i) {
570
0
    key[i] = fuzz_byte(Data, Size, 32 + i);
571
0
  }
572
573
0
  uint8_t buf[MAX_STUN_MESSAGE_SIZE];
574
0
  size_t len = 0;
575
0
  stun_init_request_str(STUN_METHOD_BINDING, buf, &len);
576
0
  (void)stun_attr_add_integrity_by_key_str(buf, &len, uname, realm, key, nonce, shatype);
577
578
0
  len = 0;
579
0
  stun_init_request_str(STUN_METHOD_ALLOCATE, buf, &len);
580
0
  (void)stun_attr_add_integrity_by_user_str(buf, &len, uname, realm, upwd, nonce, shatype);
581
582
0
  len = 0;
583
0
  stun_init_request_str(STUN_METHOD_REFRESH, buf, &len);
584
0
  (void)stun_attr_add_integrity_by_user_short_term_str(buf, &len, uname, pwd, shatype);
585
586
0
  len = 0;
587
0
  stun_init_request_str(STUN_METHOD_CHANNEL_BIND, buf, &len);
588
0
  (void)stun_attr_add_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, &len, key, pwd, shatype);
589
590
0
  len = 0;
591
0
  stun_init_request_str(STUN_METHOD_BINDING, buf, &len);
592
0
  (void)stun_attr_add_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, &len, key, pwd, shatype);
593
0
}
594
595
/* ------------------------------------------------------------------ */
596
/* libFuzzer entry point — run every harness on each input.           */
597
/* ------------------------------------------------------------------ */
598
318
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
599
318
  harness_integrity_sha1(Data, Size);
600
318
  harness_integrity_multi(Data, Size);
601
318
  harness_attr_iter(Data, Size);
602
318
  harness_attr_add(Data, Size);
603
318
  harness_old_stun(Data, Size);
604
318
  harness_challenge_response_builder(Data, Size);
605
318
  harness_full_addr_parser(Data, Size);
606
318
  harness_integrity_attr_add(Data, Size);
607
318
  return 0;
608
318
}