Files
coturn/fuzzing/FuzzStunClient.c
Pavel Punsky b4c138c409 Cover all public stun_buffer.c wrappers in FuzzStunClient (#1883)
Add harness_stun_buffer_api to FuzzStunClient.c that exercises every
public wrapper in src/apps/common/stun_buffer.c not already reached by
the existing harnesses: stun_get_size (NULL/non-NULL), the init_request
/ init_indication / init_success_response builders, the tid accessors,
the stun_is_indication wrapper (which gates the static is_channel_msg),
the attr_add / attr_add_channel_number / attr_add_addr /
attr_add_even_port (both branches) / attr_get_first_by_type accessors,
stun_set_allocate_request (rt NULL and non-NULL paths),
stun_set_binding_request /
stun_prepare_binding_request, and the channel-message wrappers.

Each builder call is followed by inspect_buffer_message so the resulting
serialized message is also walked by the parser predicates. A tail block
also pumps raw fuzzer bytes through the wrapper-form predicates
(stun_is_indication, stun_is_channel_message, stun_tid_from_message,
stun_attr_get_first_by_type) so they see malformed inputs the serializer
paths cannot produce.
2026-04-26 11:28:28 -07:00

826 lines
32 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* SPDX-License-Identifier: BSD-3-Clause
*
* https://opensource.org/license/bsd-3-clause
*
* Multi-harness libFuzzer entry point for client-side STUN parsing,
* TCP framing, and address codec.
*
* Every iteration runs all sub-harnesses in sequence on the same input.
* Keeping everything behind a single binary allows the upstream OSS-Fuzz
* build recipe (which only copies FuzzStun and FuzzStunClient) to stay
* unchanged.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "apputils.h"
#include "ns_turn_msg.h"
#include "ns_turn_msg_addr.h"
#include "ns_turn_msg_defs.h"
#include "ns_turn_utils.h"
#include "stun_buffer.h"
static uint8_t fuzz_byte(const uint8_t *Data, size_t Size, size_t idx) { return Size ? Data[idx % Size] : 0; }
static uint16_t fuzz_u16(const uint8_t *Data, size_t Size, size_t idx) {
return (uint16_t)(((uint16_t)fuzz_byte(Data, Size, idx) << 8) | (uint16_t)fuzz_byte(Data, Size, idx + 1));
}
static uint32_t fuzz_u32(const uint8_t *Data, size_t Size, size_t idx) {
return ((uint32_t)fuzz_u16(Data, Size, idx) << 16) | (uint32_t)fuzz_u16(Data, Size, idx + 2);
}
static uint64_t fuzz_u64(const uint8_t *Data, size_t Size, size_t idx) {
return ((uint64_t)fuzz_u32(Data, Size, idx) << 32) | (uint64_t)fuzz_u32(Data, Size, idx + 4);
}
static bool fuzz_flag(const uint8_t *Data, size_t Size, size_t idx) { return (fuzz_byte(Data, Size, idx) & 1u) != 0; }
static void fuzz_string(const uint8_t *Data, size_t Size, size_t idx, char *out, size_t out_size) {
if (!out || !out_size) {
return;
}
const size_t max_len = out_size - 1;
const size_t len = max_len ? (size_t)(fuzz_byte(Data, Size, idx) % (max_len + 1)) : 0;
for (size_t i = 0; i < len; ++i) {
out[i] = (char)('A' + (fuzz_byte(Data, Size, idx + 1 + i) % 26));
}
out[len] = '\0';
}
static void fuzz_tid(const uint8_t *Data, size_t Size, size_t idx, stun_tid *tid) {
if (!tid) {
return;
}
memset(tid, 0, sizeof(*tid));
for (size_t i = 0; i < STUN_TID_SIZE; ++i) {
tid->tsx_id[i] = fuzz_byte(Data, Size, idx + i);
}
}
static void fuzz_addr(const uint8_t *Data, size_t Size, size_t idx, ioa_addr *addr) {
if (!addr) {
return;
}
memset(addr, 0, sizeof(*addr));
if (fuzz_flag(Data, Size, idx)) {
addr->s6.sin6_family = AF_INET6;
addr->s6.sin6_port = htons(fuzz_u16(Data, Size, idx + 1));
for (size_t i = 0; i < 16; ++i) {
addr->s6.sin6_addr.s6_addr[i] = fuzz_byte(Data, Size, idx + 3 + i);
}
if (!memcmp(addr->s6.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) {
addr->s6.sin6_addr.s6_addr[15] = 1;
}
} else {
addr->s4.sin_family = AF_INET;
addr->s4.sin_port = htons(fuzz_u16(Data, Size, idx + 1));
addr->s4.sin_addr.s_addr = htonl(fuzz_u32(Data, Size, idx + 3) | 1u);
}
}
static void inspect_buffer_message(stun_buffer *msg, uint16_t addr_attr_type, const ioa_addr *default_addr) {
if (!msg) {
return;
}
(void)stun_get_command_message_len(msg);
(void)stun_is_command_message(msg);
(void)stun_is_request(msg);
(void)stun_is_response(msg);
(void)stun_is_success_response(msg);
(void)stun_is_binding_response(msg);
(void)stun_get_method(msg);
(void)stun_get_msg_type(msg);
{
int err_code = 0;
uint8_t err_msg[256] = {0};
(void)stun_is_error_response(msg, &err_code, err_msg, sizeof(err_msg));
}
{
ioa_addr parsed = {0};
(void)stun_attr_get_first_addr(msg, addr_attr_type, &parsed, default_addr);
}
{
stun_attr_ref attr = stun_attr_get_first(msg);
while (attr) {
(void)stun_attr_get_type(attr);
(void)stun_attr_get_len(attr);
if (stun_attr_is_addr(attr)) {
ioa_addr parsed = {0};
(void)stun_attr_get_addr(msg, attr, &parsed, default_addr);
}
attr = stun_attr_get_next(msg, attr);
}
}
(void)stun_attr_get_first_channel_number(msg);
}
static void inspect_raw_message(const uint8_t *buf, size_t len, uint16_t addr_attr_type, const ioa_addr *default_addr) {
if (!buf || !len) {
return;
}
(void)stun_is_command_message_str((uint8_t *)buf, len);
(void)stun_is_request_str(buf, len);
(void)stun_is_response_str(buf, len);
(void)stun_is_success_response_str(buf, len);
(void)stun_is_binding_response_str(buf, len);
(void)stun_get_method_str(buf, len);
(void)stun_get_msg_type_str(buf, len);
{
int err_code = 0;
uint8_t err_msg[256] = {0};
(void)stun_is_error_response_str(buf, len, &err_code, err_msg, sizeof(err_msg));
}
{
ioa_addr parsed = {0};
(void)stun_attr_get_first_addr_str(buf, len, addr_attr_type, &parsed, default_addr);
}
{
stun_attr_ref attr = stun_attr_get_first_str(buf, len);
while (attr) {
(void)stun_attr_get_type(attr);
(void)stun_attr_get_len(attr);
if (stun_attr_is_addr(attr)) {
ioa_addr parsed = {0};
(void)stun_attr_get_addr_str(buf, len, attr, &parsed, default_addr);
}
attr = stun_attr_get_next_str(buf, len, attr);
}
}
(void)stun_attr_get_first_channel_number_str(buf, len);
}
/* ------------------------------------------------------------------ */
/* Raw-input coverage for stun_attr_get_first_addr. */
/* */
/* The inspect_buffer_message() path only sees messages produced by */
/* the project's own serializers, which are always well-formed. This */
/* harness feeds arbitrary fuzzer input through the stun_buffer */
/* wrapper (not just the _str variant) for every address attribute */
/* type, with both NULL and a fuzzed default_addr fallback. */
/* ------------------------------------------------------------------ */
static const uint16_t kFirstAddrAttrs[] = {
STUN_ATTRIBUTE_MAPPED_ADDRESS, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
STUN_ATTRIBUTE_XOR_PEER_ADDRESS, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, STUN_ATTRIBUTE_ALTERNATE_SERVER,
STUN_ATTRIBUTE_RESPONSE_ORIGIN, STUN_ATTRIBUTE_OTHER_ADDRESS,
};
static void harness_attr_get_first_addr(const uint8_t *Data, size_t Size) {
if (Size < STUN_HEADER_LENGTH || Size > 5120) {
return;
}
stun_buffer msg;
msg.len = Size;
memcpy(msg.buf, Data, Size);
ioa_addr default_addr = {0};
fuzz_addr(Data, Size, 0, &default_addr);
const size_t num_attrs = sizeof(kFirstAddrAttrs) / sizeof(kFirstAddrAttrs[0]);
for (size_t i = 0; i < num_attrs; ++i) {
ioa_addr parsed = {0};
(void)stun_attr_get_first_addr(&msg, kFirstAddrAttrs[i], &parsed, NULL);
memset(&parsed, 0, sizeof(parsed));
(void)stun_attr_get_first_addr(&msg, kFirstAddrAttrs[i], &parsed, &default_addr);
}
}
/* ------------------------------------------------------------------ */
/* stun_buffer-based client message parsing (original FuzzStunClient). */
/* ------------------------------------------------------------------ */
static void harness_stun_client(const uint8_t *Data, size_t Size) {
if (Size < 10 || Size > 5120) {
return;
}
stun_buffer buf;
buf.len = Size;
memcpy(buf.buf, Data, buf.len);
if (!stun_is_command_message(&buf)) {
return;
}
(void)stun_get_method_str(buf.buf, buf.len);
(void)stun_get_msg_type_str(buf.buf, buf.len);
if (stun_is_response(&buf) && stun_is_success_response(&buf) && stun_is_binding_response(&buf)) {
return;
}
stun_is_indication_str(buf.buf, buf.len);
int err_code = 0;
uint8_t err_msg[256] = {0};
stun_is_error_response_str(buf.buf, buf.len, &err_code, err_msg, sizeof(err_msg));
}
/* ------------------------------------------------------------------ */
/* ChannelData / TCP framing (FuzzChannelData). */
/* ------------------------------------------------------------------ */
static void harness_channel_data(const uint8_t *Data, size_t Size) {
if (Size < 4 || Size > 8192) {
return;
}
uint8_t buf[8192] = {0};
memcpy(buf, Data, Size);
size_t app_len_tcp = 0;
size_t app_len_udp = 0;
int mlen_tcp = stun_get_message_len_str(buf, Size, 1, &app_len_tcp);
int mlen_udp = stun_get_message_len_str(buf, Size, 0, &app_len_udp);
if (mlen_tcp > 0) {
if (app_len_tcp > Size) {
__builtin_trap();
}
if ((size_t)mlen_tcp > Size) {
__builtin_trap();
}
if ((size_t)mlen_tcp < app_len_tcp) {
__builtin_trap();
}
}
if (mlen_udp > 0) {
if (app_len_udp > Size) {
__builtin_trap();
}
if ((size_t)mlen_udp > Size) {
__builtin_trap();
}
}
size_t blen_tcp = Size;
uint16_t chn_tcp = 0;
bool is_chan_tcp = stun_is_channel_message_str(buf, &blen_tcp, &chn_tcp, true);
size_t blen_udp = Size;
uint16_t chn_udp = 0;
bool is_chan_udp = stun_is_channel_message_str(buf, &blen_udp, &chn_udp, false);
if (is_chan_tcp && (blen_tcp < 4 || blen_tcp > Size)) {
__builtin_trap();
}
if (is_chan_udp && (blen_udp < 4 || blen_udp > Size)) {
__builtin_trap();
}
}
/* ------------------------------------------------------------------ */
/* STUN address encode/decode (FuzzStunAddrCodec). */
/* ------------------------------------------------------------------ */
static void harness_addr_codec(const uint8_t *Data, size_t Size) {
if (Size < 2 || Size > 64) {
return;
}
uint8_t tid[STUN_TID_SIZE] = {0};
size_t tid_bytes = Size > (STUN_TID_SIZE + 2) ? STUN_TID_SIZE : (Size > 2 ? Size - 2 : 0);
memcpy(tid, Data, tid_bytes);
const uint8_t *payload = Data + tid_bytes;
int payload_len = (int)(Size - tid_bytes);
ioa_addr addr = {0};
/* XOR decode + round-trip */
if (stun_addr_decode(&addr, payload, payload_len, 1, STUN_MAGIC_COOKIE, tid) == 0) {
uint8_t enc_buf[32] = {0};
int enc_len = 0;
if (stun_addr_encode(&addr, enc_buf, &enc_len, 1, STUN_MAGIC_COOKIE, tid) == 0) {
ioa_addr addr2 = {0};
stun_addr_decode(&addr2, enc_buf, enc_len, 1, STUN_MAGIC_COOKIE, tid);
}
}
/* Plain decode + round-trip */
memset(&addr, 0, sizeof(addr));
if (stun_addr_decode(&addr, payload, payload_len, 0, 0, tid) == 0) {
uint8_t enc_buf[32] = {0};
int enc_len = 0;
if (stun_addr_encode(&addr, enc_buf, &enc_len, 0, 0, tid) == 0) {
ioa_addr addr2 = {0};
stun_addr_decode(&addr2, enc_buf, enc_len, 0, 0, tid);
}
}
/* Alternate magic cookie (old STUN) */
memset(&addr, 0, sizeof(addr));
uint32_t alt_cookie = 0;
if (Size >= 4) {
memcpy(&alt_cookie, Data, 4);
}
(void)stun_addr_decode(&addr, payload, payload_len, 1, alt_cookie, tid);
}
/* ------------------------------------------------------------------ */
/* Message builders / wrappers / round-trip parsing. */
/* ------------------------------------------------------------------ */
static void harness_message_builders(const uint8_t *Data, size_t Size) {
if (!Size || Size > 4096) {
return;
}
static const uint16_t kMethods[] = {
STUN_METHOD_ALLOCATE, STUN_METHOD_BINDING, STUN_METHOD_CHANNEL_BIND, STUN_METHOD_REFRESH, STUN_METHOD_CONNECT,
};
static const uint16_t kErrorCodes[] = {
300, 400, 401, 403, 420, 437, 438, 440, 441, 442, 443, 446, 447, 486, 487, 500, 508, 699,
};
stun_tid tid = {0};
ioa_addr relay1 = {0};
ioa_addr relay2 = {0};
ioa_addr reflexive = {0};
ioa_addr peer = {0};
ioa_addr default_addr = {0};
char reason[96] = {0};
char mobile_id[96] = {0};
uint8_t raw[MAX_STUN_MESSAGE_SIZE] = {0};
fuzz_tid(Data, Size, 0, &tid);
fuzz_addr(Data, Size, 16, &relay1);
fuzz_addr(Data, Size, 40, &relay2);
fuzz_addr(Data, Size, 64, &reflexive);
fuzz_addr(Data, Size, 88, &peer);
fuzz_addr(Data, Size, 112, &default_addr);
fuzz_string(Data, Size, 136, reason, sizeof(reason));
fuzz_string(Data, Size, 232, mobile_id, sizeof(mobile_id));
const uint16_t method = kMethods[fuzz_byte(Data, Size, 328) % (sizeof(kMethods) / sizeof(kMethods[0]))];
const uint16_t error_code = kErrorCodes[fuzz_byte(Data, Size, 329) % (sizeof(kErrorCodes) / sizeof(kErrorCodes[0]))];
const uint32_t lifetime = fuzz_u32(Data, Size, 330);
const uint32_t max_lifetime = fuzz_u32(Data, Size, 334);
const uint64_t reservation_token = fuzz_u64(Data, Size, 338);
const uint16_t channel_number = fuzz_u16(Data, Size, 346);
const bool include_reason = fuzz_flag(Data, Size, 348);
const bool old_stun = fuzz_flag(Data, Size, 349);
const bool stun_backward_compatibility = fuzz_flag(Data, Size, 350);
const uint32_t old_cookie = fuzz_u32(Data, Size, 351);
/* Direct wrapper coverage for stun_init_error_response(). */
{
stun_buffer msg;
stun_init_buffer(&msg);
stun_init_error_response(method, &msg, error_code, reason[0] ? (const uint8_t *)reason : NULL, &tid,
include_reason);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
}
/* Success allocate response covers addr extraction; error allocate response
* forces the shared error builder path. */
{
stun_buffer msg;
stun_init_buffer(&msg);
(void)stun_set_allocate_response(&msg, &tid, &relay1, fuzz_flag(Data, Size, 355) ? &relay2 : NULL, &reflexive,
lifetime, max_lifetime, 0, (const uint8_t *)reason, reservation_token, mobile_id,
include_reason);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
size_t raw_len = sizeof(raw);
(void)stun_set_allocate_response_str(raw, &raw_len, &tid, &relay1, &relay2, &reflexive, lifetime, max_lifetime, 0,
(const uint8_t *)reason, reservation_token, mobile_id, include_reason);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
stun_init_buffer(&msg);
(void)stun_set_allocate_response(&msg, &tid, NULL, NULL, NULL, lifetime, max_lifetime, error_code,
reason[0] ? (const uint8_t *)reason : NULL, reservation_token, mobile_id,
include_reason);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
raw_len = sizeof(raw);
(void)stun_set_allocate_response_str(raw, &raw_len, &tid, NULL, NULL, NULL, lifetime, max_lifetime, error_code,
reason[0] ? (const uint8_t *)reason : NULL, reservation_token, mobile_id,
include_reason);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
}
{
stun_buffer msg;
stun_init_buffer(&msg);
(void)stun_set_binding_response(&msg, &tid, &reflexive, 0, (const uint8_t *)reason, include_reason);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &default_addr);
size_t raw_len = sizeof(raw);
(void)stun_set_binding_response_str(raw, &raw_len, &tid, &reflexive, 0, (const uint8_t *)reason, old_cookie,
old_stun, stun_backward_compatibility, include_reason);
inspect_raw_message(raw, raw_len, old_stun ? STUN_ATTRIBUTE_MAPPED_ADDRESS : STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
&default_addr);
stun_init_buffer(&msg);
(void)stun_set_binding_response(&msg, &tid, NULL, error_code, reason[0] ? (const uint8_t *)reason : NULL,
include_reason);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
raw_len = sizeof(raw);
(void)stun_set_binding_response_str(raw, &raw_len, &tid, NULL, error_code,
reason[0] ? (const uint8_t *)reason : NULL, old_cookie, old_stun,
stun_backward_compatibility, include_reason);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
}
{
stun_buffer msg;
stun_init_buffer(&msg);
(void)stun_set_channel_bind_request(&msg, fuzz_flag(Data, Size, 356) ? &peer : NULL, channel_number);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
size_t raw_len = sizeof(raw);
(void)stun_set_channel_bind_request_str(raw, &raw_len, fuzz_flag(Data, Size, 357) ? &peer : NULL, channel_number);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
stun_init_buffer(&msg);
stun_set_channel_bind_response(&msg, &tid, 0, (const uint8_t *)reason, include_reason);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
raw_len = sizeof(raw);
stun_set_channel_bind_response_str(raw, &raw_len, &tid, error_code, reason[0] ? (const uint8_t *)reason : NULL,
include_reason);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
}
}
/* ------------------------------------------------------------------ */
/* Deterministic branch coverage for the response builders. */
/* */
/* harness_message_builders randomizes include_reason/old_stun/NULL- */
/* vs-present selectors from single input bytes, so any one iteration */
/* only visits a fraction of the branches inside these builders. This */
/* harness hits every branch point of each listed builder on every */
/* iteration so coverage is not gated on libFuzzer finding the right */
/* bytes. Inputs (tid / addrs / strings / numeric fields) are still */
/* fuzz-derived to keep each call meaningfully distinct. */
/* ------------------------------------------------------------------ */
static void harness_response_matrix(const uint8_t *Data, size_t Size) {
if (!Size || Size > 4096) {
return;
}
stun_tid tid = {0};
ioa_addr relay1 = {0};
ioa_addr relay2 = {0};
ioa_addr reflexive = {0};
ioa_addr peer = {0};
ioa_addr default_addr = {0};
char reason[96] = {0};
char mobile_id[96] = {0};
uint8_t raw[MAX_STUN_MESSAGE_SIZE] = {0};
fuzz_tid(Data, Size, 0, &tid);
fuzz_addr(Data, Size, 16, &relay1);
fuzz_addr(Data, Size, 40, &relay2);
fuzz_addr(Data, Size, 64, &reflexive);
fuzz_addr(Data, Size, 88, &peer);
fuzz_addr(Data, Size, 112, &default_addr);
fuzz_string(Data, Size, 136, reason, sizeof(reason));
fuzz_string(Data, Size, 232, mobile_id, sizeof(mobile_id));
const uint32_t max_lifetime = fuzz_u32(Data, Size, 328) | 1u;
const uint64_t reservation_token = fuzz_u64(Data, Size, 332) | 1ull;
const uint16_t channel_number_valid = (uint16_t)(0x4000u + (fuzz_u16(Data, Size, 340) % (0x7FFFu - 0x4000u + 1u)));
const uint32_t old_cookie = fuzz_u32(Data, Size, 344);
/* stun_init_error_response — cover (reason NULL vs set) × (include reason). */
{
stun_buffer msg;
stun_init_buffer(&msg);
stun_init_error_response(STUN_METHOD_ALLOCATE, &msg, 437, NULL, &tid, false);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
stun_init_buffer(&msg);
stun_init_error_response(STUN_METHOD_BINDING, &msg, 400, (const uint8_t *)reason, &tid, true);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
}
/* stun_set_allocate_response / _str — cover every optional-field branch and
* the error path independently of the fuzzer selectors. */
{
stun_buffer msg;
/* Minimal success: relay1 only, no reflexive, no reservation, no mobile id,
* lifetime 0 (triggers the <1 default branch). */
stun_init_buffer(&msg);
(void)stun_set_allocate_response(&msg, &tid, &relay1, NULL, NULL, 0, max_lifetime, 0, NULL, 0, NULL, false);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
size_t raw_len = sizeof(raw);
(void)stun_set_allocate_response_str(raw, &raw_len, &tid, &relay1, NULL, NULL, 0, max_lifetime, 0, NULL, 0, NULL,
false);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
/* Full success: both relays + reflexive + reservation + mobile id,
* lifetime > max (triggers clamp branch). */
stun_init_buffer(&msg);
(void)stun_set_allocate_response(&msg, &tid, &relay1, &relay2, &reflexive, max_lifetime + 1, max_lifetime, 0,
(const uint8_t *)reason, reservation_token, mobile_id, true);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
raw_len = sizeof(raw);
(void)stun_set_allocate_response_str(raw, &raw_len, &tid, &relay1, &relay2, &reflexive, max_lifetime + 1,
max_lifetime, 0, (const uint8_t *)reason, reservation_token, mobile_id, true);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
/* Error path with and without a reason string. */
stun_init_buffer(&msg);
(void)stun_set_allocate_response(&msg, &tid, NULL, NULL, NULL, 0, max_lifetime, 441, NULL, 0, NULL, false);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
raw_len = sizeof(raw);
(void)stun_set_allocate_response_str(raw, &raw_len, &tid, NULL, NULL, NULL, 0, max_lifetime, 508,
(const uint8_t *)reason, 0, NULL, true);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
}
/* stun_set_binding_response / _str — cover success × error × old_stun. */
{
stun_buffer msg;
stun_init_buffer(&msg);
(void)stun_set_binding_response(&msg, &tid, &reflexive, 0, NULL, false);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &default_addr);
stun_init_buffer(&msg);
(void)stun_set_binding_response(&msg, &tid, NULL, 420, (const uint8_t *)reason, true);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
const bool matrix_old_stun[] = {false, true};
const bool matrix_backcompat[] = {false, true};
for (size_t o = 0; o < sizeof(matrix_old_stun) / sizeof(matrix_old_stun[0]); ++o) {
for (size_t b = 0; b < sizeof(matrix_backcompat) / sizeof(matrix_backcompat[0]); ++b) {
size_t raw_len = sizeof(raw);
(void)stun_set_binding_response_str(raw, &raw_len, &tid, &reflexive, 0, NULL, old_cookie, matrix_old_stun[o],
matrix_backcompat[b], false);
inspect_raw_message(raw, raw_len,
matrix_old_stun[o] ? STUN_ATTRIBUTE_MAPPED_ADDRESS : STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
&default_addr);
raw_len = sizeof(raw);
(void)stun_set_binding_response_str(raw, &raw_len, &tid, NULL, 500, (const uint8_t *)reason, old_cookie,
matrix_old_stun[o], matrix_backcompat[b], true);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
}
}
}
/* stun_set_channel_bind_request / _str — cover peer NULL vs set. */
{
stun_buffer msg;
stun_init_buffer(&msg);
(void)stun_set_channel_bind_request(&msg, NULL, channel_number_valid);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
stun_init_buffer(&msg);
(void)stun_set_channel_bind_request(&msg, &peer, channel_number_valid);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
size_t raw_len = sizeof(raw);
(void)stun_set_channel_bind_request_str(raw, &raw_len, NULL, channel_number_valid);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
raw_len = sizeof(raw);
(void)stun_set_channel_bind_request_str(raw, &raw_len, &peer, channel_number_valid);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
}
/* stun_set_channel_bind_response / _str — cover success vs error. */
{
stun_buffer msg;
stun_init_buffer(&msg);
stun_set_channel_bind_response(&msg, &tid, 0, NULL, false);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
stun_init_buffer(&msg);
stun_set_channel_bind_response(&msg, &tid, 438, (const uint8_t *)reason, true);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
size_t raw_len = sizeof(raw);
stun_set_channel_bind_response_str(raw, &raw_len, &tid, 0, NULL, false);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
raw_len = sizeof(raw);
stun_set_channel_bind_response_str(raw, &raw_len, &tid, 486, (const uint8_t *)reason, true);
inspect_raw_message(raw, raw_len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
}
}
/* ------------------------------------------------------------------ */
/* stun_buffer.c wrapper coverage. */
/* */
/* Exercises every public wrapper in src/apps/common/stun_buffer.c */
/* that is not already reached by the harnesses above: */
/* - stun_get_size NULL/non-NULL */
/* - stun_init_request / _indication / _success_response */
/* - stun_tid_from_message, stun_tid_generate_in_message */
/* - stun_is_indication wrapper (gates static is_channel_msg) */
/* - stun_attr_add, stun_attr_add_channel_number, stun_attr_add_addr */
/* - stun_attr_add_even_port (zero + non-zero branches) */
/* - stun_attr_get_first_by_type */
/* - stun_set_allocate_request (rt NULL + non-NULL) */
/* - stun_set_binding_request, stun_prepare_binding_request */
/* - stun_init_channel_message + stun_is_channel_message wrappers */
/* */
/* Each call is followed by inspect_buffer_message so the resulting */
/* serialized message is also walked by the parser predicates. */
/* The tail block also pumps raw fuzzer bytes through the predicate */
/* wrappers to hit malformed-input branches the serializers cannot */
/* produce. */
/* ------------------------------------------------------------------ */
static void harness_stun_buffer_api(const uint8_t *Data, size_t Size) {
if (!Size || Size > 4096) {
return;
}
static const uint16_t kMethods[] = {
STUN_METHOD_ALLOCATE, STUN_METHOD_BINDING, STUN_METHOD_CHANNEL_BIND, STUN_METHOD_REFRESH, STUN_METHOD_CONNECT,
};
stun_tid tid = {0};
ioa_addr peer = {0};
ioa_addr default_addr = {0};
char attr_value[64] = {0};
char rt[8] = {0};
fuzz_tid(Data, Size, 0, &tid);
fuzz_addr(Data, Size, 16, &peer);
fuzz_addr(Data, Size, 40, &default_addr);
fuzz_string(Data, Size, 64, attr_value, sizeof(attr_value));
fuzz_string(Data, Size, 128, rt, sizeof(rt));
const uint16_t method = kMethods[fuzz_byte(Data, Size, 200) % (sizeof(kMethods) / sizeof(kMethods[0]))];
const uint32_t lifetime = fuzz_u32(Data, Size, 201);
const uint16_t channel_number = (uint16_t)(0x4000u + (fuzz_u16(Data, Size, 205) & 0x3FFFu));
const uint8_t transport = fuzz_byte(Data, Size, 207);
const uint8_t even_port_value = fuzz_byte(Data, Size, 208);
const bool af4 = fuzz_flag(Data, Size, 209);
const bool af6 = fuzz_flag(Data, Size, 210);
const bool mobile = fuzz_flag(Data, Size, 211);
const bool padding = fuzz_flag(Data, Size, 212);
const int chan_payload_len = (int)(fuzz_u16(Data, Size, 213) % 256);
const int ep = (int)(int8_t)fuzz_byte(Data, Size, 215);
/* NULL-guard branches. */
(void)stun_get_size(NULL);
(void)stun_init_buffer(NULL);
(void)stun_get_msg_type(NULL);
{
stun_tid scratch = {0};
stun_tid_generate_in_message(NULL, &scratch);
}
/* stun_init_request — also covers stun_get_size (non-NULL), the static
* stun_init_command helper, and stun_attr_add* / stun_attr_get_first_by_type
* over the freshly built message. */
{
stun_buffer msg;
stun_init_buffer(&msg);
stun_init_request(method, &msg);
stun_tid extracted = {0};
stun_tid_from_message(&msg, &extracted);
stun_tid_generate_in_message(&msg, &extracted);
const int alen = (int)strlen(attr_value);
(void)stun_attr_add(&msg, STUN_ATTRIBUTE_USERNAME, attr_value, alen);
(void)stun_attr_add_channel_number(&msg, channel_number);
(void)stun_attr_add_addr(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &peer);
(void)stun_attr_add_even_port(&msg, even_port_value);
(void)stun_attr_add_even_port(&msg, 0);
(void)stun_attr_get_first_by_type(&msg, STUN_ATTRIBUTE_USERNAME);
(void)stun_attr_get_first_by_type(&msg, STUN_ATTRIBUTE_CHANNEL_NUMBER);
(void)stun_attr_get_first_by_type(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS);
(void)stun_attr_get_first_by_type(&msg, STUN_ATTRIBUTE_EVEN_PORT);
(void)stun_is_indication(&msg);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
}
/* stun_init_indication — drives the IS_STUN_INDICATION branch of
* stun_is_indication. */
{
stun_buffer msg;
stun_init_buffer(&msg);
stun_init_indication(method, &msg);
(void)stun_is_indication(&msg);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &default_addr);
}
/* stun_init_success_response. */
{
stun_buffer msg;
stun_init_buffer(&msg);
stun_init_success_response(method, &msg, &tid);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
}
/* stun_set_allocate_request — both rt NULL and rt non-NULL paths. */
{
stun_buffer msg;
stun_init_buffer(&msg);
(void)stun_set_allocate_request(&msg, lifetime, af4, af6, transport, mobile, rt[0] ? rt : NULL, ep);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
stun_init_buffer(&msg);
(void)stun_set_allocate_request(&msg, lifetime, !af4, !af6, transport, !mobile, NULL, ep);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &default_addr);
}
/* stun_set_binding_request + stun_prepare_binding_request (both currently
* delegate to stun_set_binding_request_str but exercise the wrappers). */
{
stun_buffer msg;
stun_init_buffer(&msg);
stun_set_binding_request(&msg);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
stun_init_buffer(&msg);
stun_prepare_binding_request(&msg);
inspect_buffer_message(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, &default_addr);
}
/* stun_init_channel_message + stun_is_channel_message wrappers. */
{
stun_buffer msg;
stun_init_buffer(&msg);
if (stun_init_channel_message(channel_number, &msg, chan_payload_len, padding)) {
uint16_t parsed_chn = 0;
(void)stun_is_channel_message(&msg, &parsed_chn, true);
(void)stun_is_channel_message(&msg, &parsed_chn, false);
}
{
uint16_t chn = 0;
(void)stun_is_channel_message(NULL, &chn, false);
}
}
/* Raw fuzzer bytes through the wrapper-form predicates so they see
* malformed inputs the serializer paths above never produce. */
{
stun_buffer msg;
msg.len = Size > sizeof(msg.buf) ? sizeof(msg.buf) : Size;
memcpy(msg.buf, Data, msg.len);
(void)stun_is_indication(&msg);
stun_tid extracted = {0};
stun_tid_from_message(&msg, &extracted);
{
uint16_t chn = 0;
const size_t saved_len = msg.len;
(void)stun_is_channel_message(&msg, &chn, true);
msg.len = saved_len;
(void)stun_is_channel_message(&msg, &chn, false);
}
(void)stun_attr_get_first_by_type(&msg, STUN_ATTRIBUTE_USERNAME);
(void)stun_attr_get_first_by_type(&msg, STUN_ATTRIBUTE_XOR_PEER_ADDRESS);
(void)stun_attr_get_first_by_type(&msg, STUN_ATTRIBUTE_CHANNEL_NUMBER);
}
}
/* ------------------------------------------------------------------ */
/* libFuzzer entry point — run every harness on each input. */
/* */
/* Note: OAuth token sub-harnesses are intentionally omitted here. */
/* decode_oauth_token_gcm in src/client/ns_turn_msg.c leaks the */
/* EVP_CIPHER_CTX on several early-return paths, which trips ASan */
/* under CIFuzz. Those harnesses will be re-added once the library */
/* leak is fixed in a separate PR. */
/* ------------------------------------------------------------------ */
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
harness_stun_client(Data, Size);
harness_channel_data(Data, Size);
harness_addr_codec(Data, Size);
harness_message_builders(Data, Size);
harness_attr_get_first_addr(Data, Size);
harness_response_matrix(Data, Size);
harness_stun_buffer_api(Data, Size);
return 0;
}