Skip to content

fuzzing: small improvements for ipc3 and ipc4#10819

Open
tmleman wants to merge 6 commits into
thesofproject:mainfrom
tmleman:topic/upstream/pr/fuzzing/enhancement/part1
Open

fuzzing: small improvements for ipc3 and ipc4#10819
tmleman wants to merge 6 commits into
thesofproject:mainfrom
tmleman:topic/upstream/pr/fuzzing/enhancement/part1

Conversation

@tmleman
Copy link
Copy Markdown
Contributor

@tmleman tmleman commented May 28, 2026

Fuzzer improvements – part 1

First batch of improvements to the SOF IPC fuzzer harness, together with the SOF-side fixes that fuzzing uncovered along the way. The goal of this part is to land the least invasive changes that immediately improve coverage and stop the harness from hiding real bugs, before any of the larger refactors that will follow in later parts.

A tracking issue with the full roadmap will be opened separately.

Changes

Harness / coverage:

  • platform: posix: fix extern type mismatch for posix_fuzz_buf/sz
  • fuzz: posix: size MAILBOX_HOSTBOX from SOF_IPC_MSG_MAX_SIZE
  • fuzz: posix: mirror IPC4 payload into MAILBOX_HOSTBOX
  • platform: posix: fix MAILBOX_*_BASE byte-pointer units

SOF fixes surfaced by the fuzzer:

  • ipc4: helper: reject pipeline ext payload larger than hostbox
  • ipc4: handler: bounds-check multi-pipeline SET_PIPELINE_STATE count

See individual commit messages for rationale and reproduction details.

Copilot AI review requested due to automatic review settings May 28, 2026 14:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 the native POSIX IPC fuzzing harness for SOF (IPC3 and IPC4) to increase coverage and avoid masking firmware bugs, and it includes two IPC4-side bounds checks uncovered by fuzzing.

Changes:

  • POSIX fuzz harness: fix fuzz buffer extern typing, align hostbox sizing with SOF_IPC_MSG_MAX_SIZE, and correct mailbox base macros to use byte-pointer arithmetic.
  • POSIX fuzz harness: mirror fuzz input into MAILBOX_HOSTBOX so IPC handlers that read directly from the hostbox see fuzzed payload bytes.
  • IPC4 firmware hardening: reject oversized pipeline extension payloads and bound-check multi-pipeline SET_PIPELINE_STATE counts.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/platform/posix/ipc.c Updates fuzz externs and mirrors fuzz data into the hostbox for better IPC3/IPC4 handler coverage.
src/platform/posix/include/platform/lib/memory.h Sizes MAILBOX_HOSTBOX from SOF_IPC_MSG_MAX_SIZE and fixes mailbox base macros to be byte-addressed.
src/platform/posix/fuzz.c Aligns fuzz buffer assignment/comment with the updated extern typing.
src/ipc/ipc4/helper.c Adds an early return to reject pipeline ext payload larger than the hostbox.
src/ipc/ipc4/handler-user.c Adds a mailbox-size-based cap on multi-pipeline pipelines_count to prevent OOB reads.

Comment thread src/platform/posix/ipc.c
tmleman added 2 commits May 28, 2026 17:27
The fuzzer entry point in src/platform/posix/fuzz.c defines the
shared staging symbols as:

  const uint8_t *posix_fuzz_buf;
  size_t        posix_fuzz_sz;

but src/platform/posix/ipc.c forward-declared them as

  extern uint8_t *posix_fuzz_buf, posix_fuzz_sz;

Match the definitions in ipc.c and drop the now-unnecessary cast in
fuzz.c. Also fix the stale 'native_fuzz_buf' reference in the doc
comment - the symbol was renamed long ago.

No functional change is intended on testcases whose size happens to
fit in 8 bits; longer inputs (the common case for IPC4 large_config
payloads) were previously being silently truncated.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
The POSIX fuzzing platform hard-codes MAILBOX_HOSTBOX_SIZE to 1024
bytes, which is smaller than the IPC4 maximum message size
(SOF_IPC_MSG_MAX_SIZE = 0x1000 = 4096 bytes). This is too small to
hold the full IPC4 message envelope and blocks the harness from
populating the hostbox with realistic fuzz payload for IPC4 paths
that read from MAILBOX_HOSTBOX_BASE.

It is also a latent overflow: ipc_platform_do_cmd() in posix/ipc.c
calls memcpy(posix_hostbox, comp_data, SOF_IPC_MSG_MAX_SIZE) on the
IPC3 path. Today this is safe only because IPC3 SOF_IPC_MSG_MAX_SIZE
is 384, but any Kconfig change that grows that value would write past
the end of the 1024-byte posix_hostbox array.

Derive MAILBOX_HOSTBOX_SIZE from SOF_IPC_MSG_MAX_SIZE so the storage
always matches the maximum framed message. The posix_hostbox array
declaration in posix.c is sized via the same macro and tracks
automatically.

No behaviour change for existing IPC3 flows. Prepares the harness for
a follow-up commit that copies IPC4 fuzz payload into the hostbox so
large_config / set_dx / pipeline-state-data handlers see meaningful
input.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
@tmleman tmleman force-pushed the topic/upstream/pr/fuzzing/enhancement/part1 branch from c9e2c27 to 0998622 Compare May 28, 2026 15:50
tmleman added 4 commits May 28, 2026 19:08
On real hardware (and in the IPC3 harness path) the host kernel deposits
the IPC payload into the hostbox shared-memory region before the DSP
firmware is signalled.  Many IPC4 handlers consume their payload by
reading from MAILBOX_HOSTBOX_BASE directly -- for example
LARGE_CONFIG_SET / LARGE_CONFIG_GET, vendor config, SET_DX and
SET_PIPELINE_STATE in sof/src/ipc/ipc4/handler-user.c and
ipc/ipc4/handler-kernel.c.  Until now the posix fuzz harness only
populated the IPC3 hostbox; in IPC4 builds the region stayed
zero-filled, so those handlers either rejected the message early or
operated on uninitialised data instead of on the fuzzer-controlled
bytes.

Lift the existing mailbox mirror copy out of the IPC3-only branch so
it runs for both major versions.  posix_hostbox is sized to
SOF_IPC_MSG_MAX_SIZE (see Commit  "fuzz: posix: size MAILBOX_HOSTBOX
from SOF_IPC_MSG_MAX_SIZE"), so the copy length is correct for both
targets.  Update the function comment to record the new contract.

This immediately exposes additional reachable code in the IPC4 build
(payload decoders that previously saw only zeros) without affecting
IPC3 behaviour.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
On every other SOF platform, MAILBOX_HOSTBOX_BASE, MAILBOX_DSPBOX_BASE,
MAILBOX_STREAM_BASE and MAILBOX_TRACE_BASE expand to a byte address
(an integer literal or SRAM_INBOX_BASE), so the generic mailbox API in
sof/src/include/sof/lib/mailbox.h can do plain byte arithmetic --
`MAILBOX_HOSTBOX_BASE + offset` and `memcpy(_s)(..., bytes)` -- and
land on the intended byte.

On POSIX, the bases were defined as `(&posix_hostbox[0])` etc., i.e.
plain `uint32_t *` expressions.  Pointer arithmetic on a `uint32_t *`
scales the addend by `sizeof(uint32_t) == 4`, so
`MAILBOX_HOSTBOX_BASE + offset` silently addressed byte
`offset * 4`, four times further into the buffer than the API
contract.

This was latent for years because MAILBOX_HOSTBOX_SIZE was hard-coded
to 1024 on POSIX while the largest byte offset used through
mailbox_hostbox_read() in IPC3 mailbox_validate() (offset = 8, bytes =
SOF_IPC_MSG_MAX_SIZE - 8 = 376) stays within 32 + 376 = 408 bytes,
comfortably under 1024.  After commit "fuzz: posix: size MAILBOX_HOSTBOX
from SOF_IPC_MSG_MAX_SIZE" (384 in IPC3 builds, 4096 in IPC4 builds),
the scaled IPC3 read overruns the now exactly right-sized backing buffer
by 24 bytes, which AddressSanitizer catches as a global-buffer-overflow
inside libc memcpy called from mailbox_hostbox_read() -> memcpy_s().
Reproducer (with the new 2-byte framing): two-byte fuzz input "\x80\x01"
(msgsz=384, header-only message).

Cast each base to `(uint8_t *)` so byte-offset arithmetic is honoured
and the macro semantics match every other platform.  The `uint32_t[]`
backing storage is kept for natural alignment; only how the macro
exposes that storage changes.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
ipc4_create_pipeline_payload_decode() reads pipeline extension
objects out of the hostbox region.  The function already validates
that `size = hdr->payload_words * 4` is at least `sizeof(*hdr)`, but
the symmetric upper bound check against MAILBOX_HOSTBOX_SIZE was
emitting tr_err() and continuing.  Once the loop is entered, every
per-object bounds check at lines 290 and 299 is expressed relative to
the attacker-controlled `size`, so a payload header that claims
`payload_words` significantly larger than the mailbox lets the decoder
walk `obj` arbitrarily far past the end of MAILBOX_HOSTBOX_BASE while
dereferencing `obj->object_words` and `obj->object_id`.

This was unreachable on real hardware because the host kernel is
trusted to bound the payload, and unreachable in the SOF fuzz harness
until commit "fuzz: posix: mirror IPC4 payload into MAILBOX_HOSTBOX"
wired the fuzz-controlled bytes into the hostbox. A 60 s libFuzzer/ASan
campaign with the IPC4 dictionary then surfaced a reproducible
global-buffer-overflow at helper.c:298 (READ of size 4 at `posix_hostbox
+ 4096`).

Convert the existing warning into a hard rejection so the decoder
returns -EINVAL before entering the object-walk loop.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
For multi_ppl=1, ipc4_set_pipeline_state() takes pipelines_count
straight from the host mailbox and uses it as the ppl_id[] loop
bound, with no validation against the mailbox size.

The IPC4 fuzzer reached this path with a 6-byte input that decodes
to type=SOF_IPC4_GLB_SET_PIPELINE_STATE, multi_ppl=1 and a count
field that easily exceeds MAILBOX_HOSTBOX. Once an earlier testcase
had created a matching pipeline, the ppl_id[i] read walked past the
end of the hostbox and AddressSanitizer reported a heap-buffer
overflow.

Cap pipelines_count at what the hostbox can actually hold and reject
oversized requests with IPC4_ERROR_INVALID_PARAM, logging the offending
count via ipc_cmd_err() for parity with the surrounding handlers.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
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.

4 participants