From b5a3c6f17c5e6913c0b3306145d5101a3afbaf24 Mon Sep 17 00:00:00 2001 From: Tomasz Leman Date: Wed, 27 May 2026 22:58:06 +0200 Subject: [PATCH] fuzz: posix: widen fuzz framing length to 2 bytes The fuzz harness in posix/ipc.c frames each IPC message using a length prefix at the start of the libFuzzer input buffer. Until now this prefix was a single byte multiplied by 2, encoding a maximum per-message payload of 510 bytes. That fits IPC3 (SOF_IPC_MSG_MAX_SIZE = 384) but caps IPC4 at ~12% of its 4096-byte envelope, hiding the entire large_config / vendor_config / pipeline-state-data path family from coverage feedback. Replace the prefix with two little-endian bytes, encoding lengths up to 65535 (still clamped to SOF_IPC_MSG_MAX_SIZE - 4 in code). The remainder of the framing logic is mechanically updated to consume two header bytes per message. Introduce POSIX_FUZZ_HDR_LEN to keep header arithmetic self-documenting. Existing IPC3 and IPC4 corpora are reinterpreted under the new header: what was previously a length byte becomes the low byte of a 16-bit length, with the next byte becoming the high byte. libFuzzer's mutation engine re-discovers productive frame shapes within minutes of running, so the corpora are kept rather than regenerated. Signed-off-by: Tomasz Leman --- src/platform/posix/ipc.c | 44 +++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/platform/posix/ipc.c b/src/platform/posix/ipc.c index feb4bb03873c..c8012a697847 100644 --- a/src/platform/posix/ipc.c +++ b/src/platform/posix/ipc.c @@ -58,16 +58,26 @@ void posix_fuzz_case_abort(void) fuzz_in_sz = 0; } -// 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 -// fuzzing heuristics). We then copy that much of the input buffer -// (subject to clamping obviously) into the incoming IPC message -// buffer and invoke the ISR. Any remainder will be delivered -// synchronously as another message after receipt of "complete_cmd()" -// from the SOF engine, etc... Eventually we'll receive another fuzz -// input after some amount of simulated time has passed (c.f. -// CONFIG_ZEPHYR_POSIX_FUZZ_TICKS) +// The framing protocol is super simple: the first two bytes are a +// little-endian message size in bytes (capped below at +// SOF_IPC_MSG_MAX_SIZE - 4 so it always fits in the IPC message +// buffer). We then copy that much of the input buffer (subject to +// clamping obviously) into the incoming IPC message buffer and invoke +// the ISR. Any remainder will be delivered synchronously as another +// message after receipt of "complete_cmd()" from the SOF engine, +// etc... Eventually we'll receive another fuzz input after some +// amount of simulated time has passed (c.f. +// CONFIG_ZEPHYR_POSIX_FUZZ_TICKS). +// +// The historical encoding used a single byte multiplied by 2, which +// capped each message at 510 bytes. That was fine for IPC3 +// (SOF_IPC_MSG_MAX_SIZE = 384) but limited IPC4 (max 4096) to ~12% +// of its envelope, hiding all large_config / vendor_config / +// pipeline-state-data paths from coverage feedback. Widening to two +// bytes lets the fuzzer reach the full IPC4 message range; existing +// corpora are reinterpreted but libFuzzer recovers within minutes. +#define POSIX_FUZZ_HDR_LEN 2 + static void fuzz_isr(const void *arg) { size_t rem, i, n = MIN(posix_fuzz_sz, sizeof(fuzz_in) - fuzz_in_sz); @@ -75,20 +85,22 @@ static void fuzz_isr(const void *arg) for (i = 0; i < n; i++) fuzz_in[fuzz_in_sz++] = posix_fuzz_buf[i]; - if (fuzz_in_sz == 0) + if (fuzz_in_sz < POSIX_FUZZ_HDR_LEN) return; if (!global_ipc->comp_data) return; - size_t maxsz = SOF_IPC_MSG_MAX_SIZE - 4, msgsz = fuzz_in[0] * 2; + size_t maxsz = SOF_IPC_MSG_MAX_SIZE - 4; + size_t msgsz = (size_t)fuzz_in[0] | ((size_t)fuzz_in[1] << 8); - n = MIN(msgsz, MIN(fuzz_in_sz - 1, maxsz)); - rem = fuzz_in_sz - (n + 1); + msgsz = MIN(msgsz, maxsz); + n = MIN(msgsz, fuzz_in_sz - POSIX_FUZZ_HDR_LEN); + rem = fuzz_in_sz - (n + POSIX_FUZZ_HDR_LEN); memset(global_ipc->comp_data, 0, maxsz); - memcpy(global_ipc->comp_data, &fuzz_in[1], n); - memmove(&fuzz_in[0], &fuzz_in[n + 1], rem); + memcpy(global_ipc->comp_data, &fuzz_in[POSIX_FUZZ_HDR_LEN], n); + memmove(&fuzz_in[0], &fuzz_in[n + POSIX_FUZZ_HDR_LEN], rem); fuzz_in_sz = rem; #ifdef CONFIG_IPC_MAJOR_3