mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 02:53:35 +00:00
add ncm receive-path fuzz harness with out-of-bounds seed
folds the reproducer for the recv_validate_datagram bound into a small self-contained fuzz target. it feeds a raw ntb straight into the validator (the driver is pulled in so the static function is reachable) and ships the crafted 64-byte ntb as a seed. the seed trips an asan heap-buffer-overflow against the unpatched driver and is rejected cleanly with the wLength bound in place.
This commit is contained in:
40
test/fuzz/device/net_ncm/Makefile
Normal file
40
test/fuzz/device/net_ncm/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
# Focused fuzz harness for the NCM receive path (recv_validate_datagram).
|
||||
#
|
||||
# It is self-contained: it #includes src/class/net/ncm_device.c and stubs the
|
||||
# few usbd/glue symbols, so it does not link the whole device stack.
|
||||
#
|
||||
# make # build the libFuzzer target: ./net_ncm
|
||||
# make regression # build + replay the crafted seed under ASan (no engine)
|
||||
# make clean
|
||||
|
||||
TOP := ../../../..
|
||||
|
||||
CC ?= clang
|
||||
|
||||
INC += \
|
||||
$(TOP)/src \
|
||||
.
|
||||
|
||||
CFLAGS += $(addprefix -I,$(INC)) -g -O1 -fsanitize=address
|
||||
|
||||
FUZZ_FLAGS := -fsanitize=fuzzer
|
||||
|
||||
.PHONY: all regression clean get-deps
|
||||
|
||||
all: net_ncm
|
||||
|
||||
# No external dependencies (the harness only needs the in-tree NCM driver).
|
||||
get-deps:
|
||||
|
||||
# libFuzzer supplies its own main(), so drop the standalone driver here.
|
||||
net_ncm: fuzz.c
|
||||
$(CC) $(CFLAGS) $(FUZZ_FLAGS) -DNO_MAIN -o $@ $<
|
||||
|
||||
# Standalone build with a plain main(): replays the crafted seed and any extra
|
||||
# corpus files given as arguments. Used as a deterministic regression.
|
||||
regression: fuzz.c
|
||||
$(CC) $(CFLAGS) -o net_ncm_regression $<
|
||||
./net_ncm_regression
|
||||
|
||||
clean:
|
||||
$(RM) -rf net_ncm net_ncm_regression *.dSYM
|
||||
140
test/fuzz/device/net_ncm/fuzz.c
Normal file
140
test/fuzz/device/net_ncm/fuzz.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2026 Ha Thach (tinyusb.org)
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
// Focused fuzz harness for the NCM receive path. It feeds a raw NCM Transfer
|
||||
// Block (NTB), i.e. host-controlled bytes off the OUT endpoint, straight into
|
||||
// recv_validate_datagram() which is the function that decides whether an
|
||||
// incoming NTB is well formed before the driver walks its datagram array.
|
||||
//
|
||||
// recv_validate_datagram() is static, so the driver is pulled in by #include so
|
||||
// the harness can reach it. The referenced usbd/glue symbols are stubbed below;
|
||||
// none of them are exercised by the validation path.
|
||||
//
|
||||
// The seed in net_ncm_seed_corpus.zip crafts an NTB whose first NDP carries a
|
||||
// wLength of 0xfff0 while the transfer itself is only 64 bytes. Before the
|
||||
// wNdpIndex + wLength bound was added, max_ndx was derived from that wLength and
|
||||
// the ndp16_datagram[] walk ran tens of kB past ntb->data (a 3200 byte buffer);
|
||||
// this reproduces that out-of-bounds read under -fsanitize=address.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "class/net/ncm_device.c"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stubs: referenced by the NCM driver, unused by recv_validate_datagram
|
||||
//--------------------------------------------------------------------+
|
||||
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) {
|
||||
(void) rhport; (void) ep_addr; (void) buffer; (void) total_bytes; (void) is_isr;
|
||||
return true;
|
||||
}
|
||||
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport; (void) ep_addr;
|
||||
return false;
|
||||
}
|
||||
bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
|
||||
(void) rhport; (void) desc_ep;
|
||||
return true;
|
||||
}
|
||||
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const *p_desc, uint8_t ep_count, uint8_t xfer_type,
|
||||
uint8_t *ep_out, uint8_t *ep_in) {
|
||||
(void) rhport; (void) p_desc; (void) ep_count; (void) xfer_type; (void) ep_out; (void) ep_in;
|
||||
return true;
|
||||
}
|
||||
bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const *request, void *buffer, uint16_t len) {
|
||||
(void) rhport; (void) request; (void) buffer; (void) len;
|
||||
return true;
|
||||
}
|
||||
bool tud_control_status(uint8_t rhport, tusb_control_request_t const *request) {
|
||||
(void) rhport; (void) request;
|
||||
return true;
|
||||
}
|
||||
tusb_speed_t tud_speed_get(void) {
|
||||
return TUSB_SPEED_FULL;
|
||||
}
|
||||
bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
|
||||
(void) src; (void) size;
|
||||
return true;
|
||||
}
|
||||
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
|
||||
(void) dst; (void) ref; (void) arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Fuzz entry
|
||||
//--------------------------------------------------------------------+
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
// Give the NTB a heap allocation of the exact receive-buffer size so any read
|
||||
// past ntb->data is caught by AddressSanitizer.
|
||||
recv_ntb_t *ntb = (recv_ntb_t *) malloc(sizeof(recv_ntb_t));
|
||||
if (ntb == NULL) {
|
||||
return 0;
|
||||
}
|
||||
size_t n = size < sizeof(ntb->data) ? size : sizeof(ntb->data);
|
||||
memset(ntb->data, 0, sizeof(ntb->data));
|
||||
memcpy(ntb->data, data, n);
|
||||
recv_validate_datagram(ntb, (uint32_t) size);
|
||||
free(ntb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Standalone driver (built when there is no libFuzzer engine): replays the
|
||||
// crafted NTB, or any corpus files passed on the command line.
|
||||
//--------------------------------------------------------------------+
|
||||
#ifndef NO_MAIN
|
||||
static void run_crafted_seed(void) {
|
||||
uint8_t buf[64];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
// NTH16: signature, wHeaderLength = sizeof(nth16_t), wSequence, wBlockLength, wNdpIndex
|
||||
uint32_t nth_sig = NTH16_SIGNATURE;
|
||||
memcpy(buf + 0, &nth_sig, 4);
|
||||
uint16_t v;
|
||||
v = sizeof(nth16_t); memcpy(buf + 4, &v, 2);
|
||||
v = 0; memcpy(buf + 6, &v, 2);
|
||||
v = sizeof(buf); memcpy(buf + 8, &v, 2);
|
||||
v = sizeof(nth16_t); memcpy(buf + 10, &v, 2);
|
||||
|
||||
// NDP16 at wNdpIndex: signature, wLength (unbounded pre-fix), wNextNdpIndex
|
||||
uint32_t ndp_sig = NDP16_SIGNATURE_NCM0;
|
||||
memcpy(buf + sizeof(nth16_t) + 0, &ndp_sig, 4);
|
||||
v = 0xFFF0; memcpy(buf + sizeof(nth16_t) + 4, &v, 2);
|
||||
v = 0; memcpy(buf + sizeof(nth16_t) + 6, &v, 2);
|
||||
|
||||
LLVMFuzzerTestOneInput(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
run_crafted_seed();
|
||||
return 0;
|
||||
}
|
||||
for (int i = 1; i < argc; i++) {
|
||||
FILE *f = fopen(argv[i], "rb");
|
||||
if (f == NULL) {
|
||||
continue;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
long len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (len > 0) {
|
||||
uint8_t *buf = (uint8_t *) malloc((size_t) len);
|
||||
if (buf != NULL && fread(buf, 1, (size_t) len, f) == (size_t) len) {
|
||||
LLVMFuzzerTestOneInput(buf, (size_t) len);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
BIN
test/fuzz/device/net_ncm/net_ncm_seed_corpus.zip
Normal file
BIN
test/fuzz/device/net_ncm/net_ncm_seed_corpus.zip
Normal file
Binary file not shown.
24
test/fuzz/device/net_ncm/tusb_config.h
Normal file
24
test/fuzz/device/net_ncm/tusb_config.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2026 Ha Thach (tinyusb.org)
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
// Minimal configuration to build the NCM device driver for the receive-path
|
||||
// fuzz harness. Only the NCM class is enabled; no root hub / DCD is needed
|
||||
// because the harness drives recv_validate_datagram() directly.
|
||||
#define CFG_TUSB_MCU OPT_MCU_NONE
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
|
||||
#define CFG_TUD_ENABLED 1
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
|
||||
// Network class has 2 drivers: ECM/RNDIS and NCM. Fuzz the NCM parser here.
|
||||
#define CFG_TUD_ECM_RNDIS 0
|
||||
#define CFG_TUD_NCM 1
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user