audio: copier: reject overflowing gateway config_length#11006
Merged
kv2019i merged 1 commit intoJul 17, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens the IPC4 copier module initialization against hostile/invalid gtw_cfg.config_length values that previously could overflow 32-bit size arithmetic and lead to under-allocation and heap out-of-bounds reads during gateway-copier setup.
Changes:
- Add an early upper-bound validation for host-controlled
gtw_cfg.config_length(dwords) against the received init payload size before computing variable gateway blob sizes. - Emit a clear error and reject invalid configurations with
-EINVAL. - Keep the existing precise
cfg_total_size > md->cfg.sizeguard as a secondary check for non-overflowing size mismatches.
Fix a heap out-of-bounds access found by the IPC4 libFuzzer harness
(native_sim/x86, ASAN-enabled build with CONFIG_SYS_HEAP_SANITIZER_ASAN).
A single 173-byte crash input triggers a use-after-poison READ of size 4
in copier_dai_create():
==ERROR: AddressSanitizer: use-after-poison on address 0x088cf1c4
READ of size 4 ... in copier_dai_create copier_dai.c:284
thesofproject#1 copier_init copier.c:220
thesofproject#2 module_init generic.c:123
thesofproject#3 module_adapter_new_ext module_adapter.c:307
thesofproject#4 comp_new_ipc4 ipc4/helper.c:198
thesofproject#5 ipc4_init_module_instance handler-user.c:763
copier_dai.c:284 reads cd->config.gtw_cfg.config_length, which lands 8
bytes past the end of the under-allocated cd block.
Fuzzer payload
--------------
The harness frames the raw input as a 2-byte little-endian length prefix
followed by an 8-byte IPC4 compact header (primary + extension word) and
a payload that is mirrored into MAILBOX_HOSTBOX. The decoded message is
an INIT_MODULE_INSTANCE (MODULE_MSG / INIT_INSTANCE) whose module_id
resolves to the copier driver, so comp_new_ipc4() -> module_adapter_new()
-> copier_init() runs on host-controlled config bytes.
The copier's ipc4_copier_module_cfg carried (values from gdb):
gtw_cfg.node_id.dw = 0x050012f5
dma_type = 18 (ipc4_alh_uaol_stream_link_
output_class, a non-host ALH/UAOL link
gateway), v_index = 245
gtw_cfg.dma_buffer_size = 0xffffffff
gtw_cfg.config_length = 0xffffffff <-- the trigger
md->cfg.size (payload) = 888 bytes
node_id != IPC4_INVALID_NODE_ID selects the gateway-copier path, and the
UAOL link class routes into copier_dai_create(). The long run of 0xff
bytes at the tail of the input is what supplies config_length =
0xffffffff. libFuzzer discovered the message by mutating the corpus; the
crucial field is the all-ones config_length.
Root cause
----------
copier_init() sizes the variable-length gateway blob that trails the
config struct:
if (copier->gtw_cfg.config_length > 1) {
gtw_cfg_var_size += (copier->gtw_cfg.config_length - 1) << 2;
cfg_total_size += gtw_cfg_var_size;
}
if (cfg_total_size > md->cfg.size) /* payload guard */
return -EINVAL;
cd = mod_zalloc(mod, sizeof(*cd) + gtw_cfg_var_size);
config_length is a uint32_t from the untrusted payload. The expression
(config_length - 1) << 2 is evaluated in 32-bit arithmetic and wraps:
with 0xffffffff it yields 0xfffffff8, i.e. -8 as a size_t. That poisons
both dependent computations:
* cfg_total_size wraps down to 76 (below even sizeof(*copier) = 84),
so the "cfg_total_size > md->cfg.size" guard added earlier does not
fire (76 < 888).
* mod_zalloc() is called with sizeof(*cd) + (size_t)-8 = 452 - 8 =
444 bytes, while struct copier_data needs 452. gtw_cfg.config_length
sits at offset 444, exactly past the truncated allocation.
copier_dai_create() then reads that field (and the config_data pointer
right after it) out of bounds. size_t is 32-bit on native_sim and on the
real Xtensa DSP targets, so this is a genuine firmware bug, not just a
host-fuzzer artifact.
Fix
---
Validate config_length before any size arithmetic is performed on it.
The blob is expressed in dwords and must fit within the received init
payload, so an upper bound of md->cfg.size / sizeof(uint32_t) both is
correct and keeps (config_length - 1) << 2 from overflowing (the mailbox
payload is bounded by SOF_IPC_MSG_MAX_SIZE):
if (copier->gtw_cfg.config_length >
md->cfg.size / sizeof(uint32_t)) {
comp_err(dev, "copier_init(): invalid gtw_cfg.config_length %u",
copier->gtw_cfg.config_length);
return -EINVAL;
}
The existing precise "cfg_total_size > md->cfg.size" check is retained as
a secondary guard for the non-overflowing mismatch case.
Verification
------------
- The crash input now executes cleanly: config_length is rejected with
-EINVAL, exit code 0, no ASAN report. The new bound branch is
confirmed hit (config_length 0xffffffff vs bound 222).
- Full IPC4 corpus replay: 92,780 inputs, 0 crashes (-runs=0), coverage
unchanged (cov 1331 / ft 5270), confirming the check does not
over-restrict legitimate copier init payloads.
Found-by: IPC4 libFuzzer (ASAN, native_sim)
Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
lyakh
approved these changes
Jul 17, 2026
tmleman
requested review from
kv2019i,
lgirdwood,
serhiy-katsyuba-intel,
softwarecki and
wjablon1
July 17, 2026 08:29
serhiy-katsyuba-intel
approved these changes
Jul 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix a heap out-of-bounds access found by the IPC4 libFuzzer harness (native_sim/x86, ASAN-enabled build with CONFIG_SYS_HEAP_SANITIZER_ASAN). A single 173-byte crash input triggers a use-after-poison READ of size 4 in copier_dai_create():
copier_dai.c:284 reads cd->config.gtw_cfg.config_length, which lands 8 bytes past the end of the under-allocated cd block.
Fuzzer payload
The harness frames the raw input as a 2-byte little-endian length prefix followed by an 8-byte IPC4 compact header (primary + extension word) and a payload that is mirrored into MAILBOX_HOSTBOX. The decoded message is an INIT_MODULE_INSTANCE (MODULE_MSG / INIT_INSTANCE) whose module_id resolves to the copier driver, so comp_new_ipc4() -> module_adapter_new() -> copier_init() runs on host-controlled config bytes.
The copier's ipc4_copier_module_cfg carried (values from gdb):
node_id != IPC4_INVALID_NODE_ID selects the gateway-copier path, and the UAOL link class routes into copier_dai_create(). The long run of 0xff bytes at the tail of the input is what supplies config_length = 0xffffffff. libFuzzer discovered the message by mutating the corpus; the crucial field is the all-ones config_length.
Root cause
copier_init() sizes the variable-length gateway blob that trails the config struct:
config_length is a uint32_t from the untrusted payload. The expression (config_length - 1) << 2 is evaluated in 32-bit arithmetic and wraps: with 0xffffffff it yields 0xfffffff8, i.e. -8 as a size_t. That poisons both dependent computations:
copier_dai_create() then reads that field (and the config_data pointer right after it) out of bounds. size_t is 32-bit on native_sim and on the real Xtensa DSP targets, so this is a genuine firmware bug, not just a host-fuzzer artifact.
Fix
Validate config_length before any size arithmetic is performed on it. The blob is expressed in dwords and must fit within the received init payload, so an upper bound of md->cfg.size / sizeof(uint32_t) both is correct and keeps (config_length - 1) << 2 from overflowing (the mailbox payload is bounded by SOF_IPC_MSG_MAX_SIZE):
The existing precise "cfg_total_size > md->cfg.size" check is retained as a secondary guard for the non-overflowing mismatch case.
Verification
Found-by: IPC4 libFuzzer (ASAN, native_sim)