arch/posix: Add libfuzzer support

Add support for LLVM's libfuzzer utility.  This works by building an
executable with a "LLVMFuzzerTestOneInput()" entry point (which is
external to Zephyr, running in the host process environment!), which
it drives out of its own main() routine.  The toolchain API is exposed
as just another sanitizer variant, which is clean.

Signed-off-by: Andy Ross <andyross@google.com>
This commit is contained in:
Andy Ross 2022-08-23 12:24:14 -07:00 committed by Carles Cufí
parent bb950b2d63
commit 65d657685e
4 changed files with 83 additions and 2 deletions

View File

@ -65,6 +65,10 @@ if(CONFIG_ASAN_RECOVER)
zephyr_compile_options(-fsanitize-recover=all)
endif()
if(CONFIG_ARCH_POSIX_LIBFUZZER)
list(APPEND LLVM_SANITIZERS "fuzzer")
endif()
list(JOIN LLVM_SANITIZERS "," LLVM_SANITIZERS_ARG)
if(NOT ${LLVM_SANITIZERS_ARG} STREQUAL "")
set(LLVM_SANITIZERS_ARG "-fsanitize=${LLVM_SANITIZERS_ARG}")

View File

@ -20,4 +20,35 @@ config ARCH_POSIX_RECOMMENDED_STACK_SIZE
thread stack, the real stack is the native underlying pthread stack.
Therefore the allocated stack can be limited to this size)
config ARCH_POSIX_LIBFUZZER
bool "Build fuzz test target"
help
Build the posix app as a LLVM libfuzzer target. Requires
support from the toolchain (currently only clang works, and
only on native_posix_64), and should normally be used in
concert with some of CONFIG_ASAN/UBSAN/MSAN for validation.
The application needs to implement the
LLVMFuzzerTestOneInput() entry point, which runs in the host
environment "outside" the OS. See Zephyr documentation and
sample and https://llvm.org/docs/LibFuzzer.html for more
information.
config ARCH_POSIX_FUZZ_IRQ
int "OS interrupt via which to deliver fuzz cases"
default 3
help
When using libfuzzer, new fuzz cases are delivered to Zephyr
via interrupts. The IRQ should be otherwise unused, but can
be any value desired by the app.
config ARCH_POSIX_FUZZ_TICKS
int "Ticks to allow for fuzz case processing"
default 2
help
Fuzz interrupts are delivered, from the perspective of the
OS, at a steady cadence in simulated time. In general most
apps won't require much time to reach an idle state
following a unit-test style case, so the default is short to
prevent interaction with regular timer workloads.
endmenu

View File

@ -11,6 +11,7 @@ comment "Native POSIX options"
config NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME
bool "Slow down execution to real time"
default n if ARCH_POSIX_LIBFUZZER
default y if BT_USERCHAN || !TEST
help
When selected the execution of the process will be slowed down to real time.

View File

@ -30,7 +30,9 @@
#include "hw_models_top.h"
#include <stdlib.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/time_units.h>
#include "cmdline.h"
#include "irq_ctrl.h"
void posix_exit(int exit_code)
{
@ -85,9 +87,15 @@ void posix_exec_for(uint64_t us)
} while (hwm_get_time() < (start + us));
}
#ifndef CONFIG_ARCH_POSIX_LIBFUZZER
/**
* This is the actual main for the Linux process,
* the Zephyr application main is renamed something else thru a define.
* This is the actual host process main routine. The Zephyr
* application's main() is renamed via preprocessor trickery to avoid
* collisions.
*
* Not used when building fuzz cases, as libfuzzer has its own main()
* and calls the "OS" through a per-case fuzz test entry point.
*/
int main(int argc, char *argv[])
{
@ -99,3 +107,40 @@ int main(int argc, char *argv[])
/* This line should be unreachable */
return 1; /* LCOV_EXCL_LINE */
}
#else /* CONFIG_ARCH_POSIX_LIBFUZZER */
/**
* Entry point for fuzzing (when enabled). Works by placing the data
* into two known symbols, triggering an app-visible interrupt, and
* then letting the OS run for a fixed amount of time (intended to be
* "long enough" to handle the event and reach a quiescent state
* again)
*/
uint8_t *posix_fuzz_buf, posix_fuzz_sz;
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz)
{
static bool posix_initialized;
if (!posix_initialized) {
posix_init(0, NULL);
posix_initialized = true;
}
/* Provide the fuzz data to Zephyr as an interrupt, with
* "DMA-like" data placed into posix_fuzz_buf/sz
*/
posix_fuzz_buf = (void *)data;
posix_fuzz_sz = sz;
hw_irq_ctrl_set_irq(CONFIG_ARCH_POSIX_FUZZ_IRQ);
/* Give the OS time to process whatever happened in that
* interrupt and reach an idle state.
*/
posix_exec_for(k_ticks_to_us_ceil64(CONFIG_ARCH_POSIX_FUZZ_TICKS));
return 0;
}
#endif