Skip to content

Commit ffda587

Browse files
tmlemankv2019i
authored andcommitted
platform: posix: run fuzz teardown in thread context
The IPC fuzzer aborts partway through a run with: programming error: nsif_cpu0_irq_raised_from_sw called from a HW model thread The between-testcase topology teardown (posix_ipc_teardown(), added in commit d53a762 "platform: posix: tear down IPC topology between fuzz testcases") was invoked from posix_fuzz_case_begin(), which executes on the libFuzzer driver thread. On native_sim that thread is a "HW model" context: the simulated CPU is halted (posix_is_cpu_running() == false) whenever execution is outside nsi_exec_for(). The teardown frees pipelines and cancels scheduler tasks, taking Zephyr spinlocks (and the LL scheduler's k_mutex). Releasing a spinlock reaches arch_irq_unlock() -> hw_irq_ctrl_change_lock(); if a HW interrupt is pending there it is vectored synchronously via nsif_cpu0_irq_raised_from_sw(), which aborts because the CPU is not running. gdb confirms the pending interrupt is the system tick (irq_status == 0x1, IRQ 0 = TIMER_TICK_IRQ), delivered from pipeline_posn_unlock() -> k_spin_unlock() inside pipeline_free(). Running the teardown on the driver thread was always unsafe, but only became reproducible after a Zephyr update that converted the native_sim system timer from a periodic tick to a one-shot, fully tickless model (drivers/timer/native_sim_timer.c "use the generic timer core", plus the native_simulator hwtimer_set_tick_one_shot() addition). hwtimer_enable() previously armed a periodic tick on a fixed grid and no tick happened to be pending at the between-testcase boundary; the one-shot core now arms the tick at the exact next timeout deadline, which lands at/after an nsi_exec_for() quantum boundary. nsi_exec_for() stops on its time budget, so it returns after the tick fires but before the CPU services it, leaving TIMER_TICK_IRQ pending exactly when the teardown runs. Fix this by running the teardown where SOF frees pipelines during normal operation: the EDF workqueue thread. posix_fuzz_case_begin() now only sets a flag; ipc_platform_do_cmd() consumes it and runs posix_ipc_teardown() before this testcase's first command. There the CPU is running (a pending tick is delivered legitimately) and blocking primitives such as k_mutex are valid. Clearing the pending interrupt on the driver thread was rejected as an alternative: dropping the tick leaves the one-shot timer with no armed deadline (next_timer_time == NSI_NEVER), so the simulator exits via nsi_exit() and libFuzzer reports "fuzz target exited". Validated with the IPC4 seed corpus (342142 runs) and the IPC3 seed corpus (111135 runs); both complete cleanly with no crash artifacts. Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
1 parent 43e40ff commit ffda587

1 file changed

Lines changed: 25 additions & 1 deletion

File tree

src/platform/posix/ipc.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ extern size_t posix_fuzz_sz;
3939
static uint8_t fuzz_in[65536];
4040
static size_t fuzz_in_sz;
4141

42+
/* Set on the driver thread by posix_fuzz_case_begin(), consumed on the EDF
43+
* workqueue thread by ipc_platform_do_cmd(). See posix_fuzz_case_begin().
44+
*/
45+
static bool posix_fuzz_teardown_pending;
46+
4247
/*
4348
* posix_ipc_teardown - drop all IPC-tracked objects left over from the
4449
* previous fuzz testcase so the next one starts from a clean topology.
@@ -194,7 +199,15 @@ static void posix_ipc_teardown(void)
194199
*/
195200
void posix_fuzz_case_begin(void)
196201
{
197-
posix_ipc_teardown();
202+
/*
203+
* Defer the teardown to the EDF workqueue thread (ipc_platform_do_cmd()).
204+
* This runs on the libFuzzer driver thread, where native_sim treats the
205+
* CPU as halted; posix_ipc_teardown() releases spinlocks/mutexes, and
206+
* releasing a spinlock while the system-tick IRQ is pending makes
207+
* native_sim deliver it synchronously and abort ("called from a HW model
208+
* thread").
209+
*/
210+
posix_fuzz_teardown_pending = true;
198211
fuzz_in_sz = 0;
199212
}
200213

@@ -362,6 +375,17 @@ enum task_state ipc_platform_do_cmd(struct ipc *ipc)
362375
{
363376
struct ipc_cmd_hdr *hdr;
364377

378+
#ifdef CONFIG_ARCH_POSIX_LIBFUZZER
379+
/*
380+
* Reclaim the previous testcase's topology in thread context, before
381+
* this testcase's first command. See posix_fuzz_case_begin().
382+
*/
383+
if (posix_fuzz_teardown_pending) {
384+
posix_fuzz_teardown_pending = false;
385+
posix_ipc_teardown();
386+
}
387+
#endif
388+
365389
#ifdef CONFIG_IPC_MAJOR_4
366390
memset(posix_hostbox, 0, SOF_IPC_MSG_MAX_SIZE);
367391
memcpy(posix_hostbox,

0 commit comments

Comments
 (0)