From 033ccdc5348ee278916e1d6748146f354a146e1b Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Wed, 3 Jul 2024 11:56:25 -0700 Subject: [PATCH] platform/posix: Port fuzzer to upstream "native_sim" board The older native_posix board is being deprecated, use native_sim, which is the future-proof API. In theory this should be as simple as just swapping the board name at the west level, but there are a few changes: The C API is broadly identical between the two, modulo some prefix renaming. Unfortunately linkage is more of a hassle, as the fuzzing framework inverts the sense of "entry point" and causes some trouble with the way native_sim does its two-stage link. We have to add some hackery: 1. Make sure the fuzz entry point doesn't get dropped during the initial zephyr.elf link, as it calls OS/sim layer and not the reverse. 2. Force it to be a global symbol in the final stage, so it can be seen by the code in libfuzzer that needs to call it (normally all Zephyr-side symbols are forced to be library-private to prevent collisions with the global Linux/glibc namespace environment) Signed-off-by: Andy Ross --- scripts/fuzz.sh | 4 ++-- src/platform/posix/fuzz.c | 10 +++++----- src/platform/posix/posix.c | 13 +++++++++++++ zephyr/CMakeLists.txt | 8 ++++++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/scripts/fuzz.sh b/scripts/fuzz.sh index 07f9f1b59685..27b671e53a01 100755 --- a/scripts/fuzz.sh +++ b/scripts/fuzz.sh @@ -34,7 +34,7 @@ with the -b option. Simple wrapper around a libfuzzer test run, as much for documentation as direct use. The idea here is really simple: build -for the Zephyr "native_posix" board (which is a just a x86 +for the Zephyr "native_sim" board (which is a just a x86 executable for the build host, not an emulated device) and run the resulting zephyr.exe file. This specifies a "fuzz_corpus" directory to save the seeds that produce useful coverage output for use in @@ -124,7 +124,7 @@ main() (set -x # When passing conflicting -DVAR='VAL UE1' -DVAR='VAL UE2' to CMake, # the last 'VAL UE2' wins. Previous ones are silently ignored. - west build -d build-fuzz -b native_posix "$SOF_TOP"/app/ -- \ + west build -d build-fuzz -b native_sim "$SOF_TOP"/app/ -- \ "${fuzz_configs[@]}" "$@" ) diff --git a/src/platform/posix/fuzz.c b/src/platform/posix/fuzz.c index c2b02669b0b5..7b5e6b33da13 100644 --- a/src/platform/posix/fuzz.c +++ b/src/platform/posix/fuzz.c @@ -9,9 +9,9 @@ #include #include -/* Zephyr arch APIs, not in a header (native_sim has them though) */ -void posix_init(int argc, char *argv[]); -void posix_exec_for(uint64_t us); +/* Zephyr arch APIs, not in a header */ +void nsi_init(int argc, char *argv[]); +void nsi_exec_for(uint64_t us); const uint8_t *posix_fuzz_buf; size_t posix_fuzz_sz; @@ -28,7 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz) static bool runner_initialized; if (!runner_initialized) { - posix_init(0, NULL); + nsi_init(0, NULL); runner_initialized = true; } @@ -42,6 +42,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz) /* 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_ZEPHYR_POSIX_FUZZ_TICKS)); + nsi_exec_for(k_ticks_to_us_ceil64(CONFIG_ZEPHYR_POSIX_FUZZ_TICKS)); return 0; } diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index f8024cb0dcca..4bc9976e7268 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -33,6 +33,17 @@ __asm__(".globl _trace_ctx_start\n" ".globl _trace_ctx_end\n" "_trace_ctx_end:\n"); +/* Sort of a kluge: this is an app-provided symbol, Zephyr doesn't + * know about it. But it's the ENTRY POINT of the binary, so SOF + * doesn't ever reference or call it either. It therefore gets + * dropped by the initial link of zephyr.elf, so isn't present anymore + * in the final link vs. the native_sim runner to link vs. the + * framework code in libfuzzer. Stash its address meaninglessly just + * to be sure the symbol gets referenced. + */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz); +void *native_sim_ptr_save; + struct ipc_data_host_buffer *ipc_platform_get_host_buffer(struct ipc *ipc) { return NULL; @@ -81,6 +92,8 @@ int platform_init(struct sof *sof) posix_dma_init(sof); ipc_init(sof); + native_sim_ptr_save = LLVMFuzzerTestOneInput; + return 0; } diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index e691dff9d880..25fc607f3321 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -401,6 +401,14 @@ zephyr_library_sources_ifdef(CONFIG_ZEPHYR_POSIX ${SOF_PLATFORM_PATH}/posix/fuzz.c ) +# When building the fuzzer, the LLVMFuzzerTestOneInput() entry point +# (which is defined here in the app) must be exposed to the secondary +# native_sim "runner" linkage step, which normally hides all +# Zephyr-side symbols to protect against collisions. Somewhat opaque +# cmake interface to this, alas. +set_property(TARGET native_simulator APPEND PROPERTY + LOCALIZE_EXTRA_OPTIONS "--globalize-symbol=LLVMFuzzerTestOneInput") + zephyr_library_sources_ifdef(CONFIG_LIBRARY ${SOF_PLATFORM_PATH}/library/platform.c ${SOF_PLATFORM_PATH}/library/lib/dai.c