From 49d9c4eaa1eb5c28250b425fe526fd7c55f0a305 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Mar 2026 20:09:27 +0000 Subject: [PATCH 01/10] zephyr: app: board: add native_sim board target Add native_sim board configuration and support in the build script. This allows building and running tests on the host using Zephyr's native_sim target. native_sim leverages the POSIX architecture, but the libfuzzer support specifically requires CONFIG_ARCH_POSIX_LIBFUZZER to be set. Therefore, this wraps fuzzer-specific code in ipc.c and the build of fuzz.c behind this config to allow clean compilation on the standard native_sim board. Signed-off-by: Liam Girdwood --- app/boards/native_sim.conf | 4 ++++ scripts/xtensa-build-zephyr.py | 4 ++++ src/platform/posix/ipc.c | 6 ++++++ zephyr/CMakeLists.txt | 3 +++ 4 files changed, 17 insertions(+) create mode 100644 app/boards/native_sim.conf diff --git a/app/boards/native_sim.conf b/app/boards/native_sim.conf new file mode 100644 index 000000000000..6df3eb89b648 --- /dev/null +++ b/app/boards/native_sim.conf @@ -0,0 +1,4 @@ +CONFIG_ZEPHYR_POSIX=y +CONFIG_SYS_HEAP_BIG_ONLY=y +CONFIG_ZEPHYR_NATIVE_DRIVERS=y +CONFIG_ZEPHYR_LOG=y diff --git a/scripts/xtensa-build-zephyr.py b/scripts/xtensa-build-zephyr.py index cdce063b8783..02a60df4d7c1 100755 --- a/scripts/xtensa-build-zephyr.py +++ b/scripts/xtensa-build-zephyr.py @@ -238,6 +238,10 @@ class PlatformConfig: "zephyr", "qemu_xtensa/dc233c/mmu", "", "", "zephyr" ), + "native_sim" : PlatformConfig( + "zephyr", "native_sim", + "", "", "zephyr" + ), } platform_configs = platform_configs_all.copy() diff --git a/src/platform/posix/ipc.c b/src/platform/posix/ipc.c index 701686e6ff97..14e56da530db 100644 --- a/src/platform/posix/ipc.c +++ b/src/platform/posix/ipc.c @@ -14,6 +14,7 @@ SOF_DEFINE_REG_UUID(ipc_task_posix); static struct ipc *global_ipc; +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER // Not an ISR, called from the native_posix fuzz interrupt. Left // alone for general hygiene. This is how a IPC interrupt would look // if we had one. @@ -131,6 +132,7 @@ static void fuzz_isr(const void *arg) posix_ipc_isr(NULL); } +#endif // This API is... confounded by its history. With IPC3, the job of // this function is to get a newly-received IPC message header (!) @@ -172,12 +174,14 @@ int ipc_platform_compact_read_msg(struct ipc_cmd_hdr *hdr, int words) // Re-raise the interrupt if there's still fuzz data to process void ipc_platform_complete_cmd(struct ipc *ipc) { +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER extern void posix_sw_set_pending_IRQ(unsigned int IRQn); if (fuzz_in_sz > 0) { posix_fuzz_sz = 0; posix_sw_set_pending_IRQ(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ); } +#endif } int ipc_platform_send_msg(const struct ipc_msg *msg) @@ -200,8 +204,10 @@ void ipc_platform_send_msg_direct(const struct ipc_msg *msg) int platform_ipc_init(struct ipc *ipc) { +#ifdef CONFIG_ARCH_POSIX_LIBFUZZER IRQ_CONNECT(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ, 0, fuzz_isr, NULL, 0); irq_enable(CONFIG_ZEPHYR_POSIX_FUZZ_IRQ); +#endif global_ipc = ipc; schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_posix_uuid), diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 2d33667ee29b..d9518c61c01d 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -516,6 +516,9 @@ zephyr_library_sources_ifdef(CONFIG_ZEPHYR_POSIX ${SOF_PLATFORM_PATH}/posix/dai.c ${SOF_PLATFORM_PATH}/posix/ipc.c ${SOF_PLATFORM_PATH}/posix/posix.c +) + +zephyr_library_sources_ifdef(CONFIG_ARCH_POSIX_LIBFUZZER ${SOF_PLATFORM_PATH}/posix/fuzz.c ) From 29dd4337bb9b461bf07a8f934be002d8713446b4 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 12 Mar 2026 20:18:15 +0000 Subject: [PATCH 02/10] scripts: sof-qemu-run: add native_sim and valgrind support Add native_sim board target to the sof-qemu-run scripts, and add an option to additionally run it under valgrind. The default build directory is set to ../build-native_sim Signed-off-by: Liam Girdwood --- scripts/sof-qemu-run.py | 112 +++++++++++++++++++++++++++++----------- scripts/sof-qemu-run.sh | 55 ++++++++++++++++---- 2 files changed, 126 insertions(+), 41 deletions(-) diff --git a/scripts/sof-qemu-run.py b/scripts/sof-qemu-run.py index fc985e254d51..0344639955a8 100755 --- a/scripts/sof-qemu-run.py +++ b/scripts/sof-qemu-run.py @@ -76,9 +76,12 @@ def main(): parser = argparse.ArgumentParser(description="Run QEMU via west and automatically decode crashes.") parser.add_argument("--build-dir", default="build", help="Path to the build directory containing zephyr.elf, linker.cmd, etc. Defaults to 'build'.") parser.add_argument("--log-file", default="qemu-run.log", help="Path to save the QEMU output log. Defaults to 'qemu-run.log'.") + parser.add_argument("--valgrind", action="store_true", help="Run the executable under Valgrind (only valid for native_sim).") args = parser.parse_args() # Make absolute path just in case + # The shell script cd's into `args.build_dir` before executing us, so `args.build_dir` might be relative to the shell script's pwd. + # We resolve it relative to the python script's original invocation cwd. build_dir = os.path.abspath(args.build_dir) print(f"Starting QEMU test runner. Monitoring for crashes (Build Dir: {args.build_dir})...") @@ -91,7 +94,53 @@ def main(): print("Please ensure you have sourced the Zephyr environment (e.g., source zephyr-env.sh).") sys.exit(1) - child = pexpect.spawn(west_path, ["-v", "build", "-t", "run"], encoding='utf-8') + # Detect the board configuration from CMakeCache.txt + is_native_sim = False + + cmake_cache = os.path.join(build_dir, "CMakeCache.txt") + + if os.path.isfile(cmake_cache): + with open(cmake_cache, "r") as f: + for line in f: + if line.startswith("CACHED_BOARD:STRING=") or line.startswith("BOARD:STRING="): + if "native_sim" in line.split("=", 1)[1].strip(): + is_native_sim = True + break + + # Determine execution command + # If the user is running the python script directly from outside the workspace, we need to provide the source directory. + # But if west finds it automatically (or we are in the build dir), providing `-s` might clear the CACHED_BOARD config. + run_cmd = [west_path, "-v", "build", "-d", build_dir] + + # Check if we are physically sitting inside the build directory + if os.path.abspath(".") != os.path.abspath(build_dir): + # We need to explicitly supply the app source to prevent west from crashing + app_source_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "app")) + run_cmd.extend(["-s", app_source_dir]) + + run_cmd.extend(["-t", "run"]) + + if args.valgrind: + if not is_native_sim: + print("[sof-qemu-run] Error: --valgrind is only supported for the native_sim board.") + sys.exit(1) + + print("[sof-qemu-run] Rebuilding before valgrind...") + subprocess.run([west_path, "build", "-d", build_dir], check=True) + + valgrind_path = shutil.which("valgrind") + if not valgrind_path: + print("[sof-qemu-run] Error: 'valgrind' command not found in PATH.") + sys.exit(1) + + exe_path = os.path.join(build_dir, "zephyr", "zephyr.exe") + if not os.path.isfile(exe_path): + print(f"[sof-qemu-run] Error: Executable not found at {exe_path}") + sys.exit(1) + + run_cmd = [valgrind_path, exe_path] + + child = pexpect.spawn(run_cmd[0], run_cmd[1:], encoding='utf-8') # We will accumulate output to check for crashes full_output = "" @@ -157,36 +206,39 @@ def main(): run_sof_crash_decode(build_dir, full_output) else: - print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...") - - # We need to send Ctrl-A c to enter the monitor - if child.isalive(): - child.send("\x01c") # Ctrl-A c - try: - # Wait for (qemu) prompt - child.expect(r"\(qemu\)", timeout=5) - # Send "info registers" - child.sendline("info registers") - # Wait for the next prompt - child.expect(r"\(qemu\)", timeout=5) - - info_regs_output = child.before - print("\n[sof-qemu-run] Successfully extracted registers from QEMU monitor.\n") - - # Quit qemu safely - child.sendline("quit") - child.expect(pexpect.EOF, timeout=2) - child.close() - - # Run the decoder on the intercepted register output - run_sof_crash_decode(build_dir, info_regs_output) - except pexpect.TIMEOUT: - print("\n[sof-qemu-run] Timed out waiting for QEMU monitor. Is it running?") - child.close(force=True) - except pexpect.EOF: - print("\n[sof-qemu-run] QEMU terminated before we could run monitor commands.") + if is_native_sim: + print("\n[sof-qemu-run] No crash detected. (Skipping QEMU monitor interaction for native_sim)") else: - print("\n[sof-qemu-run] Process is no longer alive, cannot extract registers.") + print("\n[sof-qemu-run] No crash detected. Interacting with QEMU Monitor to grab registers...") + + # We need to send Ctrl-A c to enter the monitor + if child.isalive(): + child.send("\x01c") # Ctrl-A c + try: + # Wait for (qemu) prompt + child.expect(r"\(qemu\)", timeout=5) + # Send "info registers" + child.sendline("info registers") + # Wait for the next prompt + child.expect(r"\(qemu\)", timeout=5) + + info_regs_output = child.before + print("\n[sof-qemu-run] Successfully extracted registers from QEMU monitor.\n") + + # Quit qemu safely + child.sendline("quit") + child.expect(pexpect.EOF, timeout=2) + child.close() + + # Run the decoder on the intercepted register output + run_sof_crash_decode(build_dir, info_regs_output) + except pexpect.TIMEOUT: + print("\n[sof-qemu-run] Timed out waiting for QEMU monitor. Is it running?") + child.close(force=True) + except pexpect.EOF: + print("\n[sof-qemu-run] QEMU terminated before we could run monitor commands.") + else: + print("\n[sof-qemu-run] Process is no longer alive, cannot extract registers.") if __name__ == "__main__": main() diff --git a/scripts/sof-qemu-run.sh b/scripts/sof-qemu-run.sh index e1ece1dd5125..ef915fbb1318 100755 --- a/scripts/sof-qemu-run.sh +++ b/scripts/sof-qemu-run.sh @@ -2,17 +2,54 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2026 Intel Corporation. All rights reserved. -# Define the build directory from the first argument (or default) -BUILD_DIR="${1:-build}" +# Get the directory of this script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOF_WORKSPACE="$(dirname "$(dirname "$SCRIPT_DIR")")" + +TARGET="native_sim" +VALGRIND_ARG="" + +usage() { + cat <' relative + to the workspace. (default: native_sim) +EOF + exit 0 +} + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + usage + ;; + --valgrind) + VALGRIND_ARG="--valgrind" + ;; + *) + # Allow users who pass the directory name directly out of habit to still work + if [[ "$1" == build-* ]]; then + TARGET="${1#build-}" + else + TARGET="$1" + fi + ;; + esac + shift +done + +BUILD_DIR="${SOF_WORKSPACE}/build-${TARGET}" # Find and source the zephyr environment script, typically via the sof-venv wrapper # or directly if running in a known zephyrproject layout. # We will use the existing helper sof-venv.sh to get the right environment. -# Get the directory of this script -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -SOF_WORKSPACE="$(dirname "$(dirname "$SCRIPT_DIR")")" - # Use the SOF workspace to locate the virtual environment VENV_DIR="$SOF_WORKSPACE/.venv" echo "Using SOF environment at $SOF_WORKSPACE" @@ -20,9 +57,5 @@ echo "Using SOF environment at $SOF_WORKSPACE" # start the virtual environment source ${VENV_DIR}/bin/activate -# Execute the QEMU runner from within the correct build directory -cd "${BUILD_DIR}" || exit 1 - # Finally run the python script which will now correctly inherit 'west' from the sourced environment. -python3 "${SCRIPT_DIR}/sof-qemu-run.py" --build-dir "${BUILD_DIR}" - +python3 "${SCRIPT_DIR}/sof-qemu-run.py" --build-dir "${BUILD_DIR}" $VALGRIND_ARG From 5f6c2f2207daddb584eb5251cb2140d3e463576a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 13 Mar 2026 14:06:34 +0000 Subject: [PATCH 03/10] zephyr: alloc: Map native_sim heap to the C library When building the firmware for native_sim, debugging allocations with host machine tools like Valgrind is constrained due to Zephyr's internal minimal libc tracking the heap manually via static pools. By bypassing Zephyr's memory interception on native_sim using nsi_host_malloc, dynamically tracked memory can surface appropriately to Valgrind memory checkers without causing a libc heap pool panic. Signed-off-by: Liam Girdwood --- zephyr/Kconfig | 14 ++++++ zephyr/lib/alloc.c | 104 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f1d1896c4234..1301a0f1ae90 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -46,6 +46,20 @@ config SOF_ZEPHYR_HEAP_CACHED Enable cached heap by mapping cached SOF memory zones to different Zephyr sys_heap objects and enable caching for non-shared zones. +config SOF_NATIVE_SIM_HOST_HEAP + bool "Use the host C library heap on native_sim" + depends on BOARD_NATIVE_SIM + default y + help + When enabled on the native_sim board, SOF heap allocations + (rmalloc/rzalloc/rballoc/rfree etc.) are routed through the + host C library via the Zephyr native simulator host trampolines + (nsi_host_malloc/nsi_host_free). This makes tools like Valgrind + effective at finding heap errors in SOF code. + + When disabled, the regular Zephyr sys_heap based implementation + is used, matching the behavior on real targets. + config SOF_ZEPHYR_HEAP_SIZE hex "Size of the Zephyr heap for SOF" default 0x40000 if VIRTUAL_HEAP diff --git a/zephyr/lib/alloc.c b/zephyr/lib/alloc.c index 8192c2caff0f..c65e35faa1e2 100644 --- a/zephyr/lib/alloc.c +++ b/zephyr/lib/alloc.c @@ -149,6 +149,7 @@ extern char _end[], _heap_sentry[]; static struct k_heap sof_heap; +#if !defined(CONFIG_SOF_NATIVE_SIM_HOST_HEAP) /** * Checks whether pointer is from a given heap memory. * @param heap Pointer to a heap. @@ -167,6 +168,7 @@ static bool is_heap_pointer(const struct k_heap *heap, void *ptr) return ((POINTER_TO_UINT(ptr) >= heap_start) && (POINTER_TO_UINT(ptr) < heap_end)); } +#endif #if CONFIG_SOF_USERSPACE_USE_SHARED_HEAP static struct k_heap shared_buffer_heap; @@ -391,6 +393,7 @@ struct k_heap *sof_sys_user_heap_get(void) #endif } +#if !defined(CONFIG_SOF_NATIVE_SIM_HOST_HEAP) static void *heap_alloc_aligned(struct k_heap *h, size_t min_align, size_t bytes) { k_spinlock_key_t key; @@ -460,7 +463,106 @@ static void heap_free(struct k_heap *h, void *mem) k_spin_unlock(&h->lock, key); } +#endif + +#if defined(CONFIG_SOF_NATIVE_SIM_HOST_HEAP) +#include +#include + +void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) +{ + void *ptr; + void *raw; + + if (alignment < sizeof(void *)) + alignment = sizeof(void *); + + raw = nsi_host_malloc(bytes + alignment + sizeof(void *)); + if (!raw) + return NULL; + + ptr = (void *)(((uintptr_t)raw + sizeof(void *) + alignment - 1) & ~(alignment - 1)); + ((void **)ptr)[-1] = raw; + + return ptr; +} +EXPORT_SYMBOL(rmalloc_align); + +void *rmalloc(uint32_t flags, size_t bytes) +{ + return rmalloc_align(flags, bytes, 0); +} +EXPORT_SYMBOL(rmalloc); + +void *rbrealloc_align(void *ptr, uint32_t flags, size_t bytes, + size_t old_bytes, uint32_t alignment) +{ + void *new_ptr; + + if (!ptr) + return rmalloc_align(flags, bytes, alignment); + + if (!bytes) { + void *raw = ((void **)ptr)[-1]; + + nsi_host_free(raw); + return NULL; + } + + new_ptr = rmalloc_align(flags, bytes, alignment); + if (!new_ptr) + return NULL; + + if (!(flags & SOF_MEM_FLAG_NO_COPY)) + memcpy_s(new_ptr, bytes, ptr, MIN(bytes, old_bytes)); + + void *raw_old = ((void **)ptr)[-1]; + + nsi_host_free(raw_old); + + return new_ptr; +} + +void *rzalloc(uint32_t flags, size_t bytes) +{ + void *ptr = rmalloc_align(flags, bytes, 0); + + if (ptr) + memset(ptr, 0, bytes); + return ptr; +} +EXPORT_SYMBOL(rzalloc); + +void *rballoc_align(uint32_t flags, size_t bytes, uint32_t align) +{ + return rmalloc_align(flags, bytes, align); +} +EXPORT_SYMBOL(rballoc_align); + +void rfree(void *ptr) +{ + if (!ptr) + return; + + void *raw = ((void **)ptr)[-1]; + + nsi_host_free(raw); +} +EXPORT_SYMBOL(rfree); + +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + return rmalloc_align(flags, bytes, alignment); +} + +void sof_heap_free(struct k_heap *heap, void *addr) +{ + rfree(addr); +} + +#else void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) { @@ -651,6 +753,8 @@ void sof_heap_free(struct k_heap *heap, void *addr) rfree(addr); } +#endif /* CONFIG_SOF_NATIVE_SIM_HOST_HEAP */ + static int heap_init(void) { sys_heap_init(&sof_heap.heap, heapmem, HEAPMEM_SIZE - SHARED_BUFFER_HEAP_MEM_SIZE); From cd8c007e7da808e1134ecf7d96d1f5999a8b5aa9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 27 Mar 2026 17:26:21 +0000 Subject: [PATCH 04/10] shell: fix build error In file included from /home/lrg/work/sof2/sof/zephyr/sof_shell.c:14: /home/lrg/work/sof2/sof/zephyr/sof_shell.c: In function 'cmd_sof_module_heap_usage': /home/lrg/work/sof2/sof/zephyr/sof_shell.c:66:77: error: 'struct module_config' has no member named 'heap_bytes' 66 | icd->id, usage, hwm, comp_mod(icd->cd)->priv.cfg.heap_bytes); | ^ /home/lrg/work/sof2/zephyr/include/zephyr/shell/shell.h:1292:47: note: in definition of macro 'shell_print' 1292 | shell_fprintf_normal(_sh, _ft "\n", ##__VA_ARGS__) | ^~~~~~~~~~~ Signed-off-by: Liam Girdwood --- zephyr/sof_shell.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zephyr/sof_shell.c b/zephyr/sof_shell.c index f10a2c9275b5..9e90abe417bb 100644 --- a/zephyr/sof_shell.c +++ b/zephyr/sof_shell.c @@ -62,8 +62,8 @@ static int cmd_sof_module_heap_usage(const struct shell *sh, continue; usage = module_adapter_heap_usage(comp_mod(icd->cd), &hwm); - shell_print(sh, "comp id 0x%08x%9zu usage%9zu hwm %9zu max\tbytes", - icd->id, usage, hwm, comp_mod(icd->cd)->priv.cfg.heap_bytes); + shell_print(sh, "comp id 0x%08x%9zu usage%9zu hwm\tbytes", + icd->id, usage, hwm); } return 0; } From 9c4f29f2a62ff84360308f899dc2fc6e0bf82528 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 27 Mar 2026 18:56:53 +0000 Subject: [PATCH 05/10] qemu: exit for boot test only otherwise spin Keep spinning in case user needs to inspect status via monitor. Signed-off-by: Liam Girdwood --- app/src/main.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main.c b/app/src/main.c index 12cc3ffdc73a..5272b1ae0605 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -57,7 +57,7 @@ static int sof_app_main(void) return 0; } -#if CONFIG_SOF_BOOT_TEST && defined(QEMU_BOOT_TESTS) +#if defined(QEMU_BOOT_TESTS) /* cleanly exit qemu so CI can continue and check test results */ static inline void qemu_xtensa_exit(int status) { @@ -73,6 +73,18 @@ static inline void qemu_xtensa_exit(int status) } #endif +#ifdef CONFIG_REBOOT +void sys_arch_reboot(int type) +{ +#if defined(QEMU_BOOT_TESTS) + qemu_xtensa_exit(type); +#endif + while (1) { + k_cpu_idle(); + } +} +#endif + #if CONFIG_ZTEST void test_main(void) { From c06f542ab85842615d815d5f96867fbaca5ee0b3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 30 Mar 2026 17:58:03 +0100 Subject: [PATCH 06/10] zephyr: alloc: Add 16MB allocation limit to native_sim host allocator When building the native_sim fuzzer, the host allocator does not possess the strict bounds of the internal Zephyr memory pools. If the fuzzer generates a malformed payload requesting an excessively large size (e.g. 4GB), it passes directly to the host ASAN allocator which aborts due to OOM or protection limits. Adding a 16MB cap allows these to fail gracefully. Signed-off-by: Liam Girdwood --- zephyr/lib/alloc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zephyr/lib/alloc.c b/zephyr/lib/alloc.c index c65e35faa1e2..589adff5905e 100644 --- a/zephyr/lib/alloc.c +++ b/zephyr/lib/alloc.c @@ -475,6 +475,11 @@ void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) void *ptr; void *raw; + if (bytes > 16 * 1024 * 1024) { + tr_err(&zephyr_tr, "rmalloc_align: requested %zu bytes exceeds 16MB limit", bytes); + return NULL; + } + if (alignment < sizeof(void *)) alignment = sizeof(void *); From 365142d0187c4dfdcf623f2d9e6829fd0dd30716 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 30 Mar 2026 17:59:07 +0100 Subject: [PATCH 07/10] platform: posix: Correct posix_fuzz_sz to size_t to prevent truncation The fuzzer's payload size posix_fuzz_sz is provided by libFuzzer as a size_t. Declaring it as a uint8_t in the ipc test harness resulted in silent payload truncation (maximum 255 bytes) causing incomplete corpus generation. This corrects the types between fuzz.c and ipc.c. Signed-off-by: Liam Girdwood --- src/platform/posix/fuzz.c | 2 ++ .../posix/include/platform/posix_fuzz.h | 25 +++++++++++++++++++ src/platform/posix/ipc.c | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/platform/posix/include/platform/posix_fuzz.h diff --git a/src/platform/posix/fuzz.c b/src/platform/posix/fuzz.c index 555a22d40578..313785427d1a 100644 --- a/src/platform/posix/fuzz.c +++ b/src/platform/posix/fuzz.c @@ -11,6 +11,8 @@ #include #include +#include + const uint8_t *posix_fuzz_buf; size_t posix_fuzz_sz; diff --git a/src/platform/posix/include/platform/posix_fuzz.h b/src/platform/posix/include/platform/posix_fuzz.h new file mode 100644 index 000000000000..9516dbf4fd48 --- /dev/null +++ b/src/platform/posix/include/platform/posix_fuzz.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2026 Intel Corporation. All rights reserved. + */ + +/** + * Symbols shared between the native_posix fuzz harness + * (src/platform/posix/fuzz.c) and the SOF posix IPC layer + * (src/platform/posix/ipc.c). + * + * Defining them in one place avoids type-mismatch bugs (e.g. a single + * `extern uint8_t *posix_fuzz_buf, posix_fuzz_sz;` declaring `posix_fuzz_sz` + * as a `uint8_t` rather than a `size_t`). + */ + +#ifndef PLATFORM_POSIX_FUZZ_H +#define PLATFORM_POSIX_FUZZ_H + +#include +#include + +extern const uint8_t *posix_fuzz_buf; +extern size_t posix_fuzz_sz; + +#endif /* PLATFORM_POSIX_FUZZ_H */ diff --git a/src/platform/posix/ipc.c b/src/platform/posix/ipc.c index 14e56da530db..4dd6f2fd79b8 100644 --- a/src/platform/posix/ipc.c +++ b/src/platform/posix/ipc.c @@ -24,7 +24,7 @@ static void posix_ipc_isr(void *arg) } // External symbols set up by the fuzzing layer -extern uint8_t *posix_fuzz_buf, posix_fuzz_sz; +#include // Lots of space. Should really synchronize with the -max_len // parameter to libFuzzer (defaults to 4096), but that requires From 01e4691df5006f8e40ca9080ebb165f15bfdd9f7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 30 Mar 2026 18:08:25 +0100 Subject: [PATCH 08/10] qemu: maths: build the math ztests on native_sim Add the math basic-arithmetic ztests (test_crc32_ztest.c, test_find_*_ztest.c, test_gcd_ztest.c, test_norm_int32_ztest.c) to the SOF boot-test build under zephyr/test/CMakeLists.txt when both CONFIG_SOF_BOOT_TEST_STANDALONE and CONFIG_ZTEST are enabled. The math helpers themselves (find_*_int*, norm_int32) are gated by CONFIG_NUMBERS_VECTOR_FIND / CONFIG_NUMBERS_NORM in src/math/numbers.c and are now enabled per-board in app/boards/native_sim.conf, where CONFIG_ZTEST is set. This keeps the gating clean and explicit per board rather than coupling math source compilation to CONFIG_ZTEST. Signed-off-by: Liam Girdwood --- zephyr/test/CMakeLists.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/zephyr/test/CMakeLists.txt b/zephyr/test/CMakeLists.txt index f548c98c5e73..119e30d1293f 100644 --- a/zephyr/test/CMakeLists.txt +++ b/zephyr/test/CMakeLists.txt @@ -26,3 +26,20 @@ endif() if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_SOF_USERSPACE_LL) zephyr_library_sources(userspace/test_ll_task.c) endif() + +if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_ZTEST) + set(MATH_ZTEST_SOURCES + ../../test/ztest/unit/math/basic/arithmetic/test_crc32_ztest.c + ../../test/ztest/unit/math/basic/arithmetic/test_find_equal_int16_ztest.c + ../../test/ztest/unit/math/basic/arithmetic/test_find_max_abs_int32_ztest.c + ../../test/ztest/unit/math/basic/arithmetic/test_find_min_int16_ztest.c + ../../test/ztest/unit/math/basic/arithmetic/test_gcd_ztest.c + ../../test/ztest/unit/math/basic/arithmetic/test_norm_int32_ztest.c + ) + + zephyr_library_sources(${MATH_ZTEST_SOURCES}) + + set_source_files_properties(${MATH_ZTEST_SOURCES} + PROPERTIES COMPILE_DEFINITIONS "CONFIG_NUMBERS_VECTOR_FIND=1;CONFIG_NUMBERS_NORM=1;UNIT_TEST=1" + ) +endif() From e3b8ad9a5e63cc5d6f20f512334945b1988b8896 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 30 Mar 2026 18:15:45 +0100 Subject: [PATCH 09/10] qemu: native_sim: run the ztests and quit after completion Run the boot tests and quit when done. Signed-off-by: Liam Girdwood --- app/boards/native_sim.conf | 4 ++++ app/src/main.c | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/boards/native_sim.conf b/app/boards/native_sim.conf index 6df3eb89b648..f81ffebcb47e 100644 --- a/app/boards/native_sim.conf +++ b/app/boards/native_sim.conf @@ -2,3 +2,7 @@ CONFIG_ZEPHYR_POSIX=y CONFIG_SYS_HEAP_BIG_ONLY=y CONFIG_ZEPHYR_NATIVE_DRIVERS=y CONFIG_ZEPHYR_LOG=y +CONFIG_ZTEST=y +CONFIG_SOF_BOOT_TEST_STANDALONE=y +CONFIG_NUMBERS_VECTOR_FIND=y +CONFIG_NUMBERS_NORM=y diff --git a/app/src/main.c b/app/src/main.c index 5272b1ae0605..2135e7f27dca 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -89,10 +89,12 @@ void sys_arch_reboot(int type) void test_main(void) { sof_app_main(); -#if CONFIG_SOF_BOOT_TEST && defined(QEMU_BOOT_TESTS) +#if CONFIG_SOF_BOOT_TEST sof_run_boot_tests(); +#if defined(QEMU_BOOT_TESTS) qemu_xtensa_exit(0); #endif +#endif } #else int main(void) From 4dbff452d51ccc3ae3975365830050bc9b650d88 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 31 Mar 2026 12:49:16 +0100 Subject: [PATCH 10/10] scripts: xtensa-build-zephyr: Support native_sim architecture The xtensa-build-zephyr.py script attempts to parse and copy the zephyr.ri (rimage) file for reproducible checksums and installation. Since the native_sim platform does not produce an rimage file, building it with this script results in a FileNotFoundError. Adding native_sim to the list of exceptions resolves the build failure. Signed-off-by: Liam Girdwood --- scripts/xtensa-build-zephyr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/xtensa-build-zephyr.py b/scripts/xtensa-build-zephyr.py index 02a60df4d7c1..6385b062fdfb 100755 --- a/scripts/xtensa-build-zephyr.py +++ b/scripts/xtensa-build-zephyr.py @@ -1170,7 +1170,7 @@ def install_platform(platform, sof_output_dir, platf_build_environ, platform_wco os.makedirs(install_key_dir, exist_ok=True) # looses file owner and group - file is commonly accessible, dont install qemu. - if platform not in ("qemu_xtensa", "qemu_xtensa_mmu"): + if platform not in ("qemu_xtensa", "qemu_xtensa_mmu", "native_sim"): shutil.copy2(abs_build_dir / "zephyr.ri", install_key_dir / output_fwname) if args.deployable_build and platform_configs[platform].ipc4: @@ -1324,7 +1324,7 @@ def gzip_compress(fname, gzdst=None): RI_INFO_UNSUPPORTED += ['imx8', 'imx8x', 'imx8m', 'imx8m_cm7', 'imx8ulp', 'imx95'] RI_INFO_UNSUPPORTED += ['rn', 'acp_6_0'] RI_INFO_UNSUPPORTED += ['mt8186', 'mt8188', 'mt8195', 'mt8196', 'mt8365'] -RI_INFO_UNSUPPORTED += ['qemu_xtensa', 'qemu_xtensa_mmu'] +RI_INFO_UNSUPPORTED += ['qemu_xtensa', 'qemu_xtensa_mmu', 'native_sim'] # For temporary workarounds. Unlike _UNSUPPORTED above, the platforms below will print a warning.