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:
parent
bb950b2d63
commit
65d657685e
|
@ -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}")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue