Skip to content

platform: posix: tear down IPC topology between fuzz testcases - #11066

Open
tmleman wants to merge 1 commit into
thesofproject:mainfrom
tmleman:topic/upstream/pr/fuzzing/enhancement/part7
Open

platform: posix: tear down IPC topology between fuzz testcases#11066
tmleman wants to merge 1 commit into
thesofproject:mainfrom
tmleman:topic/upstream/pr/fuzzing/enhancement/part7

Conversation

@tmleman

@tmleman tmleman commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Without an explicit teardown a fuzz testcase that successfully creates components, buffers or pipelines leaves them registered in global_ipc->comp_list. The next LLVMFuzzerTestOneInput() then sees a non-empty topology it never asked for, which both hides bugs (crashes that depend on freshly-empty state are missed) and fabricates them (crashes that only occur because of carry-over from a previous case are unreproducible when the artifact is replayed on its own).

Add a posix-only teardown helper, called from posix_fuzz_case_begin() once at the start of every testcase before the input is staged. The helper:

  • Runs a pre-pass that forces every COMP_TYPE_COMPONENT to COMP_STATE_READY and initialises any NULL bsource_list/bsink_list pointers, because ipc_comp_free() returns -EINVAL (and silently leaks the entry) for a component that is not READY or whose buffer lists were never list_init()'d - the latter happens when a component was registered but its init failed partway through. The pre-pass also cancels any active pipeline pipe_task so ipc_pipeline_free() does not stall waiting for it (up to 100 LL periods on native_sim).
  • Snapshots IDs into a local array per pass rather than walking and mutating comp_list simultaneously (the SOF free helpers unlink each entry).
  • Frees in dependency order COMPONENT -> BUFFER -> PIPELINE so the topology layer never dereferences an already-freed parent. The BUFFER pass is compiled out for IPC4 because ipc4/helper.c never stores COMP_TYPE_BUFFER and ipc_buffer_free() does not exist there.
  • Ends with a force-drain pass that removes anything still on the list with list_item_del() + rfree(), keeping comp_list guaranteed empty on return and the harness robust against future COMP_TYPE_* additions.

Without an explicit teardown a fuzz testcase that successfully creates
components, buffers or pipelines leaves them registered in
global_ipc->comp_list. The next LLVMFuzzerTestOneInput() then sees a
non-empty topology it never asked for, which both hides bugs (crashes
that depend on freshly-empty state are missed) and fabricates them
(crashes that only occur because of carry-over from a previous case are
unreproducible when the artifact is replayed on its own).

Add a posix-only teardown helper, called from posix_fuzz_case_begin()
once at the start of every testcase before the input is staged. The
helper:

  * Runs a pre-pass that forces every COMP_TYPE_COMPONENT to
    COMP_STATE_READY and initialises any NULL bsource_list/bsink_list
    pointers, because ipc_comp_free() returns -EINVAL (and silently
    leaks the entry) for a component that is not READY or whose buffer
    lists were never list_init()'d - the latter happens when a
    component was registered but its init failed partway through. The
    pre-pass also cancels any active pipeline pipe_task so
    ipc_pipeline_free() does not stall waiting for it (up to 100 LL
    periods on native_sim).
  * Snapshots IDs into a local array per pass rather than walking and
    mutating comp_list simultaneously (the SOF free helpers unlink each
    entry).
  * Frees in dependency order COMPONENT -> BUFFER -> PIPELINE so the
    topology layer never dereferences an already-freed parent. The
    BUFFER pass is compiled out for IPC4 because ipc4/helper.c never
    stores COMP_TYPE_BUFFER and ipc_buffer_free() does not exist there.
  * Ends with a force-drain pass that removes anything still on the list
    with list_item_del() + rfree(), keeping comp_list guaranteed empty
    on return and the harness robust against future COMP_TYPE_*
    additions.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves libFuzzer testcase isolation on the POSIX platform by ensuring each LLVMFuzzerTestOneInput() begins with an empty IPC topology, preventing cross-testcase carry-over in global_ipc->comp_list.

Changes:

  • Add a POSIX-only posix_ipc_teardown() helper that attempts to free any leftover IPC-tracked objects from the previous testcase.
  • Add a pre-pass to make leftover components/pipelines “freeable” (force component state to COMP_STATE_READY, initialize missing buffer-list heads, cancel active pipe_task).
  • Call the teardown from posix_fuzz_case_begin() so cleanup happens before staging new fuzz input.
Suppressed comments (1)

src/platform/posix/ipc.c:149

  • The force-drain pass is also capped at POSIX_TEARDOWN_MAX_DEVS, so if more than 256 entries remain it will leave comp_list non-empty. Since this pass deletes entries directly, it can safely walk with list_for_item_safe() and drain the entire list without a fixed-size ID snapshot.
	/*
	 * Force-drain anything remaining (unknown/future COMP_TYPE_*).
	 * Snapshot the residual list then remove each entry directly.
	 * The inner union pointer leaks, but comp_list will be empty
	 * and the next testcase will not observe stale entries.
	 */

Comment thread src/platform/posix/ipc.c
Comment on lines +117 to +121
for (int pass = 0; pass < (int)ARRAY_SIZE(free_order); pass++) {
uint16_t type = free_order[pass];

n = 0;
list_for_item(pos, &global_ipc->comp_list) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants