Coverage Report

Created: 2026-04-29 07:01

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 <string.h>
18
19
#include "apputils.h"
20
#include "ns_turn_msg.h"
21
#include "ns_turn_msg_defs.h"
22
#include "ns_turn_utils.h"
23
#include "stun_buffer.h"
24
25
/* ------------------------------------------------------------------ */
26
/* Integrity: SHA1 short-term + SHA256 long-term (original FuzzStun). */
27
/* ------------------------------------------------------------------ */
28
660
static void harness_integrity_sha1(const uint8_t *Data, size_t Size) {
29
660
  if (Size < 10 || Size > 5120) {
30
24
    return;
31
24
  }
32
33
636
  stun_is_command_message_full_check_str((uint8_t *)Data, Size, 1, NULL);
34
35
636
  uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser";
36
636
  uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm";
37
636
  uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt";
38
39
636
  stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, (uint8_t *)Data, Size, uname, realm, upwd,
40
636
                                   SHATYPE_SHA1);
41
636
  stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, (uint8_t *)Data, Size, uname, realm, upwd,
42
636
                                   SHATYPE_SHA256);
43
636
}
44
45
/* ------------------------------------------------------------------ */
46
/* Integrity across all SHA types + credential modes (FuzzStunIntegrity). */
47
/* ------------------------------------------------------------------ */
48
660
static void harness_integrity_multi(const uint8_t *Data, size_t Size) {
49
660
  if (Size < STUN_HEADER_LENGTH || Size > 5120) {
50
33
    return;
51
33
  }
52
53
627
  uint8_t buf[5120];
54
627
  uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser";
55
627
  uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm";
56
627
  uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt";
57
58
627
  static const SHATYPE sha_types[] = {SHATYPE_SHA1, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512};
59
627
  const size_t num_sha = sizeof(sha_types) / sizeof(sha_types[0]);
60
61
3.13k
  for (size_t s = 0; s < num_sha; s++) {
62
2.50k
    memcpy(buf, Data, Size);
63
2.50k
    stun_is_command_message_full_check_str(buf, Size, 1, NULL);
64
2.50k
    stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, Size, uname, realm, upwd, sha_types[s]);
65
66
2.50k
    memcpy(buf, Data, Size);
67
2.50k
    stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, Size, uname, realm, upwd, sha_types[s]);
68
2.50k
  }
69
627
}
70
71
/* ------------------------------------------------------------------ */
72
/* Attribute TLV iteration + typed extraction (FuzzStunAttrIter).     */
73
/* ------------------------------------------------------------------ */
74
static const uint16_t kAllAttrTypes[] = {
75
    STUN_ATTRIBUTE_MAPPED_ADDRESS,
76
    OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS,
77
    STUN_ATTRIBUTE_CHANGE_REQUEST,
78
    OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS,
79
    OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS,
80
    STUN_ATTRIBUTE_USERNAME,
81
    OLD_STUN_ATTRIBUTE_PASSWORD,
82
    STUN_ATTRIBUTE_MESSAGE_INTEGRITY,
83
    STUN_ATTRIBUTE_ERROR_CODE,
84
    STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
85
    OLD_STUN_ATTRIBUTE_REFLECTED_FROM,
86
    STUN_ATTRIBUTE_CHANNEL_NUMBER,
87
    STUN_ATTRIBUTE_LIFETIME,
88
    STUN_ATTRIBUTE_BANDWIDTH,
89
    STUN_ATTRIBUTE_XOR_PEER_ADDRESS,
90
    STUN_ATTRIBUTE_DATA,
91
    STUN_ATTRIBUTE_REALM,
92
    STUN_ATTRIBUTE_NONCE,
93
    STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,
94
    STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY,
95
    STUN_ATTRIBUTE_EVEN_PORT,
96
    STUN_ATTRIBUTE_REQUESTED_TRANSPORT,
97
    STUN_ATTRIBUTE_DONT_FRAGMENT,
98
    STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN,
99
    STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
100
    OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
101
    STUN_ATTRIBUTE_TIMER_VAL,
102
    STUN_ATTRIBUTE_RESERVATION_TOKEN,
103
    STUN_ATTRIBUTE_PRIORITY,
104
    STUN_ATTRIBUTE_PADDING,
105
    STUN_ATTRIBUTE_RESPONSE_PORT,
106
    STUN_ATTRIBUTE_CONNECTION_ID,
107
    STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY,
108
    STUN_ATTRIBUTE_ADDRESS_ERROR_CODE,
109
    STUN_ATTRIBUTE_SOFTWARE,
110
    STUN_ATTRIBUTE_ALTERNATE_SERVER,
111
    STUN_ATTRIBUTE_FINGERPRINT,
112
    STUN_ATTRIBUTE_ICE_CONTROLLED,
113
    STUN_ATTRIBUTE_RESPONSE_ORIGIN,
114
    STUN_ATTRIBUTE_OTHER_ADDRESS,
115
    STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION,
116
    STUN_ATTRIBUTE_ORIGIN,
117
    STUN_ATTRIBUTE_MOBILITY_TICKET,
118
    STUN_ATTRIBUTE_NEW_BANDWIDTH,
119
};
120
121
static const uint16_t kAddrAttrs[] = {
122
    STUN_ATTRIBUTE_MAPPED_ADDRESS,   STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,  OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
123
    STUN_ATTRIBUTE_XOR_PEER_ADDRESS, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, STUN_ATTRIBUTE_ALTERNATE_SERVER,
124
    STUN_ATTRIBUTE_RESPONSE_ORIGIN,  STUN_ATTRIBUTE_OTHER_ADDRESS,
125
};
126
127
660
static void harness_attr_iter(const uint8_t *Data, size_t Size) {
128
660
  if (Size < STUN_HEADER_LENGTH || Size > 8192) {
129
31
    return;
130
31
  }
131
132
629
  uint8_t buf[8192];
133
629
  memcpy(buf, Data, Size);
134
135
629
  if (!stun_is_command_message_str(buf, Size)) {
136
438
    return;
137
438
  }
138
139
191
  stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
140
1.48k
  while (sar) {
141
1.29k
    (void)stun_attr_get_type(sar);
142
1.29k
    (void)stun_attr_get_len(sar);
143
1.29k
    (void)stun_attr_get_value(sar);
144
1.29k
    (void)stun_attr_is_addr(sar);
145
1.29k
    sar = stun_attr_get_next_str(buf, Size, sar);
146
1.29k
  }
147
148
191
  ioa_addr addr;
149
191
  const size_t num_addr_attrs = sizeof(kAddrAttrs) / sizeof(kAddrAttrs[0]);
150
1.71k
  for (size_t i = 0; i < num_addr_attrs; i++) {
151
1.52k
    sar = stun_attr_get_first_by_type_str(buf, Size, kAddrAttrs[i]);
152
1.52k
    if (sar) {
153
7
      memset(&addr, 0, sizeof(addr));
154
7
      stun_attr_get_addr_str(buf, Size, sar, &addr, NULL);
155
7
    }
156
1.52k
    memset(&addr, 0, sizeof(addr));
157
1.52k
    stun_attr_get_first_addr_str(buf, Size, kAddrAttrs[i], &addr, NULL);
158
1.52k
  }
159
160
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANNEL_NUMBER);
161
191
  if (sar) {
162
1
    (void)stun_attr_get_channel_number(sar);
163
1
  }
164
191
  (void)stun_attr_get_first_channel_number_str(buf, Size);
165
166
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY);
167
191
  if (sar) {
168
0
    (void)stun_get_requested_address_family(sar);
169
0
  }
170
171
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY);
172
191
  if (sar) {
173
4
    (void)stun_get_requested_address_family(sar);
174
4
  }
175
176
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_EVEN_PORT);
177
191
  if (sar) {
178
0
    (void)stun_attr_get_even_port(sar);
179
0
  }
180
181
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_BANDWIDTH);
182
191
  if (sar) {
183
2
    (void)stun_attr_get_bandwidth(sar);
184
2
  }
185
186
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_NEW_BANDWIDTH);
187
191
  if (sar) {
188
0
    (void)stun_attr_get_bandwidth(sar);
189
0
  }
190
191
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESERVATION_TOKEN);
192
191
  if (sar) {
193
0
    (void)stun_attr_get_reservation_token_value(sar);
194
0
  }
195
196
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANGE_REQUEST);
197
191
  if (sar) {
198
1
    bool change_ip = false, change_port = false;
199
1
    stun_attr_get_change_request_str(sar, &change_ip, &change_port);
200
1
  }
201
202
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESPONSE_PORT);
203
191
  if (sar) {
204
0
    (void)stun_attr_get_response_port_str(sar);
205
0
  }
206
207
191
  sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_PADDING);
208
191
  if (sar) {
209
0
    (void)stun_attr_get_padding_len_str(sar);
210
0
  }
211
212
191
  {
213
191
    int err_code = 0;
214
191
    uint8_t err_msg[1024] = {0};
215
191
    stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
216
191
  }
217
191
  {
218
191
    int err_code = 0;
219
191
    uint8_t err_msg[1024] = {0};
220
191
    uint8_t chal_realm[STUN_MAX_REALM_SIZE + 1] = {0};
221
191
    uint8_t chal_nonce[STUN_MAX_NONCE_SIZE + 1] = {0};
222
191
    uint8_t server_name[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
223
191
    bool oauth = false;
224
191
    stun_is_challenge_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg), chal_realm, chal_nonce, server_name,
225
191
                                   &oauth);
226
191
  }
227
228
191
  const size_t num_all_attrs = sizeof(kAllAttrTypes) / sizeof(kAllAttrTypes[0]);
229
8.59k
  for (size_t i = 0; i < num_all_attrs; i++) {
230
8.40k
    sar = stun_attr_get_first_by_type_str(buf, Size, kAllAttrTypes[i]);
231
8.40k
    if (sar) {
232
151
      (void)stun_attr_get_type(sar);
233
151
      (void)stun_attr_get_len(sar);
234
151
      (void)stun_attr_get_value(sar);
235
151
    }
236
8.40k
  }
237
191
}
238
239
/* ------------------------------------------------------------------ */
240
/* Attribute serialization / append paths (FuzzStunAttrAdd).          */
241
/* ------------------------------------------------------------------ */
242
660
static void harness_attr_add(const uint8_t *Data, size_t Size) {
243
660
  if (Size < STUN_HEADER_LENGTH || Size > 4096) {
244
54
    return;
245
54
  }
246
247
606
  uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0};
248
606
  memcpy(buf, Data, Size);
249
606
  size_t len = Size;
250
251
606
  if (!stun_is_command_message_str(buf, len)) {
252
418
    return;
253
418
  }
254
255
188
  uint8_t test_uname[] = "fuzzuser@fuzz.realm";
256
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_USERNAME, test_uname, (int)(sizeof(test_uname) - 1));
257
258
188
  uint8_t test_realm[] = "fuzz.realm";
259
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, test_realm, (int)(sizeof(test_realm) - 1));
260
261
188
  uint8_t test_nonce[] = "fuzznonce0123456789abcdef";
262
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, test_nonce, (int)(sizeof(test_nonce) - 1));
263
264
188
  uint8_t test_sw[] = "coturn-fuzz/1.0";
265
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_SOFTWARE, test_sw, (int)(sizeof(test_sw) - 1));
266
267
188
  uint8_t lifetime_val[4] = {0x00, 0x00, 0x02, 0x58};
268
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_LIFETIME, lifetime_val, 4);
269
270
188
  uint8_t transport_val[4] = {STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, 0x00, 0x00, 0x00};
271
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_TRANSPORT, transport_val, 4);
272
273
188
  uint8_t af_val[4] = {STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4, 0x00, 0x00, 0x00};
274
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, af_val, 4);
275
276
188
  uint8_t even_port_val[1] = {0x80};
277
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_EVEN_PORT, even_port_val, 1);
278
279
188
  stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0);
280
281
188
  stun_attr_add_channel_number_str(buf, &len, 0x4001);
282
283
188
  stun_attr_add_bandwidth_str(buf, &len, 1000000);
284
285
188
  ioa_addr addr4 = {0};
286
188
  addr4.s4.sin_family = AF_INET;
287
188
  addr4.s4.sin_port = htons(12345);
288
188
  addr4.s4.sin_addr.s_addr = htonl(0xC0A80001);
289
188
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr4);
290
188
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &addr4);
291
188
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr4);
292
188
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_MAPPED_ADDRESS, &addr4);
293
294
188
  ioa_addr addr6 = {0};
295
188
  addr6.s6.sin6_family = AF_INET6;
296
188
  addr6.s6.sin6_port = htons(54321);
297
188
  addr6.s6.sin6_addr.s6_addr[15] = 1;
298
188
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr6);
299
188
  stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr6);
300
301
188
  stun_attr_add_address_error_code(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6, 440);
302
303
188
  stun_attr_add_change_request_str(buf, &len, true, true);
304
188
  stun_attr_add_response_port_str(buf, &len, 3479);
305
188
  stun_attr_add_padding_str(buf, &len, 64);
306
307
188
  if (Size > STUN_HEADER_LENGTH + 4) {
308
163
    int data_len = (int)(Size - STUN_HEADER_LENGTH);
309
163
    if (data_len > 1024) {
310
12
      data_len = 1024;
311
12
    }
312
163
    stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DATA, Data + STUN_HEADER_LENGTH, data_len);
313
163
  }
314
315
188
  stun_attr_add_fingerprint_str(buf, &len);
316
188
}
317
318
/* ------------------------------------------------------------------ */
319
/* Legacy (pre-RFC 5389) STUN detection (FuzzOldStun).                */
320
/* ------------------------------------------------------------------ */
321
660
static void harness_old_stun(const uint8_t *Data, size_t Size) {
322
660
  if (Size < STUN_HEADER_LENGTH || Size > 5120) {
323
33
    return;
324
33
  }
325
326
627
  uint8_t buf[5120];
327
627
  memcpy(buf, Data, Size);
328
329
627
  uint32_t cookie = 0;
330
627
  bool is_old = old_stun_is_command_message_str(buf, Size, &cookie);
331
627
  (void)stun_is_command_message_str(buf, Size);
332
333
627
  if (is_old) {
334
166
    (void)stun_get_msg_type_str(buf, Size);
335
166
    (void)stun_get_method_str(buf, Size);
336
337
166
    stun_is_request_str(buf, Size);
338
166
    stun_is_indication_str(buf, Size);
339
166
    stun_is_success_response_str(buf, Size);
340
341
166
    int err_code = 0;
342
166
    uint8_t err_msg[256] = {0};
343
166
    stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
344
345
166
    int fp_present = 0;
346
166
    stun_is_command_message_full_check_str(buf, Size, 1, &fp_present);
347
166
    stun_is_command_message_full_check_str(buf, Size, 0, &fp_present);
348
349
166
    stun_is_binding_request_str(buf, Size, 0);
350
166
    stun_is_binding_response_str(buf, Size);
351
352
166
    stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
353
404
    while (sar) {
354
238
      (void)stun_attr_get_type(sar);
355
238
      (void)stun_attr_get_len(sar);
356
238
      sar = stun_attr_get_next_str(buf, Size, sar);
357
238
    }
358
166
  }
359
627
}
360
361
/* ------------------------------------------------------------------ */
362
/* Challenge-response builder (FuzzStunChallengeResponse).            */
363
/*                                                                    */
364
/* stun_is_challenge_response_str only descends into its three inner  */
365
/* stun_attr_get_first_by_type_str calls when:                        */
366
/*   1) the message is an error response,                             */
367
/*   2) err_code is 401 OR 438,                                       */
368
/*   3) a REALM attribute is present,                                 */
369
/*   4) a NONCE attribute is present.                                 */
370
/* The OAuth branch additionally requires THIRD-PARTY-AUTHORIZATION.  */
371
/*                                                                    */
372
/* The fuzzer-driven harness in harness_attr_iter calls the function  */
373
/* every iteration but the conjunction of conditions is too specific  */
374
/* for libFuzzer to discover from binary mutation alone — OSS-Fuzz    */
375
/* introspector flags 9 unreached callsites under                     */
376
/* stun_attr_get_first_by_type_str gated on this function.            */
377
/*                                                                    */
378
/* Build six deterministic message variants on every iteration so     */
379
/* every internal branch is exercised regardless of the input bytes.  */
380
/* Realm / nonce / server-name lengths are derived from fuzz bytes so */
381
/* each iteration is still meaningfully distinct.                     */
382
/* ------------------------------------------------------------------ */
383
660
static void harness_challenge_response_builder(const uint8_t *Data, size_t Size) {
384
660
  if (!Size) {
385
0
    return;
386
0
  }
387
388
660
  static const uint16_t kMethods[] = {
389
660
      STUN_METHOD_ALLOCATE,
390
660
      STUN_METHOD_BINDING,
391
660
      STUN_METHOD_REFRESH,
392
660
      STUN_METHOD_CHANNEL_BIND,
393
660
  };
394
660
  const uint16_t method = kMethods[Data[0] % (sizeof(kMethods) / sizeof(kMethods[0]))];
395
396
  /* Length of each variable-length string is taken from a single fuzz byte
397
   * so libFuzzer still varies the inputs across iterations. Floors keep the
398
   * attribute non-empty so the inner copies in stun_is_challenge_response_str
399
   * actually populate the output buffers. */
400
660
  const size_t realm_len = 1 + (Size > 1 ? (Data[1] % 16) : 0);
401
660
  const size_t nonce_len = 1 + (Size > 2 ? (Data[2] % 16) : 0);
402
660
  const size_t server_len = 1 + (Size > 3 ? (Data[3] % 16) : 0);
403
404
660
  uint8_t realm_buf[STUN_MAX_REALM_SIZE + 1] = {0};
405
660
  uint8_t nonce_buf[STUN_MAX_NONCE_SIZE + 1] = {0};
406
660
  uint8_t server_buf[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
407
4.35k
  for (size_t i = 0; i < realm_len; ++i) {
408
3.69k
    realm_buf[i] = (uint8_t)('a' + (i % 26));
409
3.69k
  }
410
2.57k
  for (size_t i = 0; i < nonce_len; ++i) {
411
1.91k
    nonce_buf[i] = (uint8_t)('0' + (i % 10));
412
1.91k
  }
413
5.52k
  for (size_t i = 0; i < server_len; ++i) {
414
4.86k
    server_buf[i] = (uint8_t)('A' + (i % 26));
415
4.86k
  }
416
417
660
  stun_tid tid = {0};
418
8.58k
  for (size_t i = 0; i < STUN_TID_SIZE; ++i) {
419
7.92k
    tid.tsx_id[i] = (uint8_t)(Size > i + 4 ? Data[i + 4] : (uint8_t)i);
420
7.92k
  }
421
422
  /* Each variant builds a fresh error response and runs it through the
423
   * predicate so the inner attribute lookups fire. */
424
660
  static const struct {
425
660
    uint16_t err_code;
426
660
    bool include_realm;
427
660
    bool include_nonce;
428
660
    bool include_third_party_auth;
429
660
  } kVariants[] = {
430
660
      {401, true, true, false},   /* canonical 401 challenge: REALM + NONCE */
431
660
      {401, true, true, true},    /* same + THIRD-PARTY-AUTHORIZATION (OAuth branch) */
432
660
      {438, true, true, false},   /* covers the (*err_code) == 438 disjunct */
433
660
      {401, true, false, false},  /* REALM present, NONCE missing — negative inner path */
434
660
      {401, false, false, false}, /* err_code matches but no REALM — outer negative path */
435
660
      {400, true, true, false},   /* err_code does not match 401/438 — early-out path */
436
660
  };
437
438
4.62k
  for (size_t v = 0; v < sizeof(kVariants) / sizeof(kVariants[0]); ++v) {
439
3.96k
    uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0};
440
3.96k
    size_t len = 0;
441
3.96k
    stun_init_error_response_str(method, buf, &len, kVariants[v].err_code, (const uint8_t *)"unauthorized", &tid, true);
442
443
3.96k
    if (kVariants[v].include_realm) {
444
3.30k
      stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, realm_buf, (int)realm_len);
445
3.30k
    }
446
3.96k
    if (kVariants[v].include_third_party_auth) {
447
660
      stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION, server_buf, (int)server_len);
448
660
    }
449
3.96k
    if (kVariants[v].include_nonce) {
450
2.64k
      stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, nonce_buf, (int)nonce_len);
451
2.64k
    }
452
453
3.96k
    int err_code = 0;
454
3.96k
    uint8_t err_msg[1024] = {0};
455
3.96k
    uint8_t out_realm[STUN_MAX_REALM_SIZE + 1] = {0};
456
3.96k
    uint8_t out_nonce[STUN_MAX_NONCE_SIZE + 1] = {0};
457
3.96k
    uint8_t out_server[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
458
3.96k
    bool oauth = false;
459
3.96k
    (void)stun_is_challenge_response_str(buf, len, &err_code, err_msg, sizeof(err_msg), out_realm, out_nonce,
460
3.96k
                                         out_server, &oauth);
461
462
    /* Also exercise the NULL-oauth-pointer branch, which is reachable from
463
     * other call sites in the codebase. */
464
3.96k
    (void)stun_is_challenge_response_str(buf, len, &err_code, err_msg, sizeof(err_msg), out_realm, out_nonce,
465
3.96k
                                         out_server, NULL);
466
3.96k
  }
467
660
}
468
469
/* ------------------------------------------------------------------ */
470
/* libFuzzer entry point — run every harness on each input.           */
471
/* ------------------------------------------------------------------ */
472
978
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
473
978
  harness_integrity_sha1(Data, Size);
474
978
  harness_integrity_multi(Data, Size);
475
978
  harness_attr_iter(Data, Size);
476
978
  harness_attr_add(Data, Size);
477
978
  harness_old_stun(Data, Size);
478
978
  harness_challenge_response_builder(Data, Size);
479
978
  return 0;
480
978
}