diff --git a/.github/workflows/build_aarch64.yml b/.github/workflows/build_aarch64.yml index ccfca63169..c552a9574f 100644 --- a/.github/workflows/build_aarch64.yml +++ b/.github/workflows/build_aarch64.yml @@ -3,17 +3,17 @@ name: Build AArch64 on: push: paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' pull_request: branches: [ master ] paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml index d1b94c47b7..c16fc0d45d 100644 --- a/.github/workflows/build_arm.yml +++ b/.github/workflows/build_arm.yml @@ -3,17 +3,17 @@ name: Build ARM on: push: paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' pull_request: branches: [ master ] paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/build_esp.yml b/.github/workflows/build_esp.yml index 7f2c292faa..2e2859c952 100644 --- a/.github/workflows/build_esp.yml +++ b/.github/workflows/build_esp.yml @@ -3,17 +3,17 @@ name: Build ESP on: push: paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' pull_request: branches: [ master ] paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/build_msp430.yml b/.github/workflows/build_msp430.yml index f41344e308..7e1c5b128b 100644 --- a/.github/workflows/build_msp430.yml +++ b/.github/workflows/build_msp430.yml @@ -3,17 +3,17 @@ name: Build MSP430 on: push: paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' pull_request: branches: [ master ] paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/build_renesas.yml b/.github/workflows/build_renesas.yml index d6550ed6d2..d9254dea8b 100644 --- a/.github/workflows/build_renesas.yml +++ b/.github/workflows/build_renesas.yml @@ -3,17 +3,17 @@ name: Build Renesas on: push: paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' pull_request: branches: [ master ] paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/build_riscv.yml b/.github/workflows/build_riscv.yml index 257050a53a..026da7bdc9 100644 --- a/.github/workflows/build_riscv.yml +++ b/.github/workflows/build_riscv.yml @@ -3,17 +3,17 @@ name: Build RISC-V on: push: paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' pull_request: branches: [ master ] paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 96a59069bf..8a1101ac0d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -34,4 +34,13 @@ jobs: # Install Ceedling gem install ceedling cd test/unit-test - ceedling test:all \ No newline at end of file + ceedling test:all + + - name: Build Fuzzer + run: | + fuzz_harness=$(ls -d test/fuzz/device/*/) + for h in $fuzz_harness + do + make -C $h get-deps + make -C $h all + done diff --git a/.github/workflows/test_hardware.yml b/.github/workflows/test_hardware.yml index 622b079daf..601a97e995 100644 --- a/.github/workflows/test_hardware.yml +++ b/.github/workflows/test_hardware.yml @@ -3,17 +3,17 @@ name: Hardware Test on: push: paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' pull_request: branches: [ master ] paths: - - 'src' - - 'examples' - - 'lib' - - 'hw' + - 'src/**' + - 'examples/**' + - 'lib/**' + - 'hw/**' # Hardware in the loop (HIL) # Current self-hosted instance is running on an EPYC 7232 server hosted by HiFiPhile user diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 77c73030a2..7b1e08d5d4 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -171,6 +171,7 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) bufsize); // flush if queue more than packet size + // may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) ) { tud_cdc_n_write_flush(itf); diff --git a/test/fuzz/dcd_fuzz.cc b/test/fuzz/dcd_fuzz.cc new file mode 100644 index 0000000000..7153e20f07 --- /dev/null +++ b/test/fuzz/dcd_fuzz.cc @@ -0,0 +1,208 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#include "device/dcd.h" +#include "fuzz/fuzz_private.h" +#include +#include +#include + +#define UNUSED(x) (void)(x) + +//--------------------------------------------------------------------+ +// State tracker +//--------------------------------------------------------------------+ +struct State { + bool interrupts_enabled; + bool sof_enabled; + uint8_t address; +}; + +static State state = {false, 0, 0}; + +//--------------------------------------------------------------------+ +// Controller API +// All no-ops as we are fuzzing. +//--------------------------------------------------------------------+ +extern "C" { +void dcd_init(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_int_handler(uint8_t rhport) { + assert(_fuzz_data_provider.has_value()); + + if (!state.interrupts_enabled) { + return; + } + + // Choose if we want to generate a signal based on the fuzzed data. + if (_fuzz_data_provider->ConsumeBool()) { + dcd_event_bus_signal( + rhport, + // Choose a random event based on the fuzz data. + (dcd_eventid_t)_fuzz_data_provider->ConsumeIntegralInRange( + DCD_EVENT_INVALID + 1, DCD_EVENT_COUNT - 1), + // Identify trigger as either an interrupt or a syncrhonous call + // depending on fuzz data. + _fuzz_data_provider->ConsumeBool()); + } + + if (_fuzz_data_provider->ConsumeBool()) { + constexpr size_t kSetupFrameLength = 8; + std::vector setup = + _fuzz_data_provider->ConsumeBytes(kSetupFrameLength); + // Fuzz consumer may return less than requested. If this is the case + // we want to make sure that at least that length is allocated and available + // to the signal handler. + if (setup.size() != kSetupFrameLength) { + setup.resize(kSetupFrameLength); + } + dcd_event_setup_received(rhport, setup.data(), + // Identify trigger as either an interrupt or a + // syncrhonous call depending on fuzz data. + _fuzz_data_provider->ConsumeBool()); + } +} + +void dcd_int_enable(uint8_t rhport) { + state.interrupts_enabled = true; + UNUSED(rhport); + return; +} + +void dcd_int_disable(uint8_t rhport) { + state.interrupts_enabled = false; + UNUSED(rhport); + return; +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + UNUSED(rhport); + state.address = dev_addr; + // Respond with status. + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + return; +} + +void dcd_remote_wakeup(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_connect(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_disconnect(uint8_t rhport) { + UNUSED(rhport); + return; +} + +void dcd_sof_enable(uint8_t rhport, bool en) { + state.sof_enabled = en; + UNUSED(rhport); + return; +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { + UNUSED(rhport); + UNUSED(desc_ep); + return _fuzz_data_provider->ConsumeBool(); +} + +// Close all non-control endpoints, cancel all pending transfers if any. +// Invoked when switching from a non-zero Configuration by SET_CONFIGURE +// therefore required for multiple configuration support. +void dcd_edpt_close_all(uint8_t rhport) { + UNUSED(rhport); + return; +} + +// Close an endpoint. +// Since it is weak, caller must TU_ASSERT this function's existence before +// calling it. +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + UNUSED(rhport); + UNUSED(ep_addr); + return; +} + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to +// notify the stack +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, + uint16_t total_bytes) { + UNUSED(rhport); + UNUSED(buffer); + UNUSED(total_bytes); + + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + std::vector temp = + _fuzz_data_provider->ConsumeBytes(total_bytes); + std::copy(temp.begin(), temp.end(), buffer); + } + // Ignore output data as it's not useful for fuzzing without a more + // complex fuzzed backend. But we need to make sure it's not + // optimised out. + volatile uint8_t *dont_optimise0 = buffer; + volatile uint16_t dont_optimise1 = total_bytes; + UNUSED(dont_optimise0); + UNUSED(dont_optimise1); + + + return _fuzz_data_provider->ConsumeBool(); +} + +/* TODO: implement a fuzzed version of this. +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, + uint16_t total_bytes) {} +*/ + +// Stall endpoint, any queuing transfer should be removed from endpoint +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + + UNUSED(rhport); + UNUSED(ep_addr); + return; +} + +// clear stall, data toggle is also reset to DATA0 +// This API never calls with control endpoints, since it is auto cleared when +// receiving setup packet +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + + UNUSED(rhport); + UNUSED(ep_addr); + return; +} +} \ No newline at end of file diff --git a/test/fuzz/device/cdc/CMakeLists.txt b/test/fuzz/device/cdc/CMakeLists.txt new file mode 100644 index 0000000000..fa6e83b7e6 --- /dev/null +++ b/test/fuzz/device/cdc/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/test/fuzz/device/cdc/Makefile b/test/fuzz/device/cdc/Makefile new file mode 100644 index 0000000000..ee51936b5c --- /dev/null +++ b/test/fuzz/device/cdc/Makefile @@ -0,0 +1,12 @@ +include ../../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c)) +SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc)) + +include ../../rules.mk diff --git a/test/fuzz/device/cdc/src/fuzz.cc b/test/fuzz/device/cdc/src/fuzz.cc new file mode 100644 index 0000000000..d40483ca63 --- /dev/null +++ b/test/fuzz/device/cdc/src/fuzz.cc @@ -0,0 +1,174 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "class/cdc/cdc_device.h" +#include "fuzz/fuzz.h" +#include "tusb.h" +#include +#include +#include + +extern "C" { + +#define FUZZ_ITERATIONS 500 + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +void cdc_task(FuzzedDataProvider *provider); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + FuzzedDataProvider provider(Data, Size); + std::vector callback_data = provider.ConsumeBytes( + provider.ConsumeIntegralInRange(0, Size)); + fuzz_init(callback_data.data(), callback_data.size()); + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + for (int i = 0; i < FUZZ_ITERATIONS; i++) { + if (provider.remaining_bytes() == 0) { + return 0; + } + tud_int_handler(provider.ConsumeIntegral()); + tud_task(); // tinyusb device task + cdc_task(&provider); + } + + return 0; +} + +//--------------------------------------------------------------------+ +// USB CDC +//--------------------------------------------------------------------+ +enum CdcApiFuncs { + kCdcNConnected, + kCdcNGetLineState, + kCdcNGetLineCoding, + kCdcNSetWantedChar, + kCdcNAvailable, + kCdcNRead, + kCdcNReadChar, + kCdcNReadFlush, + kCdcNPeek, + kCdcNWrite, + kCdcNWriteChar, + kCdcNWriteStr, + kCdcNWriteFlush, + kCdcNWriteAvailable, + kCdcNWriteClear, + // We don't need to fuzz tud_cdc_* as they are just wrappers + // calling with n==0. + kMaxValue, +}; + +void cdc_task(FuzzedDataProvider *provider) { + + assert(provider != NULL); + const int kMaxBufferSize = 4096; + switch (provider->ConsumeEnum()) { + case kCdcNConnected: + // TODO: Fuzz interface number + (void)tud_cdc_n_connected(0); + break; + case kCdcNGetLineState: + // TODO: Fuzz interface number + (void)tud_cdc_n_get_line_state(0); + break; + case kCdcNGetLineCoding: { + cdc_line_coding_t coding; + // TODO: Fuzz interface number + (void)tud_cdc_n_get_line_coding(0, &coding); + } break; + case kCdcNSetWantedChar: + // TODO: Fuzz interface number + (void)tud_cdc_n_set_wanted_char(0, provider->ConsumeIntegral()); + break; + case kCdcNAvailable: + // TODO: Fuzz interface number + (void)tud_cdc_n_available(0); + break; + case kCdcNRead: { + std::vector buffer; + buffer.resize(provider->ConsumeIntegralInRange(0, kMaxBufferSize)); + // TODO: Fuzz interface number + (void)tud_cdc_n_read(0, buffer.data(), buffer.size()); + break; + } + case kCdcNReadChar: + // TODO: Fuzz interface number + tud_cdc_n_read_char(0); + break; + case kCdcNReadFlush: + // TODO: Fuzz interface number + tud_cdc_n_read_flush(0); + break; + case kCdcNPeek: { + uint8_t peak = 0; + tud_cdc_n_peek(0, &peak); + break; + } + case kCdcNWrite: { + std::vector buffer = provider->ConsumeBytes( + provider->ConsumeIntegralInRange(0, kMaxBufferSize)); + + // TODO: Fuzz interface number + (void)tud_cdc_n_write(0, buffer.data(), buffer.size()); + } break; + +case kCdcNWriteChar: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_char(0, provider->ConsumeIntegral()); + break; +case kCdcNWriteStr: { + std::string str = provider->ConsumeRandomLengthString(kMaxBufferSize); + // TODO: Fuzz interface number + (void)tud_cdc_n_write_str(0, str.c_str()); + break; +} +case kCdcNWriteFlush: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_flush(0); + break; +case kCdcNWriteAvailable: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_available(0); + break; +case kCdcNWriteClear: + // TODO: Fuzz interface number + (void)tud_cdc_n_write_clear(0); + break; +case kMaxValue: + // Noop. + break; +} +} +} \ No newline at end of file diff --git a/test/fuzz/device/cdc/src/tusb_config.h b/test/fuzz/device/cdc/src/tusb_config.h new file mode 100644 index 0000000000..10a8a825a1 --- /dev/null +++ b/test/fuzz/device/cdc/src/tusb_config.h @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test/fuzz/device/cdc/src/usb_descriptors.cc b/test/fuzz/device/cdc/src/usb_descriptors.cc new file mode 100644 index 0000000000..0f636f05d7 --- /dev/null +++ b/test/fuzz/device/cdc/src/usb_descriptors.cc @@ -0,0 +1,229 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. + * Auto ProductID layout's Bitmap: + * [MSB] HID | CDC [LSB] + */ +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | \ + _PID_MAP(VENDOR, 4)) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + + return (uint8_t const *)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; + +#define EPNUM_CDC_NOTIF 0x81 +#define EPNUM_CDC_OUT 0x02 +#define EPNUM_CDC_IN 0x82 + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) + +// full speed configuration +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and +// other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change +// configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete. device_qualifier descriptor describes +// information about a high-speed capable device that would change if the device +// were operating at the other speed. If not highspeed capable stall this +// request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *)&desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete Configuration descriptor in the other speed +// e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration + : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration + : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456789012", // 3: Serials, should use chip ID + "TinyUSB CDC", // 4: CDC Interface +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t)strlen(str); + if (chr_count > 31) + chr_count = 31; + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/test/fuzz/device/msc/CMakeLists.txt b/test/fuzz/device/msc/CMakeLists.txt new file mode 100644 index 0000000000..fa6e83b7e6 --- /dev/null +++ b/test/fuzz/device/msc/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/test/fuzz/device/msc/Makefile b/test/fuzz/device/msc/Makefile new file mode 100644 index 0000000000..ee51936b5c --- /dev/null +++ b/test/fuzz/device/msc/Makefile @@ -0,0 +1,12 @@ +include ../../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c)) +SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc)) + +include ../../rules.mk diff --git a/test/fuzz/device/msc/src/fuzz.cc b/test/fuzz/device/msc/src/fuzz.cc new file mode 100644 index 0000000000..5689004522 --- /dev/null +++ b/test/fuzz/device/msc/src/fuzz.cc @@ -0,0 +1,62 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "class/cdc/cdc_device.h" +#include "fuzz/fuzz.h" +#include "tusb.h" +#include +#include +#include + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +#define FUZZ_ITERATIONS 500 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + FuzzedDataProvider provider(Data, Size); + std::vector callback_data = provider.ConsumeBytes( + provider.ConsumeIntegralInRange(0, Size)); + fuzz_init(callback_data.data(), callback_data.size()); + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + for (int i = 0; i < FUZZ_ITERATIONS; i++) { + if (provider.remaining_bytes() == 0) { + return 0; + } + tud_int_handler(provider.ConsumeIntegral()); + tud_task(); // tinyusb device task + } + + return 0; +} + diff --git a/test/fuzz/device/msc/src/tusb_config.h b/test/fuzz/device/msc/src/tusb_config.h new file mode 100644 index 0000000000..ca39c6b0a6 --- /dev/null +++ b/test/fuzz/device/msc/src/tusb_config.h @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 1 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test/fuzz/device/msc/src/usb_descriptors.cc b/test/fuzz/device/msc/src/usb_descriptors.cc new file mode 100644 index 0000000000..ded401fc97 --- /dev/null +++ b/test/fuzz/device/msc/src/usb_descriptors.cc @@ -0,0 +1,224 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(MSC, 0) | _PID_MAP(HID, 1) | _PID_MAP(MIDI, 2) | \ + _PID_MAP(VENDOR, 3)) +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + + return (uint8_t const *)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { ITF_NUM_MSC = 0, ITF_NUM_TOTAL }; + +#define EPNUM_MSC_OUT 0x05 +#define EPNUM_MSC_IN 0x85 + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) + +// full speed configuration +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and +// other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change +// configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete. device_qualifier descriptor describes +// information about a high-speed capable device that would change if the device +// were operating at the other speed. If not highspeed capable stall this +// request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *)&desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete Configuration descriptor in the other speed +// e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration + : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration + : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456789012", // 3: Serials, should use chip ID + "TinyUSB MSC", // 4: MSC Interface + +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t)strlen(str); + if (chr_count > 31) + chr_count = 31; + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/test/fuzz/device/net/CMakeLists.txt b/test/fuzz/device/net/CMakeLists.txt new file mode 100644 index 0000000000..fa6e83b7e6 --- /dev/null +++ b/test/fuzz/device/net/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/test/fuzz/device/net/Makefile b/test/fuzz/device/net/Makefile new file mode 100644 index 0000000000..22241fcdc0 --- /dev/null +++ b/test/fuzz/device/net/Makefile @@ -0,0 +1,71 @@ +DEPS_SUBMODULES += lib/lwip + +include ../../../../tools/top.mk +include ../../make.mk + +# suppress warning caused by lwip +CFLAGS += \ + -Wno-error=null-dereference \ + -Wno-error=unused-parameter \ + -Wno-error=unused-variable + +INC += \ + src \ + $(TOP)/hw \ + $(TOP)/lib/lwip/src/include \ + $(TOP)/lib/lwip/src/include/ipv4 \ + $(TOP)/lib/lwip/src/include/lwip/apps \ + $(TOP)/lib/networking + +# Example source +SRC_C += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.c)) +SRC_CXX += $(addprefix $(CURRENT_PATH)/, $(wildcard src/*.cc)) + +# lwip sources +SRC_C += \ + lib/lwip/src/core/altcp.c \ + lib/lwip/src/core/altcp_alloc.c \ + lib/lwip/src/core/altcp_tcp.c \ + lib/lwip/src/core/def.c \ + lib/lwip/src/core/dns.c \ + lib/lwip/src/core/inet_chksum.c \ + lib/lwip/src/core/init.c \ + lib/lwip/src/core/ip.c \ + lib/lwip/src/core/mem.c \ + lib/lwip/src/core/memp.c \ + lib/lwip/src/core/netif.c \ + lib/lwip/src/core/pbuf.c \ + lib/lwip/src/core/raw.c \ + lib/lwip/src/core/stats.c \ + lib/lwip/src/core/sys.c \ + lib/lwip/src/core/tcp.c \ + lib/lwip/src/core/tcp_in.c \ + lib/lwip/src/core/tcp_out.c \ + lib/lwip/src/core/timeouts.c \ + lib/lwip/src/core/udp.c \ + lib/lwip/src/core/ipv4/autoip.c \ + lib/lwip/src/core/ipv4/dhcp.c \ + lib/lwip/src/core/ipv4/etharp.c \ + lib/lwip/src/core/ipv4/icmp.c \ + lib/lwip/src/core/ipv4/igmp.c \ + lib/lwip/src/core/ipv4/ip4.c \ + lib/lwip/src/core/ipv4/ip4_addr.c \ + lib/lwip/src/core/ipv4/ip4_frag.c \ + lib/lwip/src/core/ipv6/dhcp6.c \ + lib/lwip/src/core/ipv6/ethip6.c \ + lib/lwip/src/core/ipv6/icmp6.c \ + lib/lwip/src/core/ipv6/inet6.c \ + lib/lwip/src/core/ipv6/ip6.c \ + lib/lwip/src/core/ipv6/ip6_addr.c \ + lib/lwip/src/core/ipv6/ip6_frag.c \ + lib/lwip/src/core/ipv6/mld6.c \ + lib/lwip/src/core/ipv6/nd6.c \ + lib/lwip/src/netif/ethernet.c \ + lib/lwip/src/netif/slipif.c \ + lib/lwip/src/apps/http/httpd.c \ + lib/lwip/src/apps/http/fs.c \ + lib/networking/dhserver.c \ + lib/networking/dnserver.c \ + lib/networking/rndis_reports.c + +include ../../rules.mk diff --git a/test/fuzz/device/net/src/arch/cc.h b/test/fuzz/device/net/src/arch/cc.h new file mode 100644 index 0000000000..56a0cacf7e --- /dev/null +++ b/test/fuzz/device/net/src/arch/cc.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __CC_H__ +#define __CC_H__ + +//#include "cpu.h" + +typedef int sys_prot_t; + + + +/* define compiler specific symbols */ +#if defined (__ICCARM__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/test/fuzz/device/net/src/fuzz.cc b/test/fuzz/device/net/src/fuzz.cc new file mode 100644 index 0000000000..ea49436651 --- /dev/null +++ b/test/fuzz/device/net/src/fuzz.cc @@ -0,0 +1,99 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include "class/cdc/cdc_device.h" +#include "class/net/net_device.h" +#include "fuzz/fuzz.h" +#include "tusb.h" +#include +#include +#include + +extern "C" { + +#define FUZZ_ITERATIONS 500 + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +void net_task(FuzzedDataProvider *provider); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + FuzzedDataProvider provider(Data, Size); + std::vector callback_data = provider.ConsumeBytes( + provider.ConsumeIntegralInRange(0, Size)); + fuzz_init(callback_data.data(), callback_data.size()); + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + for (int i = 0; i < FUZZ_ITERATIONS; i++) { + if (provider.remaining_bytes() == 0) { + return 0; + } + tud_int_handler(provider.ConsumeIntegral()); + tud_task(); // tinyusb device task + net_task(&provider); + } + + return 0; +} + +//--------------------------------------------------------------------+ +// USB CDC +//--------------------------------------------------------------------+ +enum NetApiFuncs { + kNetworkRecvRenew, + kNetworkCanXmit, + kNetworkXmit, + kMaxValue, +}; + +void net_task(FuzzedDataProvider *provider) { + + assert(provider != NULL); + switch (provider->ConsumeEnum()) { + + case kNetworkRecvRenew: + tud_network_recv_renew(); + break; + case kNetworkCanXmit: + (void)tud_network_can_xmit(provider->ConsumeIntegral()); + case kNetworkXmit: + // TODO: Actually pass real values here later. + tud_network_xmit(NULL, 0); + + case kMaxValue: + // Noop. + break; + } +} +} diff --git a/test/fuzz/device/net/src/lwipopts.h b/test/fuzz/device/net/src/lwipopts.h new file mode 100644 index 0000000000..a215017c7a --- /dev/null +++ b/test/fuzz/device/net/src/lwipopts.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +#define NO_SYS 1 +#define MEM_ALIGNMENT 4 +#define LWIP_RAW 0 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_DHCP 0 +#define LWIP_ICMP 1 +#define LWIP_UDP 1 +#define LWIP_TCP 1 +#define LWIP_IPV4 1 +#define LWIP_IPV6 0 +#define ETH_PAD_SIZE 0 +#define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67)) + +#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/) +#define TCP_SND_BUF (2 * TCP_MSS) +#define TCP_WND (TCP_MSS) + +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 + +#define LWIP_HTTPD_CGI 0 +#define LWIP_HTTPD_SSI 0 +#define LWIP_HTTPD_SSI_INCLUDE_TAG 0 + +#define LWIP_SINGLE_NETIF 1 + +#define PBUF_POOL_SIZE 2 + +#define HTTPD_USE_CUSTOM_FSDATA 0 + +#define LWIP_MULTICAST_PING 1 +#define LWIP_BROADCAST_PING 1 +#define LWIP_IPV6_MLD 0 +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 0 + +#endif /* __LWIPOPTS_H__ */ diff --git a/test/fuzz/device/net/src/tusb_config.h b/test/fuzz/device/net/src/tusb_config.h new file mode 100644 index 0000000000..6ad859337f --- /dev/null +++ b/test/fuzz/device/net/src/tusb_config.h @@ -0,0 +1,122 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// Common Configuration +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// Network class has 2 drivers: ECM/RNDIS and NCM. +// Only one of the drivers can be enabled +#define CFG_TUD_ECM_RNDIS 1 +#define CFG_TUD_NCM (1-CFG_TUD_ECM_RNDIS) + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 + + + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/test/fuzz/device/net/src/usb_descriptors.cc b/test/fuzz/device/net/src/usb_descriptors.cc new file mode 100644 index 0000000000..c600bd8014 --- /dev/null +++ b/test/fuzz/device/net/src/usb_descriptors.cc @@ -0,0 +1,229 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. + * Auto ProductID layout's Bitmap: + * [MSB] HID | CDC [LSB] + */ +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3) | \ + _PID_MAP(VENDOR, 4)) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + static tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + + return (uint8_t const *)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum { ITF_NUM_CDC = 0, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; + +#define EPNUM_CDC_NOTIF 0x81 +#define EPNUM_CDC_OUT 0x02 +#define EPNUM_CDC_IN 0x82 + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) + +// full speed configuration +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and +// other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP notification address and size, EP data + // address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, + EPNUM_CDC_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change +// configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete. device_qualifier descriptor describes +// information about a high-speed capable device that would change if the device +// were operating at the other speed. If not highspeed capable stall this +// request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *)&desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete Configuration descriptor in the other speed +// e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration + : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration + : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456789012", // 3: Serials, should use chip ID + "TinyUSB CDC", // 4: CDC Interface +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t)strlen(str); + if (chr_count > 31) + chr_count = 31; + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/test/fuzz/dicts/cdc.dict b/test/fuzz/dicts/cdc.dict new file mode 100644 index 0000000000..138775cdca --- /dev/null +++ b/test/fuzz/dicts/cdc.dict @@ -0,0 +1,74 @@ +# List of supported OIDs +RNDIS_OID_GEN_SUPPORTED_LIST="\x00\x01\x01\x01" +# Hardware status +RNDIS_OID_GEN_HARDWARE_STATUS="\x00\x01\x01\x02" +# Media types supported (encoded) +RNDIS_OID_GEN_MEDIA_SUPPORTED="\x00\x01\x01\x03" +# Media types in use (encoded) +RNDIS_OID_GEN_MEDIA_IN_USE="\x00\x01\x01\x04" +RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD="\x00\x01\x01\x05" +# Maximum frame size in bytes +RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE="\x00\x01\x01\x06" +# Link speed in units of 100 bps +RNDIS_OID_GEN_LINK_SPEED="\x00\x01\x01\x07" +# Transmit buffer space +RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE="\x00\x01\x01\x08" +# Receive buffer space +RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE="\x00\x01\x01\x09" +# NDIS version number used by the driver +RNDIS_OID_GEN_DRIVER_VERSION="\x00\x01\x01\x10" +# Maximum total packet length in bytes +RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE="\x00\x01\x01\x11" +# Optional protocol flags (encoded) +RNDIS_OID_GEN_PROTOCOL_OPTIONS="\x00\x01\x01\x12" +# Optional NIC flags (encoded) +RNDIS_OID_GEN_MAC_OPTIONS="\x00\x01\x01\x13" +# Whether the NIC is connected to the network +RNDIS_OID_GEN_MEDIA_CONNECT_STATUS="\x00\x01\x01\x14" +# The maximum number of send packets the driver can accept per call to its MiniportSendPacketsfunction +RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS="\x00\x01\x01\x15" +# Vendor-assigned version number of the driver +RNDIS_OID_GEN_VENDOR_DRIVER_VERSION="\x00\x01\x01\x16" +# The custom GUIDs (Globally Unique Identifier) supported by the miniport driver +RNDIS_OID_GEN_SUPPORTED_GUIDS="\x00\x01\x01\x17" +# List of network-layer addresses associated with the binding between a transport and the driver +RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES="\x00\x01\x01\x18" +# Size of packets' additional headers +RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET="\x00\x01\x01\x19" +RNDIS_OID_GEN_MEDIA_CAPABILITIES="\x00\x01\x02\x01" +# Physical media supported by the miniport driver (encoded) +RNDIS_OID_GEN_PHYSICAL_MEDIUM="\x00\x01\x02\x02" +# Permanent station address +RNDIS_OID_802_3_PERMANENT_ADDRESS="\x01\x01\x01\x01" +# Current station address +RNDIS_OID_802_3_CURRENT_ADDRESS="\x01\x01\x01\x02" +# Current multicast address list +RNDIS_OID_802_3_MULTICAST_LIST="\x01\x01\x01\x03" +# Maximum size of multicast address list +RNDIS_OID_802_3_MAXIMUM_LIST_SIZE="\x01\x01\x01\x04" +# Directed packets. Directed packets contain a destination address equal to the station address of the NIC. +RNDIS_PACKET_TYPE_DIRECTED="\x00\x00\x00\x01" +# Multicast address packets sent to addresses in the multicast address list. +RNDIS_PACKET_TYPE_MULTICAST="\x00\x00\x00\x02" +# All multicast address packets, not just the ones enumerated in the multicast address list. +RNDIS_PACKET_TYPE_ALL_MULTICAST="\x00\x00\x00\x04" +# Broadcast packets. +RNDIS_PACKET_TYPE_BROADCAST="\x00\x00\x00\x08" +# All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge. +RNDIS_PACKET_TYPE_SOURCE_ROUTING="\x00\x00\x00\x10" +# Specifies all packets regardless of whether VLAN filtering is enabled or not and whether the VLAN identifier matches or not. +RNDIS_PACKET_TYPE_PROMISCUOUS="\x00\x00\x00\x20" +# SMT packets that an FDDI NIC receives. +RNDIS_PACKET_TYPE_SMT="\x00\x00\x00\x40" +# All packets sent by installed protocols and all packets indicated by the NIC that is identified by a given NdisBindingHandle. +RNDIS_PACKET_TYPE_ALL_LOCAL="\x00\x00\x00\x80" +# Packets sent to the current group address. +RNDIS_PACKET_TYPE_GROUP="\x00\x00\x10\x00" +# All functional address packets, not just the ones in the current functional address. +RNDIS_PACKET_TYPE_ALL_FUNCTIONAL="\x00\x00\x20\x00" +# Functional address packets sent to addresses included in the current functional address. +RNDIS_PACKET_TYPE_FUNCTIONAL="\x00\x00\x40\x00" +# NIC driver frames that a Token Ring NIC receives. +RNDIS_PACKET_TYPE_MAC_FRAME="\x00\x00\x80\x00" +RNDIS_PACKET_TYPE_NO_LOCAL="\x00\x01\x00\x00" + diff --git a/test/fuzz/fuzz.cc b/test/fuzz/fuzz.cc new file mode 100644 index 0000000000..2e4186f1ac --- /dev/null +++ b/test/fuzz/fuzz.cc @@ -0,0 +1,34 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "fuzzer/FuzzedDataProvider.h" +#include + +std::optional _fuzz_data_provider; + +extern "C" int fuzz_init(const uint8_t *data, size_t size) { + _fuzz_data_provider.emplace(data, size); + return 0; +} diff --git a/test/fuzz/fuzz.h b/test/fuzz/fuzz.h new file mode 100644 index 0000000000..6aa4949a11 --- /dev/null +++ b/test/fuzz/fuzz.h @@ -0,0 +1,37 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int fuzz_init(const uint8_t *data, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/test/fuzz/fuzz_private.h b/test/fuzz/fuzz_private.h new file mode 100644 index 0000000000..0d791fcdc7 --- /dev/null +++ b/test/fuzz/fuzz_private.h @@ -0,0 +1,30 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#pragma once +#include "fuzzer/FuzzedDataProvider.h" +#include + +extern std::optional _fuzz_data_provider; diff --git a/test/fuzz/make.mk b/test/fuzz/make.mk new file mode 100644 index 0000000000..03254112fc --- /dev/null +++ b/test/fuzz/make.mk @@ -0,0 +1,104 @@ +# --------------------------------------- +# Common make definition for all examples +# --------------------------------------- + +# Build directory +BUILD := _build +PROJECT := $(notdir $(CURDIR)) + +# Handy check parameter function +check_defined = \ + $(strip $(foreach 1,$1, \ + $(call __check_defined,$1,$(strip $(value 2))))) +__check_defined = \ + $(if $(value $1),, \ + $(error Undefined make flag: $1$(if $2, ($2)))) + +#-------------- Fuzz harness compiler ------------ + +CC = clang +CXX = clang++ +GDB = gdb +OBJCOPY = objcopy +SIZE = size +MKDIR = mkdir + +ifeq ($(CMDEXE),1) + CP = copy + RM = del + PYTHON = python +else + SED = sed + CP = cp + RM = rm + PYTHON = python3 +endif + +#-------------- Source files and compiler flags -------------- + + +INC += $(TOP)/test + +# Compiler Flags +CFLAGS += \ + -ggdb \ + -fsanitize=fuzzer \ + -fsanitize=address \ + -fsanitize=undefined \ + -fdata-sections \ + -ffunction-sections \ + -fno-strict-aliasing \ + -Wall \ + -Wextra \ + -Werror \ + -Wfatal-errors \ + -Wdouble-promotion \ + -Wstrict-prototypes \ + -Wstrict-overflow \ + -Werror-implicit-function-declaration \ + -Wfloat-equal \ + -Wundef \ + -Wshadow \ + -Wwrite-strings \ + -Wsign-compare \ + -Wmissing-format-attribute \ + -Wunreachable-code \ + -Wcast-align \ + -Wcast-qual \ + -Wnull-dereference \ + -Wuninitialized \ + -Wunused \ + -Wredundant-decls \ + -O1 + +CFLAGS += \ + -Wno-error=unreachable-code \ + -DOPT_MCU_FUZZ=1 \ + -DCFG_TUSB_MCU=OPT_MCU_FUZZ + +CXXFLAGS += \ + -xc++ \ + -Wno-c++11-narrowing \ + -fno-implicit-templates + +# conversion is too strict for most mcu driver, may be disable sign/int/arith-conversion +# -Wconversion + +# Debugging/Optimization +ifeq ($(DEBUG), 1) + CFLAGS += -Og +else + CFLAGS += $(CFLAGS_OPTIMIZED) +endif + +# Log level is mapped to TUSB DEBUG option +ifneq ($(LOG),) + CMAKE_DEFSYM += -DLOG=$(LOG) + CFLAGS += -DCFG_TUSB_DEBUG=$(LOG) +endif + +# Logger: default is uart, can be set to rtt or swo +ifneq ($(LOGGER),) + CMAKE_DEFSYM += -DLOGGER=$(LOGGER) +endif + diff --git a/test/fuzz/msc_fuzz.cc b/test/fuzz/msc_fuzz.cc new file mode 100644 index 0000000000..e906ca971f --- /dev/null +++ b/test/fuzz/msc_fuzz.cc @@ -0,0 +1,162 @@ +#include "fuzz/fuzz_private.h" +#include "tusb.h" +#include +#include +#include + +#if CFG_TUD_MSC==1 + +// Whether host does safe eject. +// tud_msc_get_maxlun_cb returns a uint8_t so the max logical units that are +// allowed is 255, so we need to keep track of 255 fuzzed logical units. +static std::array::max()> ejected = {false}; + +extern "C" { +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, +// 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], + uint8_t product_id[16], uint8_t product_rev[4]) { + (void)lun; + assert(_fuzz_data_provider.has_value()); + + std::string vid = _fuzz_data_provider->ConsumeBytesAsString(8); + std::string pid = _fuzz_data_provider->ConsumeBytesAsString(16); + std::string rev = _fuzz_data_provider->ConsumeBytesAsString(4); + + memcpy(vendor_id, vid.c_str(), strlen(vid.c_str())); + memcpy(product_id, pid.c_str(), strlen(pid.c_str())); + memcpy(product_rev, rev.c_str(), strlen(rev.c_str())); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) { + // RAM disk is ready until ejected + if (ejected[lun]) { + // Additional Sense 3A-00 is NOT_FOUND + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + + return _fuzz_data_provider->ConsumeBool(); +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and +// SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size Application update +// block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, + uint16_t *block_size) { + (void)lun; + *block_count = _fuzz_data_provider->ConsumeIntegral(); + *block_size = _fuzz_data_provider->ConsumeIntegral(); +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, + bool load_eject) { + (void)power_condition; + assert(_fuzz_data_provider.has_value()); + + if (load_eject) { + if (start) { + // load disk storage + } else { + // unload disk storage + ejected[lun] = true; + } + } + + return _fuzz_data_provider->ConsumeBool(); +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, + void *buffer, uint32_t bufsize) { + assert(_fuzz_data_provider.has_value()); + (void)lun; + (void)lba; + (void)offset; + + std::vector consumed_buffer = _fuzz_data_provider->ConsumeBytes( + _fuzz_data_provider->ConsumeIntegralInRange(0, bufsize)); + memcpy(buffer, consumed_buffer.data(), consumed_buffer.size()); + + // Sometimes return an error code; + if (_fuzz_data_provider->ConsumeBool()) { + return _fuzz_data_provider->ConsumeIntegralInRange( + std::numeric_limits::min(), -1); + } + + return consumed_buffer.size(); +} + +bool tud_msc_is_writable_cb(uint8_t lun) { + assert(_fuzz_data_provider.has_value()); + (void)lun; + return _fuzz_data_provider->ConsumeBool(); +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, + uint8_t *buffer, uint32_t bufsize) { + // Ignore these as they are outputs and don't affect the return value. + (void)lun; + (void)lba; + (void)offset; + (void)buffer; + assert(_fuzz_data_provider.has_value()); + + // -ve error codes -> bufsize. + return _fuzz_data_provider->ConsumeIntegralInRange( + std::numeric_limits::min(), bufsize); +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, + uint16_t bufsize) { + (void)buffer; + (void)bufsize; + assert(_fuzz_data_provider.has_value()); + + switch (scsi_cmd[0]) { + case SCSI_CMD_TEST_UNIT_READY: + break; + case SCSI_CMD_INQUIRY: + break; + case SCSI_CMD_MODE_SELECT_6: + break; + case SCSI_CMD_MODE_SENSE_6: + break; + case SCSI_CMD_START_STOP_UNIT: + break; + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + break; + case SCSI_CMD_READ_CAPACITY_10: + break; + case SCSI_CMD_REQUEST_SENSE: + break; + case SCSI_CMD_READ_FORMAT_CAPACITY: + break; + case SCSI_CMD_READ_10: + break; + case SCSI_CMD_WRITE_10: + break; + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + return _fuzz_data_provider->ConsumeIntegralInRange( + std::numeric_limits::min(), -1); + } + + return 0; +} +} + +#endif \ No newline at end of file diff --git a/test/fuzz/net_fuzz.cc b/test/fuzz/net_fuzz.cc new file mode 100644 index 0000000000..8467d6d8ec --- /dev/null +++ b/test/fuzz/net_fuzz.cc @@ -0,0 +1,82 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb_config.h" +#if defined(CFG_TUD_ECM_RNDIS) || defined(CFG_TUD_NCM) + +#include "class/net/net_device.h" +#include "fuzz_private.h" +#include +#include +#include +#include "lwip/sys.h" + +extern "C" { +bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { + assert(_fuzz_data_provider.has_value()); + (void)src; + (void)size; + return _fuzz_data_provider->ConsumeBool(); +} + +// client must provide this: copy from network stack packet pointer to dst +uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { + (void)ref; + (void)arg; + + assert(_fuzz_data_provider.has_value()); + + uint16_t size = _fuzz_data_provider->ConsumeIntegral(); + std::vector temp = _fuzz_data_provider->ConsumeBytes(size); + memcpy(dst, temp.data(), temp.size()); + return size; +} + +/* lwip has provision for using a mutex, when applicable */ +sys_prot_t sys_arch_protect(void) { return 0; } +void sys_arch_unprotect(sys_prot_t pval) { (void)pval; } + +//------------- ECM/RNDIS -------------// + +// client must provide this: initialize any network state back to the beginning +void tud_network_init_cb(void) { + // NoOp. +} + +// client must provide this: 48-bit MAC address +// TODO removed later since it is not part of tinyusb stack +const uint8_t tud_network_mac_address[6] = {0}; + +//------------- NCM -------------// + +// callback to client providing optional indication of internal state of network +// driver +void tud_network_link_state_cb(bool state) { + (void)state; + // NoOp. +} +} + +#endif \ No newline at end of file diff --git a/test/fuzz/rules.mk b/test/fuzz/rules.mk new file mode 100644 index 0000000000..d9c2a1a777 --- /dev/null +++ b/test/fuzz/rules.mk @@ -0,0 +1,161 @@ +# --------------------------------------- +# Common make rules for all examples +# --------------------------------------- + +# Set all as default goal +.DEFAULT_GOAL := all + +# --------------------------------------- +# Compiler Flags +# --------------------------------------- + +LIBS_GCC ?= -lm + +# libc +LIBS += $(LIBS_GCC) + +ifneq ($(BOARD), spresense) +LIBS += -lc -Wl,-Bstatic -lc++ -Wl,-Bdynamic +endif + +# TinyUSB Stack source +SRC_C += \ + src/tusb.c \ + src/common/tusb_fifo.c \ + src/device/usbd.c \ + src/device/usbd_control.c \ + src/class/audio/audio_device.c \ + src/class/cdc/cdc_device.c \ + src/class/dfu/dfu_device.c \ + src/class/dfu/dfu_rt_device.c \ + src/class/hid/hid_device.c \ + src/class/midi/midi_device.c \ + src/class/msc/msc_device.c \ + src/class/net/ecm_rndis_device.c \ + src/class/net/ncm_device.c \ + src/class/usbtmc/usbtmc_device.c \ + src/class/video/video_device.c \ + src/class/vendor/vendor_device.c + + +# Fuzzers are c++ +SRC_CXX += \ + test/fuzz/dcd_fuzz.cc \ + test/fuzz/fuzz.cc \ + test/fuzz/msc_fuzz.cc \ + test/fuzz/net_fuzz.cc \ + test/fuzz/usbd_fuzz.cc + +# TinyUSB stack include +INC += $(TOP)/src + +CFLAGS += $(addprefix -I,$(INC)) +CXXFLAGS += -std=c++17 + +# LTO makes it difficult to analyze map file for optimizing size purpose +# We will run this option in ci +ifeq ($(NO_LTO),1) +CFLAGS := $(filter-out -flto,$(CFLAGS)) +endif + +ifneq ($(LD_FILE),) +LDFLAGS_LD_FILE ?= -Wl,-T,$(TOP)/$(LD_FILE) +endif + +LDFLAGS += $(CFLAGS) $(LDFLAGS_LD_FILE) -fuse-ld=lld -Wl,-Map=$@.map -Wl,--cref -Wl,-gc-sections +ifneq ($(SKIP_NANOLIB), 1) +endif + +ASFLAGS += $(CFLAGS) + +# Assembly files can be name with upper case .S, convert it to .s +SRC_S := $(SRC_S:.S=.s) + +# Due to GCC LTO bug https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966 +# assembly file should be placed first in linking order +# '_asm' suffix is added to object of assembly file +OBJ += $(addprefix $(BUILD)/obj/, $(SRC_S:.s=_asm.o)) +OBJ += $(addprefix $(BUILD)/obj/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/obj/, $(SRC_CXX:.cc=_cxx.o)) + +# Verbose mode +ifeq ("$(V)","1") +$(info CFLAGS $(CFLAGS) ) $(info ) +$(info LDFLAGS $(LDFLAGS)) $(info ) +$(info ASFLAGS $(ASFLAGS)) $(info ) +endif + +# --------------------------------------- +# Rules +# --------------------------------------- + +all: $(BUILD)/$(PROJECT) + +OBJ_DIRS = $(sort $(dir $(OBJ))) +$(OBJ): | $(OBJ_DIRS) +$(OBJ_DIRS): +ifeq ($(CMDEXE),1) + @$(MKDIR) $(subst /,\,$@) +else + @$(MKDIR) -p $@ +endif + +$(BUILD)/$(PROJECT): $(OBJ) + @echo LINK $@ + @ $(CXX) -o $@ $(LIB_FUZZING_ENGINE) $^ $(LIBS) $(LDFLAGS) + +# We set vpath to point to the top of the tree so that the source files +# can be located. By following this scheme, it allows a single build rule +# to be used to compile all .c files. +vpath %.c . $(TOP) +$(BUILD)/obj/%.o: %.c + @echo CC $(notdir $@) + @$(CC) $(CFLAGS) -c -MD -o $@ $< + +# All cpp srcs +vpath %.cc . $(TOP) +$(BUILD)/obj/%_cxx.o: %.cc + @echo CXX $(notdir $@) + @$(CXX) $(CFLAGS) $(CXXFLAGS) -c -MD -o $@ $< + +# ASM sources lower case .s +vpath %.s . $(TOP) +$(BUILD)/obj/%_asm.o: %.s + @echo AS $(notdir $@) + @$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $< + +# ASM sources upper case .S +vpath %.S . $(TOP) +$(BUILD)/obj/%_asm.o: %.S + @echo AS $(notdir $@) + @$(CC) -x assembler-with-cpp $(ASFLAGS) -c -o $@ $< + +.PHONY: clean +clean: +ifeq ($(CMDEXE),1) + rd /S /Q $(subst /,\,$(BUILD)) +else + $(RM) -rf $(BUILD) +endif +# ---------------- GNU Make End ----------------------- + +# get depenecies +.PHONY: get-deps +get-deps: + ifdef DEPS_SUBMODULES + git -C $(TOP) submodule update --init $(DEPS_SUBMODULES) + endif + +size: $(BUILD)/$(PROJECT) + -@echo '' + @$(SIZE) $< + -@echo '' + +# linkermap must be install previously at https://github.com/hathach/linkermap +linkermap: $(BUILD)/$(PROJECT) + @linkermap -v $<.map + +# Print out the value of a make variable. +# https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile +print-%: + @echo $* = $($*) \ No newline at end of file diff --git a/test/fuzz/usbd_fuzz.cc b/test/fuzz/usbd_fuzz.cc new file mode 100644 index 0000000000..45c5fd7eaf --- /dev/null +++ b/test/fuzz/usbd_fuzz.cc @@ -0,0 +1,73 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Nathaniel Brough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "fuzz/fuzz_private.h" +#include "tusb.h" +// #include "usb_descriptors.h" + +#ifndef CFG_FUZZ_MAX_STRING_LEN +#define CFG_FUZZ_MAX_STRING_LEN 1000 +#endif + +extern "C" { + +/* TODO: Implement a fuzzed version of this. +uint8_t const *tud_descriptor_bos_cb(void) { } +*/ + +/* TODO: Implement a fuzzed version of this. +uint8_t const *tud_descriptor_device_qualifier_cb(void) {} +*/ + +/* TODO: Implement a fuzzed version of this. +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {} +*/ + +void tud_mount_cb(void) { + // NOOP +} + +void tud_umount_cb(void) { + // NOOP +} + +void tud_suspend_cb(bool remote_wakeup_en) { + (void)remote_wakeup_en; + // NOOP +} + +void tud_resume_cb(void) { + // NOOP +} + +/* TODO: Implement a fuzzed version of this. +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, + tusb_control_request_t const *request) {} +*/ + +/* TODO: Implement a fuzzed version of this. +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {} +*/ +}