mirror of https://github.com/coturn/coturn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
825 lines
32 KiB
825 lines
32 KiB
/*
|
|
* 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;
|
|
}
|
|
|