Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 148 additions & 79 deletions tests/oss-fuzz/network_message_target.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,154 @@
#include <stdlib.h>
#include <string.h>

/* Test message parsing and processing functions from coap_net.c */
/* Test UDP datagram dispatch and message handling */
static void
test_message_processing(coap_context_t *ctx, coap_session_t *session,
uint8_t *data, size_t size) {
test_dgram_dispatch(coap_context_t *ctx, coap_session_t *session,
uint8_t *data, size_t size) {
if (!ctx || !session || !data || size < 4) {
return;
}

coap_pdu_t *pdu = coap_pdu_init(0, 0, 0, size);
session->state = COAP_SESSION_STATE_ESTABLISHED;
coap_handle_dgram(ctx, session, data, size);
}

/* Test retransmission queue operations (coap_wait_ack, peek_next, pop_next) */
static void
test_queue_operations(coap_context_t *ctx, coap_session_t *session,
const uint8_t *data, size_t size) {
if (!ctx || !session || size < 8) {
return;
}

coap_pdu_t *pdu = coap_pdu_init(COAP_MESSAGE_CON,
COAP_REQUEST_CODE_GET,
coap_new_message_id(session),
64);
if (!pdu) {
return;
}

/* Parse raw message data as CoAP PDU */
if (coap_pdu_parse(session->proto, data, size, pdu) > 0) {
/* Successfully parsed - exercise PDU accessor functions */
coap_pdu_get_type(pdu);
coap_pdu_get_code(pdu);
coap_pdu_get_mid(pdu);
size_t token_len = (data[0] % 8) + 1;
if (token_len <= size) {
coap_add_token(pdu, token_len, data);
}

coap_queue_t *node = coap_new_node();
if (node) {
node->session = session;
node->pdu = pdu;
node->id = pdu->mid;
node->timeout = 1000;

coap_wait_ack(ctx, session, node);

/* Iterate through PDU options */
coap_opt_iterator_t opt_iter;
coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
while (coap_option_next(&opt_iter)) {
coap_queue_t *peek = coap_peek_next(ctx);
if (peek) {
(void)peek;
}

/* Access payload data */
size_t payload_len;
const uint8_t *payload;
if (coap_get_data(pdu, &payload_len, &payload)) {
coap_queue_t *popped = coap_pop_next(ctx);
if (popped) {
coap_delete_node(popped);
}
} else {
coap_delete_pdu(pdu);
}

coap_delete_pdu(pdu);
}

/* Test message sending functions from coap_net.c */
/* Test TCP transport and CSM signaling */
static void
test_send_functions(coap_session_t *session, coap_pdu_t *pdu) {
if (!session || !pdu) {
test_reliable_transport(coap_context_t *ctx, coap_session_t *session,
uint8_t *data, size_t size) {
if (!ctx || !session || !data || size < 8) {
return;
}

/* Test ACK and RST sending */
coap_send_ack(session, pdu);
coap_send_rst(session, pdu);
session->proto = COAP_PROTO_TCP;
session->state = COAP_SESSION_STATE_ESTABLISHED;
session->type = COAP_SESSION_TYPE_CLIENT;

/* Test error response generation */
if (COAP_PDU_IS_REQUEST(pdu)) {
coap_send_error(session, pdu, COAP_RESPONSE_CODE(400), NULL);
coap_send_error(session, pdu, COAP_RESPONSE_CODE(404), NULL);
coap_send_error(session, pdu, COAP_RESPONSE_CODE(500), NULL);
coap_pdu_t *pdu = coap_pdu_init(COAP_MESSAGE_CON,
COAP_SIGNALING_CODE_CSM,
0,
64);
if (!pdu) {
return;
}

if (size >= 4) {
uint32_t max_msg_size = ((uint32_t)data[0] << 24) |
((uint32_t)data[1] << 16) |
((uint32_t)data[2] << 8) |
(uint32_t)data[3];
if (max_msg_size >= 64) {
uint8_t buf[4];
coap_insert_option(pdu, COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE,
coap_encode_var_safe(buf, sizeof(buf), max_msg_size),
buf);
}
}

coap_dispatch(ctx, session, pdu);

coap_delete_pdu(pdu);
}

/* Test message queue operations from coap_net.c */
/* Test session state transitions and send operations */
static void
test_queue_operations(coap_context_t *ctx) {
if (!ctx) {
test_session_operations(coap_context_t *ctx, coap_session_t *session,
const uint8_t *data, size_t size) {
if (!ctx || !session || !data || size < 16) {
return;
}

/* Test queue management functions */
coap_queue_t *node = coap_new_node();
if (node) {
/* peek_next and pop_next exercise queue code paths */
(void)coap_peek_next(ctx);
(void)coap_pop_next(ctx);
coap_delete_node(node);
coap_session_state_t states[] = {
COAP_SESSION_STATE_NONE,
COAP_SESSION_STATE_CONNECTING,
COAP_SESSION_STATE_HANDSHAKE,
COAP_SESSION_STATE_CSM,
COAP_SESSION_STATE_ESTABLISHED
};

session->state = states[data[0] % 5];

coap_pdu_t *pdu = coap_pdu_init(COAP_MESSAGE_CON,
COAP_REQUEST_CODE_GET,
coap_new_message_id(session),
128);
if (!pdu) {
return;
}

size_t token_len = (data[1] % 8) + 1;
if (token_len + 2 <= size) {
coap_add_token(pdu, token_len, &data[2]);
}

if (size >= token_len + 10) {
size_t path_len = (data[token_len + 2] % 16) + 1;
if (token_len + 2 + path_len + 1 <= size) {
coap_add_option(pdu, COAP_OPTION_URI_PATH, path_len,
&data[token_len + 3]);
}
}

if (session->state == COAP_SESSION_STATE_ESTABLISHED) {
coap_send_ack(session, pdu);
coap_send_rst(session, pdu);

if (COAP_PDU_IS_REQUEST(pdu)) {
coap_send_error(session, pdu, COAP_RESPONSE_CODE(400), NULL);
coap_send_error(session, pdu, COAP_RESPONSE_CODE(404), NULL);
coap_send_error(session, pdu, COAP_RESPONSE_CODE(500), NULL);
}
}

coap_delete_pdu(pdu);
}

/* Test context configuration with fuzzer input */
/* Test context configuration */
static void
test_context_config(coap_context_t *ctx, const uint8_t *data, size_t size) {
if (!ctx || size < 16) {
Expand All @@ -86,14 +161,12 @@ test_context_config(coap_context_t *ctx, const uint8_t *data, size_t size) {
coap_context_set_max_idle_sessions(ctx, data[1]);
coap_context_set_max_handshake_sessions(ctx, data[2]);
coap_context_set_session_timeout(ctx, data[3]);
/* Valid token size range is 8-4096 */
size_t token_size = 8 + (data[4] % 256);
coap_context_set_max_token_size(ctx, token_size);

if (size >= 20) {
size_t csm_max_message_size = ((uint32_t)data[16] << 24) | (data[17] << 16) |
(data[18] << 8) | data[19];
/* CSM max message size must be >= 64 per spec */
if (csm_max_message_size >= 64) {
coap_context_set_csm_max_message_size(ctx, csm_max_message_size);
}
Expand All @@ -116,7 +189,6 @@ test_psk_config(coap_context_t *ctx, const uint8_t *data, size_t size) {
size_t hint_len = (data[1] % 16) + 1;

if (size >= 32 + key_len + hint_len) {
/* Create null-terminated hint string (required by API contract) */
char *hint = malloc(hint_len + 1);
if (!hint) {
return;
Expand All @@ -138,7 +210,6 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
coap_address_t local_addr, remote_addr;
uint8_t *data_copy = NULL;

/* Require minimum size for meaningful testing */
if (size < 8) {
return 0;
}
Expand All @@ -152,7 +223,8 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

/* Initialize CoAP library */
coap_startup();
coap_set_log_level(COAP_LOG_DEBUG);
coap_set_log_level(COAP_LOG_EMERG);
coap_debug_set_packet_loss("100%");

/* Setup local address */
coap_address_init(&local_addr);
Expand All @@ -169,57 +241,54 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
test_context_config(ctx, data, size);
}

/* Test PSK configuration conditionally */
if (size >= 32) {
test_psk_config(ctx, data, size);
}

/* Setup remote address for session creation */
/* Create client session for testing */
coap_address_init(&remote_addr);
remote_addr.addr.sa.sa_family = AF_INET;
memcpy(&remote_addr.addr.sin.sin_addr.s_addr,
&data[size > 4 ? size - 4 : 0],
size > 4 ? 4 : size);
remote_addr.addr.sin.sin_port = htons(5683);

/* Create client session for testing */
session = coap_new_client_session(ctx, NULL, &remote_addr, COAP_PROTO_UDP);
coap_proto_t proto = COAP_PROTO_UDP;
if (size >= 2) {
if (data[0] & 0x10) {
proto = COAP_PROTO_TCP;
#if !COAP_DISABLE_TCP
} else if (data[0] & 0x08) {
proto = COAP_PROTO_DTLS;
#endif
}
}

session = coap_new_client_session(ctx, NULL, &remote_addr, proto);
if (!session) {
goto cleanup;
}

/* Partition fuzz input for different test scenarios */
size_t msg_offset = 0;

/* Test 1: Message processing with full input */
if (size >= msg_offset + 4) {
if (size >= 8 && session->proto == COAP_PROTO_UDP) {
size_t msg_offset = size >= 16 ? 8 : 4;
size_t msg_len = size - msg_offset;
if (msg_len > 4) {
test_message_processing(ctx, session, data_copy + msg_offset, msg_len);
if (msg_len >= 4) {
test_dgram_dispatch(ctx, session, data_copy + msg_offset, msg_len);
}
}

/* Test 2: Send functions if we can parse as valid PDU */
if (size >= 4) {
coap_pdu_t *pdu = coap_pdu_init(0, 0, 0, size);
if (pdu) {
if (coap_pdu_parse(COAP_PROTO_UDP, data_copy, size, pdu)) {
test_send_functions(session, pdu);
}
coap_delete_pdu(pdu);
}
if (size >= 16) {
test_queue_operations(ctx, session, data + 1, size - 1);
}

/* Test 3: Queue operations */
test_queue_operations(ctx);
#if !COAP_DISABLE_TCP
if (size >= 16) {
test_reliable_transport(ctx, session, data_copy + 4, size - 4);
}
#endif

/* Test 4: Message processing with alternative data slice */
if (size >= 16) {
size_t alt_offset = size / 2;
size_t alt_len = size - alt_offset;
if (alt_len >= 4) {
test_message_processing(ctx, session, data_copy + alt_offset, alt_len);
}
test_session_operations(ctx, session, data, size);
}

if (size >= 32) {
test_psk_config(ctx, data, size);
}

cleanup:
Expand Down
Loading