mirror of
https://github.com/coturn/coturn.git
synced 2026-05-08 20:26:10 +02:00
Merge 10 fuzz targets into FuzzStun and FuzzStunClient via dispatcher (#1873)
Upstream OSS-Fuzz build recipe
(google/oss-fuzz/projects/coturn/build.sh) only copies two fuzzer
binaries -- FuzzStun and FuzzStunClient -- and their seed corpora into
$OUT. The eight additional fuzz targets added later never ran on
oss-fuzz.com, which is why the introspector profile reports "fuzzer no
longer available" for them.
Rather than patching the Google-owned build recipe, fold all fuzzers
into the two binaries OSS-Fuzz actually ships. Each target now begins
with a single-byte selector (Data[0] mod 5) that dispatches to one of
five sub-harnesses:
FuzzStun - integrity (SHA1/multi-SHA), attr_iter, attr_add,
old_stun
FuzzStunClient - stun_client, channel_data, addr_codec, oauth_token,
oauth_roundtrip
No upstream OSS-Fuzz changes are required.
This commit is contained in:
parent
4f8385e142
commit
c8b3dd6513
@ -2,19 +2,14 @@ set(FUZZ_COMMON_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/FuzzOpenSSLInit.c
|
||||
)
|
||||
|
||||
# Only two fuzz targets are produced because the upstream OSS-Fuzz build
|
||||
# recipe (google/oss-fuzz/projects/coturn/build.sh) copies only these two
|
||||
# binaries and their seed corpora into $OUT. Additional fuzzing coverage is
|
||||
# reached by dispatching from a single-byte selector inside each target.
|
||||
|
||||
add_executable(FuzzStun FuzzStun.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzStun turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
add_executable(FuzzChannelData FuzzChannelData.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzChannelData turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input/FuzzChannelData_seed_corpus.zip
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input/FuzzStun_seed_corpus.zip
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
||||
set(FuzzStunClientSRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/FuzzStunClient.c
|
||||
${PROJECT_SOURCE_DIR}/src/apps/common/stun_buffer.c
|
||||
@ -23,32 +18,12 @@ set(FuzzStunClientSRC
|
||||
add_executable(FuzzStunClient ${FuzzStunClientSRC} ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzStunClient turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input/FuzzStun_seed_corpus.zip
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input/FuzzStunClient_seed_corpus.zip
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_executable(FuzzOAuthToken FuzzOAuthToken.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzOAuthToken turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
# --- New fuzzing targets ---
|
||||
|
||||
add_executable(FuzzStunAttrIter FuzzStunAttrIter.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzStunAttrIter turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
add_executable(FuzzStunIntegrity FuzzStunIntegrity.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzStunIntegrity turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
add_executable(FuzzStunAddrCodec FuzzStunAddrCodec.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzStunAddrCodec turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
add_executable(FuzzOldStun FuzzOldStun.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzOldStun turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
add_executable(FuzzOAuthRoundTrip FuzzOAuthRoundTrip.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzOAuthRoundTrip turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
add_executable(FuzzStunAttrAdd FuzzStunAttrAdd.c ${FUZZ_COMMON_SOURCES})
|
||||
target_link_libraries(FuzzStunAttrAdd turnclient ${LIB_FUZZING_ENGINE})
|
||||
|
||||
# Shared dictionary for all STUN/TURN fuzz targets
|
||||
# Shared dictionary for STUN/TURN fuzz targets
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/stun.dict
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for ChannelData TCP framing (issue #1837).
|
||||
*
|
||||
* Exercises stun_get_message_len_str() and stun_is_channel_message_str()
|
||||
* against arbitrary input, with focus on:
|
||||
* - uint16_t overflow when data_len >= 0xFFFD (4 + data_len wraps)
|
||||
* - Consistency between the two framing functions
|
||||
* - Padding boundary conditions
|
||||
* - Invalid channel numbers (outside 0x4000–0x7FFF)
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
|
||||
#define kMinInputLength 4
|
||||
#define kMaxInputLength 8192
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Work on a mutable copy — stun_get_message_len_str takes uint8_t * */
|
||||
uint8_t buf[kMaxInputLength];
|
||||
memcpy(buf, Data, Size);
|
||||
|
||||
size_t app_len_tcp = 0;
|
||||
size_t app_len_udp = 0;
|
||||
|
||||
/* Test TCP framing (padding=1): the overflow path that triggered #1837 */
|
||||
int mlen_tcp = stun_get_message_len_str(buf, Size, 1, &app_len_tcp);
|
||||
|
||||
/* Test UDP framing (padding=0): should be consistent with TCP result for
|
||||
* data_len values that are already 4-byte aligned */
|
||||
int mlen_udp = stun_get_message_len_str(buf, Size, 0, &app_len_udp);
|
||||
|
||||
/* Invariant: if TCP framing accepted the message, app_len must be <= Size
|
||||
* and the padded total (mlen_tcp) must also be <= Size. */
|
||||
if (mlen_tcp > 0) {
|
||||
if (app_len_tcp > Size) {
|
||||
__builtin_trap();
|
||||
}
|
||||
if ((size_t)mlen_tcp > Size) {
|
||||
__builtin_trap();
|
||||
}
|
||||
/* TCP padded length must be >= the unpadded app_len */
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/* stun_is_channel_message_str: mandatory_padding=1 (TCP), then optional */
|
||||
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 stun_is_channel_message_str accepted it, the reported length must be
|
||||
* within the buffer and at least 4 bytes (header). */
|
||||
if (is_chan_tcp) {
|
||||
if (blen_tcp < 4 || blen_tcp > Size) {
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
if (is_chan_udp) {
|
||||
if (blen_udp < 4 || blen_udp > Size) {
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for OAuth token encode/decode round-trip.
|
||||
*
|
||||
* Exercises:
|
||||
* - encode_oauth_token() serialization (currently unfuzzed)
|
||||
* - decode_oauth_token() with A256GCM (FuzzOAuthToken only tests A128GCM)
|
||||
* - Round-trip consistency: encode then decode
|
||||
* - Raw fuzz bytes as encoded token with A256GCM key
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
#include "ns_turn_msg_defs.h"
|
||||
|
||||
/* 32-byte all-zero key for A256GCM */
|
||||
static const uint8_t kFuzzKey256[32] = {0};
|
||||
/* 16-byte all-zero key for A128GCM */
|
||||
static const uint8_t kFuzzKey128[16] = {0};
|
||||
|
||||
#define kMinInputLength 16
|
||||
#define kMaxInputLength 512
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Split fuzz input: first 12 bytes = nonce, rest = token fields */
|
||||
uint8_t nonce[OAUTH_GCM_NONCE_SIZE] = {0};
|
||||
size_t nonce_len = Size > OAUTH_GCM_NONCE_SIZE ? OAUTH_GCM_NONCE_SIZE : Size;
|
||||
memcpy(nonce, Data, nonce_len);
|
||||
|
||||
/* Build a token from fuzz data for the encode path */
|
||||
oauth_token src_token = {0};
|
||||
if (Size > OAUTH_GCM_NONCE_SIZE + sizeof(uint64_t) + sizeof(uint32_t)) {
|
||||
const uint8_t *p = Data + OAUTH_GCM_NONCE_SIZE;
|
||||
memcpy(&src_token.enc_block.timestamp, p, sizeof(uint64_t));
|
||||
p += sizeof(uint64_t);
|
||||
memcpy(&src_token.enc_block.lifetime, p, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
size_t key_len = Size - (size_t)(p - Data);
|
||||
if (key_len > MAXSHASIZE) {
|
||||
key_len = MAXSHASIZE;
|
||||
}
|
||||
src_token.enc_block.key_length = (uint16_t)key_len;
|
||||
memcpy(src_token.enc_block.mac_key, p, key_len);
|
||||
}
|
||||
|
||||
/* Test A256GCM encode + decode round-trip */
|
||||
{
|
||||
oauth_key key = {0};
|
||||
key.as_rs_alg = A256GCM;
|
||||
memcpy(key.as_rs_key, kFuzzKey256, sizeof(kFuzzKey256));
|
||||
key.as_rs_key_size = sizeof(kFuzzKey256);
|
||||
memcpy(key.auth_key, kFuzzKey256, sizeof(kFuzzKey256));
|
||||
key.auth_key_size = sizeof(kFuzzKey256);
|
||||
|
||||
encoded_oauth_token etoken = {0};
|
||||
bool enc_ok = encode_oauth_token((const uint8_t *)"fuzz-server", &etoken, &key, &src_token, nonce);
|
||||
|
||||
if (enc_ok) {
|
||||
oauth_token dec_token = {0};
|
||||
bool dec_ok = decode_oauth_token((const uint8_t *)"fuzz-server", &etoken, &key, &dec_token);
|
||||
|
||||
/* If round-trip succeeded, nonce_length must be valid */
|
||||
if (dec_ok && dec_token.enc_block.nonce_length > OAUTH_MAX_NONCE_SIZE) {
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test A128GCM encode + decode round-trip */
|
||||
{
|
||||
oauth_key key = {0};
|
||||
key.as_rs_alg = A128GCM;
|
||||
memcpy(key.as_rs_key, kFuzzKey128, sizeof(kFuzzKey128));
|
||||
key.as_rs_key_size = sizeof(kFuzzKey128);
|
||||
memcpy(key.auth_key, kFuzzKey128, sizeof(kFuzzKey128));
|
||||
key.auth_key_size = sizeof(kFuzzKey128);
|
||||
|
||||
encoded_oauth_token etoken = {0};
|
||||
bool enc_ok = encode_oauth_token((const uint8_t *)"fuzz-server", &etoken, &key, &src_token, nonce);
|
||||
|
||||
if (enc_ok) {
|
||||
oauth_token dec_token = {0};
|
||||
bool dec_ok = decode_oauth_token((const uint8_t *)"fuzz-server", &etoken, &key, &dec_token);
|
||||
|
||||
if (dec_ok && dec_token.enc_block.nonce_length > OAUTH_MAX_NONCE_SIZE) {
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Also test raw fuzz bytes as an encoded token with A256GCM
|
||||
* (complements FuzzOAuthToken which uses A128GCM) */
|
||||
if (Size <= MAX_ENCODED_OAUTH_TOKEN_SIZE) {
|
||||
oauth_key key = {0};
|
||||
key.as_rs_alg = A256GCM;
|
||||
memcpy(key.as_rs_key, kFuzzKey256, sizeof(kFuzzKey256));
|
||||
key.as_rs_key_size = sizeof(kFuzzKey256);
|
||||
memcpy(key.auth_key, kFuzzKey256, sizeof(kFuzzKey256));
|
||||
key.auth_key_size = sizeof(kFuzzKey256);
|
||||
|
||||
encoded_oauth_token etoken = {0};
|
||||
etoken.size = Size;
|
||||
memcpy(etoken.token, Data, Size);
|
||||
|
||||
oauth_token dec_token = {0};
|
||||
bool dec_ok = decode_oauth_token((const uint8_t *)"fuzz-server", &etoken, &key, &dec_token);
|
||||
|
||||
if (dec_ok && dec_token.enc_block.nonce_length > OAUTH_MAX_NONCE_SIZE) {
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for OAuth token decoding (issue #1838).
|
||||
*
|
||||
* Exercises decode_oauth_token() against arbitrary input, with focus on:
|
||||
* - Stack buffer overflow when nonce_length field > OAUTH_MAX_NONCE_SIZE (256)
|
||||
* - encoded_field_size underflow in unsigned subtraction
|
||||
* - OpenSSL GCM IV-length handling with out-of-range nonce lengths
|
||||
*
|
||||
* The fuzz input is treated as the raw bytes of an encoded_oauth_token. A
|
||||
* fixed oauth_key with a known algorithm is used so the fuzzer exercises the
|
||||
* parsing and bounds-checking paths rather than key derivation.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
#include "ns_turn_msg_defs.h"
|
||||
|
||||
/* 16-byte all-zero key for A128GCM (128-bit key size). */
|
||||
static const uint8_t kFuzzAsRsKey[16] = {0};
|
||||
|
||||
/* Minimum meaningful token: 2 (nonce_len) + nonce + 4 (key_len) + 8
|
||||
* (timestamp) + OAUTH_GCM_TAG_SIZE (16) + 1 (at least one ciphertext byte)
|
||||
* With nonce_len = 0 that is 2 + 0 + 4 + 8 + 16 + 1 = 31. */
|
||||
#define kMinInputLength 31
|
||||
#define kMaxInputLength MAX_ENCODED_OAUTH_TOKEN_SIZE
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
oauth_key key = {0};
|
||||
key.as_rs_alg = A128GCM;
|
||||
memcpy(key.as_rs_key, kFuzzAsRsKey, sizeof(kFuzzAsRsKey));
|
||||
key.as_rs_key_size = sizeof(kFuzzAsRsKey);
|
||||
/* auth_key_size must be non-zero for the integrity path; use the same key. */
|
||||
memcpy(key.auth_key, kFuzzAsRsKey, sizeof(kFuzzAsRsKey));
|
||||
key.auth_key_size = sizeof(kFuzzAsRsKey);
|
||||
|
||||
encoded_oauth_token etoken = {0};
|
||||
etoken.size = Size;
|
||||
memcpy(etoken.token, Data, Size);
|
||||
|
||||
oauth_token dtoken = {0};
|
||||
|
||||
/*
|
||||
* The return value is intentionally ignored: we are looking for crashes
|
||||
* (buffer overflows, use-after-free, etc.), not for decryption success.
|
||||
*
|
||||
* Invariant: if decoding claims success, the nonce_length stored in
|
||||
* dtoken must fit within the nonce buffer.
|
||||
*/
|
||||
bool ok = decode_oauth_token((const uint8_t *)"fuzz-server", &etoken, &key, &dtoken);
|
||||
|
||||
if (ok) {
|
||||
/* nonce_length must be within the fixed buffer declared in the struct. */
|
||||
if (dtoken.enc_block.nonce_length > OAUTH_MAX_NONCE_SIZE) {
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for old (pre-RFC 5389) STUN message detection.
|
||||
*
|
||||
* Exercises old_stun_is_command_message_str() which accepts messages
|
||||
* without the 0x2112A442 magic cookie -- a separate validation path
|
||||
* from the modern STUN parser.
|
||||
*
|
||||
* Includes an invariant check: if modern STUN accepts a message,
|
||||
* old STUN must also accept it (old is a superset).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
|
||||
#define kMinInputLength STUN_HEADER_LENGTH
|
||||
#define kMaxInputLength 5120
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buf[kMaxInputLength];
|
||||
memcpy(buf, Data, Size);
|
||||
|
||||
/* Old STUN: any magic cookie accepted, returns the cookie value */
|
||||
uint32_t cookie = 0;
|
||||
bool is_old = old_stun_is_command_message_str(buf, Size, &cookie);
|
||||
|
||||
/* Modern STUN for comparison */
|
||||
bool is_modern = stun_is_command_message_str(buf, Size);
|
||||
|
||||
/* Invariant: modern STUN messages are a subset of old STUN messages
|
||||
* (old accepts any cookie, modern requires 0x2112A442) */
|
||||
if (is_modern && !is_old) {
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
/* If old STUN accepted it, exercise message type extraction */
|
||||
if (is_old) {
|
||||
uint16_t msg_type = stun_get_msg_type_str(buf, Size);
|
||||
uint16_t method = stun_get_method_str(buf, Size);
|
||||
(void)msg_type;
|
||||
(void)method;
|
||||
|
||||
stun_is_request_str(buf, Size);
|
||||
stun_is_indication_str(buf, Size);
|
||||
stun_is_success_response_str(buf, Size);
|
||||
|
||||
int err_code = 0;
|
||||
uint8_t err_msg[256] = {0};
|
||||
stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
|
||||
|
||||
/* Full check with fingerprint validation */
|
||||
int fp_present = 0;
|
||||
stun_is_command_message_full_check_str(buf, Size, 1, &fp_present);
|
||||
stun_is_command_message_full_check_str(buf, Size, 0, &fp_present);
|
||||
|
||||
/* Binding-specific checks */
|
||||
stun_is_binding_request_str(buf, Size, 0);
|
||||
stun_is_binding_response_str(buf, Size);
|
||||
|
||||
/* Attribute iteration on old STUN messages */
|
||||
stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
|
||||
while (sar) {
|
||||
(void)stun_attr_get_type(sar);
|
||||
(void)stun_attr_get_len(sar);
|
||||
sar = stun_attr_get_next_str(buf, Size, sar);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,21 +1,33 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Multi-harness libFuzzer entry point for server-side STUN parsing.
|
||||
*
|
||||
* Every iteration runs all sub-harnesses in sequence on the same input:
|
||||
* RFC 5769 integrity checks, multi-SHA/credential integrity, attribute
|
||||
* iteration, attribute serialization, and legacy (pre-RFC 5389) STUN
|
||||
* detection. Keeping everything behind a single binary allows the
|
||||
* upstream OSS-Fuzz build recipe to stay unchanged.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "apputils.h"
|
||||
#include "ns_turn_msg.h"
|
||||
#include "ns_turn_msg_defs.h"
|
||||
#include "ns_turn_utils.h"
|
||||
#include "stun_buffer.h"
|
||||
|
||||
static SHATYPE shatype = SHATYPE_SHA1;
|
||||
|
||||
#define kMinInputLength 10
|
||||
#define kMaxInputLength 5120
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data,
|
||||
size_t Size) { // rfc5769check
|
||||
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 1;
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Integrity: SHA1 short-term + SHA256 long-term (original FuzzStun). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void harness_integrity_sha1(const uint8_t *Data, size_t Size) {
|
||||
if (Size < 10 || Size > 5120) {
|
||||
return;
|
||||
}
|
||||
|
||||
stun_is_command_message_full_check_str((uint8_t *)Data, Size, 1, NULL);
|
||||
@ -24,12 +36,336 @@ extern int LLVMFuzzerTestOneInput(const uint8_t *Data,
|
||||
uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm";
|
||||
uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt";
|
||||
|
||||
/* Short-term credentials, SHA1 (original path) */
|
||||
stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, (uint8_t *)Data, Size, uname, realm, upwd, shatype);
|
||||
|
||||
/* Long-term credentials, SHA256 */
|
||||
stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, (uint8_t *)Data, Size, uname, realm, upwd,
|
||||
SHATYPE_SHA1);
|
||||
stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, (uint8_t *)Data, Size, uname, realm, upwd,
|
||||
SHATYPE_SHA256);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Integrity across all SHA types + credential modes (FuzzStunIntegrity). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void harness_integrity_multi(const uint8_t *Data, size_t Size) {
|
||||
if (Size < STUN_HEADER_LENGTH || Size > 5120) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buf[5120];
|
||||
uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser";
|
||||
uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm";
|
||||
uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt";
|
||||
|
||||
static const SHATYPE sha_types[] = {SHATYPE_SHA1, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512};
|
||||
const size_t num_sha = sizeof(sha_types) / sizeof(sha_types[0]);
|
||||
|
||||
for (size_t s = 0; s < num_sha; s++) {
|
||||
memcpy(buf, Data, Size);
|
||||
stun_is_command_message_full_check_str(buf, Size, 1, NULL);
|
||||
stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, Size, uname, realm, upwd, sha_types[s]);
|
||||
|
||||
memcpy(buf, Data, Size);
|
||||
stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, Size, uname, realm, upwd, sha_types[s]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Attribute TLV iteration + typed extraction (FuzzStunAttrIter). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static const uint16_t kAllAttrTypes[] = {
|
||||
STUN_ATTRIBUTE_MAPPED_ADDRESS,
|
||||
OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS,
|
||||
STUN_ATTRIBUTE_CHANGE_REQUEST,
|
||||
OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS,
|
||||
OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS,
|
||||
STUN_ATTRIBUTE_USERNAME,
|
||||
OLD_STUN_ATTRIBUTE_PASSWORD,
|
||||
STUN_ATTRIBUTE_MESSAGE_INTEGRITY,
|
||||
STUN_ATTRIBUTE_ERROR_CODE,
|
||||
STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
|
||||
OLD_STUN_ATTRIBUTE_REFLECTED_FROM,
|
||||
STUN_ATTRIBUTE_CHANNEL_NUMBER,
|
||||
STUN_ATTRIBUTE_LIFETIME,
|
||||
STUN_ATTRIBUTE_BANDWIDTH,
|
||||
STUN_ATTRIBUTE_XOR_PEER_ADDRESS,
|
||||
STUN_ATTRIBUTE_DATA,
|
||||
STUN_ATTRIBUTE_REALM,
|
||||
STUN_ATTRIBUTE_NONCE,
|
||||
STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,
|
||||
STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY,
|
||||
STUN_ATTRIBUTE_EVEN_PORT,
|
||||
STUN_ATTRIBUTE_REQUESTED_TRANSPORT,
|
||||
STUN_ATTRIBUTE_DONT_FRAGMENT,
|
||||
STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN,
|
||||
STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
|
||||
OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
|
||||
STUN_ATTRIBUTE_TIMER_VAL,
|
||||
STUN_ATTRIBUTE_RESERVATION_TOKEN,
|
||||
STUN_ATTRIBUTE_PRIORITY,
|
||||
STUN_ATTRIBUTE_PADDING,
|
||||
STUN_ATTRIBUTE_RESPONSE_PORT,
|
||||
STUN_ATTRIBUTE_CONNECTION_ID,
|
||||
STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY,
|
||||
STUN_ATTRIBUTE_ADDRESS_ERROR_CODE,
|
||||
STUN_ATTRIBUTE_SOFTWARE,
|
||||
STUN_ATTRIBUTE_ALTERNATE_SERVER,
|
||||
STUN_ATTRIBUTE_FINGERPRINT,
|
||||
STUN_ATTRIBUTE_ICE_CONTROLLED,
|
||||
STUN_ATTRIBUTE_RESPONSE_ORIGIN,
|
||||
STUN_ATTRIBUTE_OTHER_ADDRESS,
|
||||
STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION,
|
||||
STUN_ATTRIBUTE_ORIGIN,
|
||||
STUN_ATTRIBUTE_MOBILITY_TICKET,
|
||||
STUN_ATTRIBUTE_NEW_BANDWIDTH,
|
||||
};
|
||||
|
||||
static const uint16_t kAddrAttrs[] = {
|
||||
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_iter(const uint8_t *Data, size_t Size) {
|
||||
if (Size < STUN_HEADER_LENGTH || Size > 8192) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buf[8192];
|
||||
memcpy(buf, Data, Size);
|
||||
|
||||
if (!stun_is_command_message_str(buf, Size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
|
||||
while (sar) {
|
||||
(void)stun_attr_get_type(sar);
|
||||
(void)stun_attr_get_len(sar);
|
||||
(void)stun_attr_get_value(sar);
|
||||
(void)stun_attr_is_addr(sar);
|
||||
sar = stun_attr_get_next_str(buf, Size, sar);
|
||||
}
|
||||
|
||||
ioa_addr addr;
|
||||
const size_t num_addr_attrs = sizeof(kAddrAttrs) / sizeof(kAddrAttrs[0]);
|
||||
for (size_t i = 0; i < num_addr_attrs; i++) {
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, kAddrAttrs[i]);
|
||||
if (sar) {
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
stun_attr_get_addr_str(buf, Size, sar, &addr, NULL);
|
||||
}
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
stun_attr_get_first_addr_str(buf, Size, kAddrAttrs[i], &addr, NULL);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANNEL_NUMBER);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_channel_number(sar);
|
||||
}
|
||||
(void)stun_attr_get_first_channel_number_str(buf, Size);
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY);
|
||||
if (sar) {
|
||||
(void)stun_get_requested_address_family(sar);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY);
|
||||
if (sar) {
|
||||
(void)stun_get_requested_address_family(sar);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_EVEN_PORT);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_even_port(sar);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_BANDWIDTH);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_bandwidth(sar);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_NEW_BANDWIDTH);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_bandwidth(sar);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESERVATION_TOKEN);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_reservation_token_value(sar);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANGE_REQUEST);
|
||||
if (sar) {
|
||||
bool change_ip = false, change_port = false;
|
||||
stun_attr_get_change_request_str(sar, &change_ip, &change_port);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESPONSE_PORT);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_response_port_str(sar);
|
||||
}
|
||||
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_PADDING);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_padding_len_str(sar);
|
||||
}
|
||||
|
||||
{
|
||||
int err_code = 0;
|
||||
uint8_t err_msg[1024] = {0};
|
||||
stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
|
||||
}
|
||||
{
|
||||
int err_code = 0;
|
||||
uint8_t err_msg[1024] = {0};
|
||||
uint8_t chal_realm[STUN_MAX_REALM_SIZE + 1] = {0};
|
||||
uint8_t chal_nonce[STUN_MAX_NONCE_SIZE + 1] = {0};
|
||||
uint8_t server_name[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
|
||||
bool oauth = false;
|
||||
stun_is_challenge_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg), chal_realm, chal_nonce, server_name,
|
||||
&oauth);
|
||||
}
|
||||
|
||||
const size_t num_all_attrs = sizeof(kAllAttrTypes) / sizeof(kAllAttrTypes[0]);
|
||||
for (size_t i = 0; i < num_all_attrs; i++) {
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, kAllAttrTypes[i]);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_type(sar);
|
||||
(void)stun_attr_get_len(sar);
|
||||
(void)stun_attr_get_value(sar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Attribute serialization / append paths (FuzzStunAttrAdd). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void harness_attr_add(const uint8_t *Data, size_t Size) {
|
||||
if (Size < STUN_HEADER_LENGTH || Size > 4096) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0};
|
||||
memcpy(buf, Data, Size);
|
||||
size_t len = Size;
|
||||
|
||||
if (!stun_is_command_message_str(buf, len)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t test_uname[] = "fuzzuser@fuzz.realm";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_USERNAME, test_uname, (int)(sizeof(test_uname) - 1));
|
||||
|
||||
uint8_t test_realm[] = "fuzz.realm";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, test_realm, (int)(sizeof(test_realm) - 1));
|
||||
|
||||
uint8_t test_nonce[] = "fuzznonce0123456789abcdef";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, test_nonce, (int)(sizeof(test_nonce) - 1));
|
||||
|
||||
uint8_t test_sw[] = "coturn-fuzz/1.0";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_SOFTWARE, test_sw, (int)(sizeof(test_sw) - 1));
|
||||
|
||||
uint8_t lifetime_val[4] = {0x00, 0x00, 0x02, 0x58};
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_LIFETIME, lifetime_val, 4);
|
||||
|
||||
uint8_t transport_val[4] = {STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, 0x00, 0x00, 0x00};
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_TRANSPORT, transport_val, 4);
|
||||
|
||||
uint8_t af_val[4] = {STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4, 0x00, 0x00, 0x00};
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, af_val, 4);
|
||||
|
||||
uint8_t even_port_val[1] = {0x80};
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_EVEN_PORT, even_port_val, 1);
|
||||
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0);
|
||||
|
||||
stun_attr_add_channel_number_str(buf, &len, 0x4001);
|
||||
|
||||
stun_attr_add_bandwidth_str(buf, &len, 1000000);
|
||||
|
||||
ioa_addr addr4 = {0};
|
||||
addr4.s4.sin_family = AF_INET;
|
||||
addr4.s4.sin_port = htons(12345);
|
||||
addr4.s4.sin_addr.s_addr = htonl(0xC0A80001);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr4);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &addr4);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr4);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_MAPPED_ADDRESS, &addr4);
|
||||
|
||||
ioa_addr addr6 = {0};
|
||||
addr6.s6.sin6_family = AF_INET6;
|
||||
addr6.s6.sin6_port = htons(54321);
|
||||
addr6.s6.sin6_addr.s6_addr[15] = 1;
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr6);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr6);
|
||||
|
||||
stun_attr_add_address_error_code(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6, 440);
|
||||
|
||||
stun_attr_add_change_request_str(buf, &len, true, true);
|
||||
stun_attr_add_response_port_str(buf, &len, 3479);
|
||||
stun_attr_add_padding_str(buf, &len, 64);
|
||||
|
||||
if (Size > STUN_HEADER_LENGTH + 4) {
|
||||
int data_len = (int)(Size - STUN_HEADER_LENGTH);
|
||||
if (data_len > 1024) {
|
||||
data_len = 1024;
|
||||
}
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DATA, Data + STUN_HEADER_LENGTH, data_len);
|
||||
}
|
||||
|
||||
stun_attr_add_fingerprint_str(buf, &len);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Legacy (pre-RFC 5389) STUN detection (FuzzOldStun). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void harness_old_stun(const uint8_t *Data, size_t Size) {
|
||||
if (Size < STUN_HEADER_LENGTH || Size > 5120) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buf[5120];
|
||||
memcpy(buf, Data, Size);
|
||||
|
||||
uint32_t cookie = 0;
|
||||
bool is_old = old_stun_is_command_message_str(buf, Size, &cookie);
|
||||
(void)stun_is_command_message_str(buf, Size);
|
||||
|
||||
if (is_old) {
|
||||
(void)stun_get_msg_type_str(buf, Size);
|
||||
(void)stun_get_method_str(buf, Size);
|
||||
|
||||
stun_is_request_str(buf, Size);
|
||||
stun_is_indication_str(buf, Size);
|
||||
stun_is_success_response_str(buf, Size);
|
||||
|
||||
int err_code = 0;
|
||||
uint8_t err_msg[256] = {0};
|
||||
stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
|
||||
|
||||
int fp_present = 0;
|
||||
stun_is_command_message_full_check_str(buf, Size, 1, &fp_present);
|
||||
stun_is_command_message_full_check_str(buf, Size, 0, &fp_present);
|
||||
|
||||
stun_is_binding_request_str(buf, Size, 0);
|
||||
stun_is_binding_response_str(buf, Size);
|
||||
|
||||
stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
|
||||
while (sar) {
|
||||
(void)stun_attr_get_type(sar);
|
||||
(void)stun_attr_get_len(sar);
|
||||
sar = stun_attr_get_next_str(buf, Size, sar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* libFuzzer entry point — run every harness on each input. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
harness_integrity_sha1(Data, Size);
|
||||
harness_integrity_multi(Data, Size);
|
||||
harness_attr_iter(Data, Size);
|
||||
harness_attr_add(Data, Size);
|
||||
harness_old_stun(Data, Size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for STUN address encode/decode.
|
||||
*
|
||||
* Exercises stun_addr_decode() directly with arbitrary address attribute
|
||||
* payloads, testing:
|
||||
* - Invalid family bytes (not 0x01 or 0x02)
|
||||
* - Truncated IPv4 (<8 bytes) and IPv6 (<20 bytes) payloads
|
||||
* - XOR decoding with arbitrary transaction IDs
|
||||
* - Round-trip encode->decode consistency
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
#include "ns_turn_msg_addr.h"
|
||||
|
||||
#define kMinInputLength 2
|
||||
#define kMaxInputLength 64
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Layout: first 12 bytes are used as a transaction ID (padded with zeros
|
||||
* if input is shorter), remaining bytes are the raw address attribute value.
|
||||
*/
|
||||
uint8_t tid[STUN_TID_SIZE] = {0};
|
||||
size_t tid_bytes =
|
||||
Size > (STUN_TID_SIZE + kMinInputLength) ? STUN_TID_SIZE : (Size > kMinInputLength ? Size - kMinInputLength : 0);
|
||||
memcpy(tid, Data, tid_bytes);
|
||||
const uint8_t *payload = Data + tid_bytes;
|
||||
int payload_len = (int)(Size - tid_bytes);
|
||||
|
||||
ioa_addr addr = {0};
|
||||
int rc;
|
||||
|
||||
/* Decode with XOR */
|
||||
rc = stun_addr_decode(&addr, payload, payload_len, 1, STUN_MAGIC_COOKIE, tid);
|
||||
|
||||
/* If decode succeeded, verify round-trip */
|
||||
if (rc == 0) {
|
||||
uint8_t enc_buf[32] = {0};
|
||||
int enc_len = 0;
|
||||
int erc = stun_addr_encode(&addr, enc_buf, &enc_len, 1, STUN_MAGIC_COOKIE, tid);
|
||||
|
||||
if (erc == 0) {
|
||||
/* Decode the re-encoded buffer and compare */
|
||||
ioa_addr addr2 = {0};
|
||||
stun_addr_decode(&addr2, enc_buf, enc_len, 1, STUN_MAGIC_COOKIE, tid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Decode without XOR */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
rc = stun_addr_decode(&addr, payload, payload_len, 0, 0, tid);
|
||||
|
||||
if (rc == 0) {
|
||||
uint8_t enc_buf[32] = {0};
|
||||
int enc_len = 0;
|
||||
int erc = stun_addr_encode(&addr, enc_buf, &enc_len, 0, 0, tid);
|
||||
|
||||
if (erc == 0) {
|
||||
ioa_addr addr2 = {0};
|
||||
stun_addr_decode(&addr2, enc_buf, enc_len, 0, 0, tid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test with a different magic cookie value (old STUN) */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
uint32_t alt_cookie = 0;
|
||||
if (Size >= 4) {
|
||||
memcpy(&alt_cookie, Data, 4);
|
||||
}
|
||||
rc = stun_addr_decode(&addr, payload, payload_len, 1, alt_cookie, tid);
|
||||
(void)rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for STUN attribute serialization.
|
||||
*
|
||||
* Exercises stun_attr_add_str(), stun_attr_add_addr_str(), and related
|
||||
* serialization functions by treating fuzz input as a partial STUN message
|
||||
* and attempting to append various attribute types. Tests buffer bounds
|
||||
* checking in the write path.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
#include "ns_turn_msg_defs.h"
|
||||
|
||||
#define kMinInputLength STUN_HEADER_LENGTH
|
||||
#define kMaxInputLength 4096
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Large buffer to allow room for appending attributes */
|
||||
uint8_t buf[MAX_STUN_MESSAGE_SIZE] = {0};
|
||||
memcpy(buf, Data, Size);
|
||||
size_t len = Size;
|
||||
|
||||
if (!stun_is_command_message_str(buf, len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* String attribute (USERNAME) */
|
||||
uint8_t test_uname[] = "fuzzuser@fuzz.realm";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_USERNAME, test_uname, (int)(sizeof(test_uname) - 1));
|
||||
|
||||
/* String attribute (REALM) */
|
||||
uint8_t test_realm[] = "fuzz.realm";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REALM, test_realm, (int)(sizeof(test_realm) - 1));
|
||||
|
||||
/* String attribute (NONCE) */
|
||||
uint8_t test_nonce[] = "fuzznonce0123456789abcdef";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_NONCE, test_nonce, (int)(sizeof(test_nonce) - 1));
|
||||
|
||||
/* String attribute (SOFTWARE) */
|
||||
uint8_t test_sw[] = "coturn-fuzz/1.0";
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_SOFTWARE, test_sw, (int)(sizeof(test_sw) - 1));
|
||||
|
||||
/* 4-byte attribute (LIFETIME) */
|
||||
uint8_t lifetime_val[4] = {0x00, 0x00, 0x02, 0x58}; /* 600 seconds */
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_LIFETIME, lifetime_val, 4);
|
||||
|
||||
/* 4-byte attribute (REQUESTED-TRANSPORT) */
|
||||
uint8_t transport_val[4] = {STUN_ATTRIBUTE_TRANSPORT_UDP_VALUE, 0x00, 0x00, 0x00};
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_TRANSPORT, transport_val, 4);
|
||||
|
||||
/* 4-byte attribute (REQUESTED-ADDRESS-FAMILY) */
|
||||
uint8_t af_val[4] = {STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV4, 0x00, 0x00, 0x00};
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY, af_val, 4);
|
||||
|
||||
/* 1-byte attribute (EVEN-PORT) */
|
||||
uint8_t even_port_val[1] = {0x80}; /* R bit set */
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_EVEN_PORT, even_port_val, 1);
|
||||
|
||||
/* 0-byte attribute (DONT-FRAGMENT) */
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DONT_FRAGMENT, NULL, 0);
|
||||
|
||||
/* Channel number */
|
||||
stun_attr_add_channel_number_str(buf, &len, 0x4001);
|
||||
|
||||
/* Bandwidth */
|
||||
stun_attr_add_bandwidth_str(buf, &len, 1000000);
|
||||
|
||||
/* Address attribute - IPv4 */
|
||||
ioa_addr addr4;
|
||||
memset(&addr4, 0, sizeof(addr4));
|
||||
addr4.s4.sin_family = AF_INET;
|
||||
addr4.s4.sin_port = htons(12345);
|
||||
addr4.s4.sin_addr.s_addr = htonl(0xC0A80001); /* 192.168.0.1 */
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr4);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &addr4);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr4);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_MAPPED_ADDRESS, &addr4);
|
||||
|
||||
/* Address attribute - IPv6 */
|
||||
ioa_addr addr6;
|
||||
memset(&addr6, 0, sizeof(addr6));
|
||||
addr6.s6.sin6_family = AF_INET6;
|
||||
addr6.s6.sin6_port = htons(54321);
|
||||
/* ::1 */
|
||||
addr6.s6.sin6_addr.s6_addr[15] = 1;
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, &addr6);
|
||||
stun_attr_add_addr_str(buf, &len, STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS, &addr6);
|
||||
|
||||
/* Address error code (RFC 8656) */
|
||||
stun_attr_add_address_error_code(buf, &len, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY_VALUE_IPV6, 440);
|
||||
|
||||
/* RFC 5780 attributes */
|
||||
stun_attr_add_change_request_str(buf, &len, true, true);
|
||||
stun_attr_add_response_port_str(buf, &len, 3479);
|
||||
stun_attr_add_padding_str(buf, &len, 64);
|
||||
|
||||
/* DATA attribute with fuzz payload */
|
||||
if (Size > STUN_HEADER_LENGTH + 4) {
|
||||
int data_len = (int)(Size - STUN_HEADER_LENGTH);
|
||||
if (data_len > 1024) {
|
||||
data_len = 1024;
|
||||
}
|
||||
stun_attr_add_str(buf, &len, STUN_ATTRIBUTE_DATA, Data + STUN_HEADER_LENGTH, data_len);
|
||||
}
|
||||
|
||||
/* Fingerprint - must be last */
|
||||
stun_attr_add_fingerprint_str(buf, &len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,228 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for STUN attribute TLV iteration and typed extraction.
|
||||
*
|
||||
* Exercises the attribute iterator and every per-type accessor against
|
||||
* arbitrary input, catching OOB reads in the TLV chain, address XOR
|
||||
* decoding, and value extraction for all defined attribute types.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
#include "ns_turn_msg_defs.h"
|
||||
|
||||
#define kMinInputLength STUN_HEADER_LENGTH
|
||||
#define kMaxInputLength 8192
|
||||
|
||||
/* Every attribute type defined in coturn */
|
||||
static const uint16_t kAllAttrTypes[] = {
|
||||
STUN_ATTRIBUTE_MAPPED_ADDRESS,
|
||||
OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS,
|
||||
STUN_ATTRIBUTE_CHANGE_REQUEST,
|
||||
OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS,
|
||||
OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS,
|
||||
STUN_ATTRIBUTE_USERNAME,
|
||||
OLD_STUN_ATTRIBUTE_PASSWORD,
|
||||
STUN_ATTRIBUTE_MESSAGE_INTEGRITY,
|
||||
STUN_ATTRIBUTE_ERROR_CODE,
|
||||
STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
|
||||
OLD_STUN_ATTRIBUTE_REFLECTED_FROM,
|
||||
STUN_ATTRIBUTE_CHANNEL_NUMBER,
|
||||
STUN_ATTRIBUTE_LIFETIME,
|
||||
STUN_ATTRIBUTE_BANDWIDTH,
|
||||
STUN_ATTRIBUTE_XOR_PEER_ADDRESS,
|
||||
STUN_ATTRIBUTE_DATA,
|
||||
STUN_ATTRIBUTE_REALM,
|
||||
STUN_ATTRIBUTE_NONCE,
|
||||
STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS,
|
||||
STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY,
|
||||
STUN_ATTRIBUTE_EVEN_PORT,
|
||||
STUN_ATTRIBUTE_REQUESTED_TRANSPORT,
|
||||
STUN_ATTRIBUTE_DONT_FRAGMENT,
|
||||
STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN,
|
||||
STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
|
||||
OLD_STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS,
|
||||
STUN_ATTRIBUTE_TIMER_VAL,
|
||||
STUN_ATTRIBUTE_RESERVATION_TOKEN,
|
||||
STUN_ATTRIBUTE_PRIORITY,
|
||||
STUN_ATTRIBUTE_PADDING,
|
||||
STUN_ATTRIBUTE_RESPONSE_PORT,
|
||||
STUN_ATTRIBUTE_CONNECTION_ID,
|
||||
STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY,
|
||||
STUN_ATTRIBUTE_ADDRESS_ERROR_CODE,
|
||||
STUN_ATTRIBUTE_SOFTWARE,
|
||||
STUN_ATTRIBUTE_ALTERNATE_SERVER,
|
||||
STUN_ATTRIBUTE_FINGERPRINT,
|
||||
STUN_ATTRIBUTE_ICE_CONTROLLED,
|
||||
STUN_ATTRIBUTE_RESPONSE_ORIGIN,
|
||||
STUN_ATTRIBUTE_OTHER_ADDRESS,
|
||||
STUN_ATTRIBUTE_THIRD_PARTY_AUTHORIZATION,
|
||||
STUN_ATTRIBUTE_ORIGIN,
|
||||
STUN_ATTRIBUTE_MOBILITY_TICKET,
|
||||
STUN_ATTRIBUTE_NEW_BANDWIDTH,
|
||||
};
|
||||
|
||||
#define kNumAttrTypes (sizeof(kAllAttrTypes) / sizeof(kAllAttrTypes[0]))
|
||||
|
||||
/* All address-type attributes that use XOR or plain address encoding */
|
||||
static const uint16_t kAddrAttrs[] = {
|
||||
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,
|
||||
};
|
||||
|
||||
#define kNumAddrAttrs (sizeof(kAddrAttrs) / sizeof(kAddrAttrs[0]))
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buf[kMaxInputLength];
|
||||
memcpy(buf, Data, Size);
|
||||
|
||||
/* Gate on valid STUN envelope so the fuzzer focuses on attribute internals */
|
||||
if (!stun_is_command_message_str(buf, Size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Phase 1: iterate all attributes via the TLV chain */
|
||||
stun_attr_ref sar = stun_attr_get_first_str(buf, Size);
|
||||
while (sar) {
|
||||
int attr_type = stun_attr_get_type(sar);
|
||||
int attr_len = stun_attr_get_len(sar);
|
||||
const uint8_t *attr_val = stun_attr_get_value(sar);
|
||||
bool is_addr = stun_attr_is_addr(sar);
|
||||
(void)attr_type;
|
||||
(void)attr_len;
|
||||
(void)attr_val;
|
||||
(void)is_addr;
|
||||
|
||||
sar = stun_attr_get_next_str(buf, Size, sar);
|
||||
}
|
||||
|
||||
/* Phase 2: extract typed values for address attributes */
|
||||
ioa_addr addr;
|
||||
for (size_t i = 0; i < kNumAddrAttrs; i++) {
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, kAddrAttrs[i]);
|
||||
if (sar) {
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
stun_attr_get_addr_str(buf, Size, sar, &addr, NULL);
|
||||
}
|
||||
/* Also test the combined find+decode helper */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
stun_attr_get_first_addr_str(buf, Size, kAddrAttrs[i], &addr, NULL);
|
||||
}
|
||||
|
||||
/* Phase 3: scalar attribute extractors */
|
||||
|
||||
/* Channel number */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANNEL_NUMBER);
|
||||
if (sar) {
|
||||
uint16_t chn = stun_attr_get_channel_number(sar);
|
||||
(void)chn;
|
||||
}
|
||||
/* Also via the first-channel helper */
|
||||
{
|
||||
uint16_t first_chn = stun_attr_get_first_channel_number_str(buf, Size);
|
||||
(void)first_chn;
|
||||
}
|
||||
|
||||
/* Requested address family */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY);
|
||||
if (sar) {
|
||||
int fam = stun_get_requested_address_family(sar);
|
||||
(void)fam;
|
||||
}
|
||||
|
||||
/* Additional address family (RFC 8656) */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_ADDITIONAL_ADDRESS_FAMILY);
|
||||
if (sar) {
|
||||
int fam = stun_get_requested_address_family(sar);
|
||||
(void)fam;
|
||||
}
|
||||
|
||||
/* Even port */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_EVEN_PORT);
|
||||
if (sar) {
|
||||
uint8_t ep = stun_attr_get_even_port(sar);
|
||||
(void)ep;
|
||||
}
|
||||
|
||||
/* Bandwidth */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_BANDWIDTH);
|
||||
if (sar) {
|
||||
band_limit_t bw = stun_attr_get_bandwidth(sar);
|
||||
(void)bw;
|
||||
}
|
||||
|
||||
/* New bandwidth (experimental) */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_NEW_BANDWIDTH);
|
||||
if (sar) {
|
||||
band_limit_t bw = stun_attr_get_bandwidth(sar);
|
||||
(void)bw;
|
||||
}
|
||||
|
||||
/* Reservation token */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESERVATION_TOKEN);
|
||||
if (sar) {
|
||||
uint64_t tok = stun_attr_get_reservation_token_value(sar);
|
||||
(void)tok;
|
||||
}
|
||||
|
||||
/* RFC 5780: CHANGE-REQUEST */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_CHANGE_REQUEST);
|
||||
if (sar) {
|
||||
bool change_ip = false, change_port = false;
|
||||
stun_attr_get_change_request_str(sar, &change_ip, &change_port);
|
||||
}
|
||||
|
||||
/* RFC 5780: RESPONSE-PORT */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_RESPONSE_PORT);
|
||||
if (sar) {
|
||||
int rport = stun_attr_get_response_port_str(sar);
|
||||
(void)rport;
|
||||
}
|
||||
|
||||
/* RFC 5780: PADDING */
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, STUN_ATTRIBUTE_PADDING);
|
||||
if (sar) {
|
||||
int pad_len = stun_attr_get_padding_len_str(sar);
|
||||
(void)pad_len;
|
||||
}
|
||||
|
||||
/* Phase 4: error response parsing */
|
||||
{
|
||||
int err_code = 0;
|
||||
uint8_t err_msg[1024] = {0};
|
||||
stun_is_error_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg));
|
||||
}
|
||||
|
||||
/* Challenge response parsing (401/438 with realm+nonce) */
|
||||
{
|
||||
int err_code = 0;
|
||||
uint8_t err_msg[1024] = {0};
|
||||
uint8_t realm[STUN_MAX_REALM_SIZE + 1] = {0};
|
||||
uint8_t nonce[STUN_MAX_NONCE_SIZE + 1] = {0};
|
||||
uint8_t server_name[STUN_MAX_SERVER_NAME_SIZE + 1] = {0};
|
||||
bool oauth = false;
|
||||
stun_is_challenge_response_str(buf, Size, &err_code, err_msg, sizeof(err_msg), realm, nonce, server_name, &oauth);
|
||||
}
|
||||
|
||||
/* Phase 5: search for every defined attribute type */
|
||||
for (size_t i = 0; i < kNumAttrTypes; i++) {
|
||||
sar = stun_attr_get_first_by_type_str(buf, Size, kAllAttrTypes[i]);
|
||||
if (sar) {
|
||||
(void)stun_attr_get_type(sar);
|
||||
(void)stun_attr_get_len(sar);
|
||||
(void)stun_attr_get_value(sar);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,48 +1,170 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#define kMinInputLength 10
|
||||
#define kMaxInputLength 5120
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data,
|
||||
size_t Size) { // stunclient.c
|
||||
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 1;
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 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)) {
|
||||
/* Message type and method extraction */
|
||||
uint16_t method = stun_get_method_str(buf.buf, buf.len);
|
||||
uint16_t msg_type = stun_get_msg_type_str(buf.buf, buf.len);
|
||||
(void)method;
|
||||
(void)msg_type;
|
||||
|
||||
if (stun_is_response(&buf)) {
|
||||
if (stun_is_success_response(&buf)) {
|
||||
if (stun_is_binding_response(&buf)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Indication and error response checks */
|
||||
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));
|
||||
if (!stun_is_command_message(&buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return 1;
|
||||
(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);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* https://opensource.org/license/bsd-3-clause
|
||||
*
|
||||
* Fuzzing target for STUN message integrity across all SHA types
|
||||
* and credential modes.
|
||||
*
|
||||
* Exercises stun_check_message_integrity_str() with:
|
||||
* - SHATYPE_SHA1, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512
|
||||
* - TURN_CREDENTIALS_SHORT_TERM, TURN_CREDENTIALS_LONG_TERM
|
||||
*
|
||||
* The existing FuzzStun only tests SHA1 with short-term credentials.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ns_turn_msg.h"
|
||||
#include "ns_turn_msg_defs.h"
|
||||
|
||||
#define kMinInputLength STUN_HEADER_LENGTH
|
||||
#define kMaxInputLength 5120
|
||||
|
||||
static const SHATYPE kShaTypes[] = {SHATYPE_SHA1, SHATYPE_SHA256, SHATYPE_SHA384, SHATYPE_SHA512};
|
||||
|
||||
#define kNumShaTypes (sizeof(kShaTypes) / sizeof(kShaTypes[0]))
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < kMinInputLength || Size > kMaxInputLength) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buf[kMaxInputLength];
|
||||
|
||||
uint8_t uname[STUN_MAX_USERNAME_SIZE + 1] = "fuzzuser";
|
||||
uint8_t realm[STUN_MAX_REALM_SIZE + 1] = "fuzz.realm";
|
||||
uint8_t upwd[STUN_MAX_PWD_SIZE + 1] = "VOkJxbRl1RmTxUk/WvJxBt";
|
||||
|
||||
for (size_t s = 0; s < kNumShaTypes; s++) {
|
||||
/* Short-term credentials */
|
||||
memcpy(buf, Data, Size);
|
||||
stun_is_command_message_full_check_str(buf, Size, 1, NULL);
|
||||
stun_check_message_integrity_str(TURN_CREDENTIALS_SHORT_TERM, buf, Size, uname, realm, upwd, kShaTypes[s]);
|
||||
|
||||
/* Long-term credentials */
|
||||
memcpy(buf, Data, Size);
|
||||
stun_check_message_integrity_str(TURN_CREDENTIALS_LONG_TERM, buf, Size, uname, realm, upwd, kShaTypes[s]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user