Skip to content

Commit bfa6456

Browse files
committed
platform: posix: drain-or-abort loop for fuzz testcase isolation
The libFuzzer harness used to stage the testcase bytes, raise the fuzz IRQ, and then unconditionally run the native_sim scheduler for CONFIG_ZEPHYR_POSIX_FUZZ_TICKS ticks before returning. That has two problems for reproducibility: * If the OS finishes draining the IPC much faster than the tick budget (the common case), we still burn the full budget, which slows exec/s without buying any coverage. * If the OS does NOT finish within the budget (deep handlers, long pipeline walks, large payloads), the staged input buffer plus the per-call fuzz_in[] queue carry over into the next testcase. That leaks state across cases and is the root cause of crashes that disappear when replayed individually. Split the budget into POSIX_FUZZ_DRAIN_QUANTA (=8) quanta and after each one ask the IPC layer whether anything is still pending; return as soon as the queue is empty, otherwise run the abort hook to wipe both the raw fuzz buffer and the staged IPC payload before the next call. Together with the hooks added in the previous commit this guarantees that LLVMFuzzerTestOneInput observes a clean staging state on entry regardless of what the previous case did. No protocol or coverage change is intended; the goal is reproducible crashes and slightly higher throughput on short inputs. Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
1 parent 608fcda commit bfa6456

1 file changed

Lines changed: 37 additions & 6 deletions

File tree

src/platform/posix/fuzz.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,68 @@
1010
#include <zephyr/sys/time_units.h>
1111
#include <nsi_cpu_if.h>
1212
#include <nsi_main_semipublic.h>
13+
#include <platform/fuzz.h>
1314

1415
const uint8_t *posix_fuzz_buf;
1516
size_t posix_fuzz_sz;
1617

18+
/* Number of simulator quanta the budget is split into for the
19+
* drain-or-abort loop. More quanta = earlier exit on quick testcases.
20+
*/
21+
#define POSIX_FUZZ_DRAIN_QUANTA 8
22+
1723
/**
1824
* Entry point for fuzzing. Works by placing the data
1925
* into two known symbols, triggering an app-visible interrupt, and
20-
* then letting the simulator run for a fixed amount of time (intended to be
21-
* "long enough" to handle the event and reach a quiescent state
22-
* again)
26+
* then letting the simulator run for up to a fixed amount of time
27+
* split into small quanta, exiting as soon as the OS has drained the
28+
* staged fuzz input. If the budget is exhausted before drain we drop
29+
* pending state to keep testcases isolated.
2330
*/
2431
NATIVE_SIMULATOR_IF
2532
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz)
2633
{
2734
static bool runner_initialized;
35+
uint64_t total_us;
36+
uint64_t quantum_us;
37+
int i;
2838

2939
if (!runner_initialized) {
3040
nsi_init(0, NULL);
3141
runner_initialized = true;
3242
}
3343

44+
total_us = k_ticks_to_us_ceil64(CONFIG_ZEPHYR_POSIX_FUZZ_TICKS);
45+
quantum_us = (total_us + POSIX_FUZZ_DRAIN_QUANTA - 1) / POSIX_FUZZ_DRAIN_QUANTA;
46+
if (quantum_us == 0)
47+
quantum_us = 1;
48+
49+
/* Fresh testcase: drop any leftovers from a previous case before
50+
* staging the new buffer, so state from the prior input cannot
51+
* influence this one.
52+
*/
53+
posix_fuzz_case_begin();
54+
3455
/* Provide the fuzz data to the embedded OS as an interrupt, with
3556
* "DMA-like" data placed into posix_fuzz_buf/sz.
3657
*/
3758
posix_fuzz_buf = data;
3859
posix_fuzz_sz = sz;
3960
hw_irq_ctrl_set_irq(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ);
4061

41-
/* Give the OS time to process whatever happened in that
42-
* interrupt and reach an idle state.
62+
/* Bounded drain loop: run simulator in small quanta and exit
63+
* as soon as both the raw input buffer and the staged IPC
64+
* queue are empty.
65+
*/
66+
for (i = 0; i < POSIX_FUZZ_DRAIN_QUANTA; i++) {
67+
nsi_exec_for(quantum_us);
68+
if (!posix_fuzz_case_pending())
69+
return 0;
70+
}
71+
72+
/* Budget exhausted without full drain: hard reset so the next
73+
* testcase starts clean.
4374
*/
44-
nsi_exec_for(k_ticks_to_us_ceil64(CONFIG_ZEPHYR_POSIX_FUZZ_TICKS));
75+
posix_fuzz_case_abort();
4576
return 0;
4677
}

0 commit comments

Comments
 (0)