From c27bb54330503fbacbf09e4e918ec1e7ab20e352 Mon Sep 17 00:00:00 2001 From: Christopher Krah Date: Mon, 20 May 2024 16:08:30 +0400 Subject: [PATCH] feat: add initial fuzzing harness --- fuzz/CMakeLists.txt | 70 ++++++++++++++++++ fuzz/Makefile | 18 +++++ fuzz/harness.cc | 168 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 fuzz/CMakeLists.txt create mode 100644 fuzz/Makefile create mode 100644 fuzz/harness.cc diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt new file mode 100644 index 00000000..44a5c0f8 --- /dev/null +++ b/fuzz/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.9.5) +project(rplidar_fuzz) + +if (NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif () + +message(STATUS "CXX Compiler: ${CMAKE_CXX_COMPILER}") +message(STATUS "CXX Flags before: ${CMAKE_CXX_FLAGS}") + +if (CMAKE_CXX_COMPILER) + # Check if the CXX compiler is afl-clang or afl-clang++ + if (CMAKE_CXX_COMPILER MATCHES "afl-clang-fast") + message(STATUS "Using AFL Clang compiler") + # Set flags for AFL + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer,address,undefined -ggdb -O2 -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address,undefined -ggdb -O2") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address,undefined -ggdb -O2") + else() + message(STATUS "Using regular Clang/GCC compiler") + # Enable fuzzer and address sanitizers for other compilers + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer,address,undefined -ggdb -O2") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=fuzzer,address,undefined -ggdb -O2") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=fuzzer,address,undefined -ggdb -O2") + endif() + + message(STATUS "CXX Flags after: ${CMAKE_CXX_FLAGS}") + + # Include directories for the SDK library + include_directories( + ../sdk/include + ../sdk/src + ../sdk/src/arch/linux + ../sdk/src/hal) + + # Create instrumented SDK library + add_library(rplidar_sdk_instr SHARED + ../sdk/src/rplidar_driver.cpp + ../sdk/src/arch/linux/net_serial.cpp + ../sdk/src/arch/linux/net_socket.cpp + ../sdk/src/arch/linux/timer.cpp + ../sdk/src/hal/thread.cpp) + + target_include_directories(rplidar_sdk_instr + PUBLIC + ../sdk/include + ../sdk/src + PRIVATE + ../sdk/src/arch/linux + ../sdk/src/hal) + + # Add the fuzz test executable + add_executable(harness harness.cc) + + # Link against the instrumented rplidar_sdk library + target_link_libraries(harness rplidar_sdk_instr) + + # Set the RPATH to find the SDK library + set_target_properties(harness PROPERTIES INSTALL_RPATH "\$ORIGIN") + + # Set the output directory for the instrumented SDK library + set_target_properties(rplidar_sdk_instr PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + + # Set the output directory for the fuzz test executable + set_target_properties(harness PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") +else() + message(FATAL_ERROR "CMAKE_CXX is not set. Please specify the CXX compiler.") +endif () diff --git a/fuzz/Makefile b/fuzz/Makefile new file mode 100644 index 00000000..4159a66a --- /dev/null +++ b/fuzz/Makefile @@ -0,0 +1,18 @@ +BUILD_DIR := build + +.PHONY: all clean fuzz afl + +all: fuzz + +clean: + rm -rf $(BUILD_DIR) + +fuzz: clean + mkdir -p $(BUILD_DIR) + cd $(BUILD_DIR) && CC=clang CXX=clang++ cmake .. && make + mv $(BUILD_DIR)/harness harness + +afl: clean + mkdir -p $(BUILD_DIR) + cd $(BUILD_DIR) && CC=afl-clang-fast CXX=afl-clang-fast++ cmake .. && make + mv $(BUILD_DIR)/harness harness_afl diff --git a/fuzz/harness.cc b/fuzz/harness.cc new file mode 100644 index 00000000..ac3dceb3 --- /dev/null +++ b/fuzz/harness.cc @@ -0,0 +1,168 @@ +#include "sdkcommon.h" + +#include "hal/abs_rxtx.h" +#include "hal/event.h" +#include "hal/locker.h" +#include "hal/socket.h" +#include "hal/thread.h" +#include "hal/types.h" +#include "rplidar_cmd.h" +#include "rplidar_driver.h" +#include "sdkcommon.h" + +// Is needed for `std::unique_ptr` +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_ALLOC 1024 * 1000 + +// extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { return 0; } + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + + // Create an instance of the RPlidarDriver + auto driverType = fdp.ConsumeBool() + ? rp::standalone::rplidar::DRIVER_TYPE_TCP + : rp::standalone::rplidar::DRIVER_TYPE_SERIALPORT; + auto drv = rp::standalone::rplidar::RPlidarDriver::CreateDriver(driverType); + + if (!drv) { + return 0; + } + + // Connect to the RPLIDAR device + if (fdp.remaining_bytes() >= 2) { + if (driverType == rp::standalone::rplidar::DRIVER_TYPE_SERIALPORT) { + std::string portPath = fdp.ConsumeRandomLengthString(256); + uint32_t baudrate = fdp.ConsumeIntegral(); + drv->connect(portPath.c_str(), baudrate); + } else { + std::string ip = fdp.ConsumeRandomLengthString(15); + uint32_t port = fdp.ConsumeIntegral(); + + // Check if _binded_socket is null before calling connect + // if (static_cast(drv) + // ->_channel._binded_socket == nullptr) { + // return -1; + //} + + drv->connect(ip.c_str(), port); + } + } + // Check if the device is connected + drv->isConnected(); + + // Reset the device + if (fdp.ConsumeBool()) { + drv->reset(); + } + + // Get all supported scan modes + if (fdp.ConsumeBool()) { + std::vector outModes; + drv->getAllSupportedScanModes(outModes); + } + + // Get typical scan mode + if (fdp.ConsumeBool()) { + uint16_t outMode; + drv->getTypicalScanMode(outMode); + } + + // Start scan + if (fdp.ConsumeBool()) { + bool force = fdp.ConsumeBool(); + bool useTypicalScan = fdp.ConsumeBool(); + rp::standalone::rplidar::RplidarScanMode outUsedScanMode; + drv->startScan(force, useTypicalScan, 0, &outUsedScanMode); + } + + // Start scan express + if (fdp.ConsumeBool()) { + bool force = fdp.ConsumeBool(); + uint16_t scanMode = fdp.ConsumeIntegral(); + rp::standalone::rplidar::RplidarScanMode outUsedScanMode; + drv->startScanExpress(force, scanMode, 0, &outUsedScanMode); + } + + // Get health status + if (fdp.ConsumeBool()) { + rplidar_response_device_health_t health; + drv->getHealth(health); + } + + // Get device info + if (fdp.ConsumeBool()) { + rplidar_response_device_info_t info; + drv->getDeviceInfo(info); + } + + // Set motor PWM + if (fdp.ConsumeBool()) { + uint16_t pwm = fdp.ConsumeIntegral(); + drv->setMotorPWM(pwm); + } + + // Start motor + if (fdp.ConsumeBool()) { + drv->startMotor(); + } + + // Stop motor + if (fdp.ConsumeBool()) { + drv->stopMotor(); + } + + // Check motor control support + if (fdp.ConsumeBool()) { + bool support; + drv->checkMotorCtrlSupport(support); + } + + // Get scan data + if (fdp.ConsumeBool()) { + // FIXME: Gotta wait for https://github.com/google/sanitizers/issues/1720 + // size_t count = fdp.ConsumeIntegral(); + size_t count = fdp.ConsumeIntegralInRange(0, MAX_ALLOC); + + auto nodebuffer = std::unique_ptr( + new rplidar_response_measurement_node_hq_t[count]); + auto timeout = static_cast(fdp.ConsumeIntegral()); + drv->grabScanDataHq(nodebuffer.get(), count, timeout); + } + + // Ascend scan data + if (fdp.ConsumeBool()) { + // FIXME: same as above + // size_t count = fdp.ConsumeIntegral(); + size_t count = fdp.ConsumeIntegralInRange(0, MAX_ALLOC); + auto nodebuffer = std::unique_ptr( + new rplidar_response_measurement_node_hq_t[count]); + drv->ascendScanData(nodebuffer.get(), count); + } + + // Get scan data with interval + if (fdp.ConsumeBool()) { + // FIXME: same as above + // size_t count = fdp.ConsumeIntegral(); + size_t count = fdp.ConsumeIntegralInRange(0, MAX_ALLOC); + auto nodebuffer = std::unique_ptr( + new rplidar_response_measurement_node_hq_t[count]); + drv->getScanDataWithIntervalHq(nodebuffer.get(), count); + } + // Dispose of the driver + rp::standalone::rplidar::RPlidarDriver::DisposeDriver(drv); + + return 0; +}