Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 45 additions & 6 deletions src/platform/posix/fuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,72 @@
const uint8_t *posix_fuzz_buf;
size_t posix_fuzz_sz;

/*
* Implemented in sof/src/platform/posix/ipc.c. These small hooks let
* us reset and observe the staging state owned by the IPC layer so
* testcases are properly isolated from each other.
*/
extern void posix_fuzz_case_begin(void);
extern void posix_fuzz_case_abort(void);
extern bool posix_fuzz_case_pending(void);

/* Number of simulator quanta the budget is split into for the
* drain-or-abort loop. More quanta = earlier exit on quick testcases.
*/
#define POSIX_FUZZ_DRAIN_QUANTA 8

/**
* Entry point for fuzzing. Works by placing the data
* into two known symbols, triggering an app-visible interrupt, and
* then letting the simulator run for a fixed amount of time (intended to be
* "long enough" to handle the event and reach a quiescent state
* again)
* then letting the simulator run for up to a fixed amount of time
* split into small quanta, exiting as soon as the OS has drained the
* staged fuzz input. If the budget is exhausted before drain we drop
* pending state to keep testcases isolated.
*/
NATIVE_SIMULATOR_IF
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz)
{
static bool runner_initialized;
uint64_t total_us;
uint64_t quantum_us;
int i;

if (!runner_initialized) {
nsi_init(0, NULL);
runner_initialized = true;
}

total_us = k_ticks_to_us_ceil64(CONFIG_ZEPHYR_POSIX_FUZZ_TICKS);
quantum_us = (total_us + POSIX_FUZZ_DRAIN_QUANTA - 1) / POSIX_FUZZ_DRAIN_QUANTA;
if (quantum_us == 0)
quantum_us = 1;

/* Fresh testcase: drop any leftovers from a previous case before
* staging the new buffer, so state from the prior input cannot
* influence this one.
*/
posix_fuzz_case_begin();

/* Provide the fuzz data to the embedded OS as an interrupt, with
* "DMA-like" data placed into posix_fuzz_buf/sz.
*/
posix_fuzz_buf = data;
posix_fuzz_sz = sz;
hw_irq_ctrl_set_irq(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ);

/* Give the OS time to process whatever happened in that
* interrupt and reach an idle state.
/* Bounded drain loop: run simulator in small quanta and exit
* as soon as both the raw input buffer and the staged IPC
* queue are empty.
*/
for (i = 0; i < POSIX_FUZZ_DRAIN_QUANTA; i++) {
nsi_exec_for(quantum_us);
if (!posix_fuzz_case_pending())
return 0;
}

/* Budget exhausted without full drain: hard reset so the next
* testcase starts clean.
*/
nsi_exec_for(k_ticks_to_us_ceil64(CONFIG_ZEPHYR_POSIX_FUZZ_TICKS));
posix_fuzz_case_abort();
return 0;
}
24 changes: 24 additions & 0 deletions src/platform/posix/ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <sof/ipc/schedule.h>
#include <sof/schedule/edf_schedule.h>
#include <sof/audio/component_ext.h>
#include <stdbool.h>
#include <stddef.h>

// 6c8f0d53-ff77-4ca1-b825-c0c4e1b0d322
SOF_DEFINE_REG_UUID(ipc_task_posix);
Expand All @@ -33,6 +35,28 @@ extern size_t posix_fuzz_sz;
static uint8_t fuzz_in[65536];
static size_t fuzz_in_sz;

/*
* Testcase-isolation helpers used by the libFuzzer entry point in
* fuzz.c. They keep ownership of the cross-call state in one module
* so a new testcase never observes leftovers from a previous one that
* failed to drain inside the simulator tick budget.
*/
void posix_fuzz_case_begin(void)
{
fuzz_in_sz = 0;
}

bool posix_fuzz_case_pending(void)
{
return posix_fuzz_sz != 0 || fuzz_in_sz != 0;
}

void posix_fuzz_case_abort(void)
{
posix_fuzz_sz = 0;
fuzz_in_sz = 0;
}
Comment on lines +38 to +58

// The protocol here is super simple: the first byte is a message size
// in units of 16 bits (the buffer maximum defaults to 384 bytes, and
// I didn't want to waste space early in the buffer lest I confuse the
Expand Down
Loading