Skip to content

Commit

Permalink
platform/posix: Port fuzzer to upstream "native_sim" board
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
andyross committed Jul 3, 2024
1 parent 2aaee2e commit 1b3e72d
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 7 deletions.
4 changes: 2 additions & 2 deletions scripts/fuzz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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[@]}" "$@"
)

Expand Down
10 changes: 5 additions & 5 deletions src/platform/posix/fuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
#include <irq_ctrl.h>
#include <zephyr/sys/time_units.h>

/* 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;
Expand All @@ -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;
}

Expand All @@ -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;
}
13 changes: 13 additions & 0 deletions src/platform/posix/posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -81,6 +92,8 @@ int platform_init(struct sof *sof)
posix_dma_init(sof);
ipc_init(sof);

native_sim_ptr_save = LLVMFuzzerTestOneInput;

return 0;
}

Expand Down
8 changes: 8 additions & 0 deletions zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 1b3e72d

Please sign in to comment.