audio: module_adapter: always set init_data in module_adapter_init_data#10966
Open
tmleman wants to merge 1 commit into
Open
audio: module_adapter: always set init_data in module_adapter_init_data#10966tmleman wants to merge 1 commit into
tmleman wants to merge 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request fixes a crash in the IPC4 module adapter initialization path where module_config.init_data could remain NULL when an extended-init payload included a MODULE_DATA typed object, causing legacy modules (e.g., volume) to dereference a null config pointer during module_init().
Changes:
- Always assign
dst->init_data = cfginmodule_adapter_init_data()so legacy modules always receive a valid config pointer. - Keep
dst->availconditional so extended-init-aware modules can continue to rely onext_data->module_datawithout changing behavior.
Fix a NULL pointer dereference found by the IPC4 libFuzzer harness
(native_sim/x86, ASAN-enabled build). The fuzzer discovered two
crash inputs (87 and 241 bytes) that trigger SEGV at address 0x28
in volume_init(), crashing on:
vol->config[0].channel_id == IPC4_ALL_CHANNELS_MASK
where vol (cfg->init_data) is NULL and offset 0x28 (40) matches
offsetof(ipc4_peak_volume_module_cfg, config[0].channel_id).
Root cause
----------
module_adapter_init_data() only assigned dst->init_data when the
legacy IPC path was active:
if (!config->ipc_extended_init || !dst->ext_data->module_data) {
dst->init_data = cfg;
dst->avail = true;
}
The fuzzer crafted a payload with the extended_init bit set in the
IPC4 extension word and a well-formed MODULE_DATA typed object in
the payload. This satisfied both conditions to skip the assignment,
leaving init_data as NULL (from struct zero-initialization). Any
subsequent module_init() that dereferences cfg->init_data without a
NULL check would crash. Affected modules include volume (confirmed
crash), selector, and mux (latent NULL derefs); only src_ipc4 and
smart_amp had pre-existing guards.
Fix
---
Move the init_data assignment outside the conditional so it is
always set:
dst->init_data = cfg;
if (!config->ipc_extended_init || !dst->ext_data->module_data)
dst->avail = true;
This is safe because module_ext_init_decode() strips the extended
init header and typed objects from the spec before
module_adapter_init_data() is called (lines 118-122 of the same
file advance spec->data past the consumed objects). Therefore cfg
always points to the same [base_module_cfg + module-specific data]
layout regardless of whether extended_init was set.
The avail flag remains conditional: extended-init-aware modules
(cadence_ipc4) set cfg.avail = false during their init and read
ext_data->module_data directly, never dereferencing init_data.
Alternatives considered
-----------------------
1. Adding a NULL check (if (!vol) return -EINVAL) in each affected
module (volume_init, selector_init, mux_init). Rejected because
it treats the symptom per-module rather than fixing the framework
invariant, and would need to be replicated in every future module
that reads init_data.
2. Rejecting extended_init messages for modules that do not support
it (capability flag per module). This would be architecturally
cleaner but requires a larger refactor and a new per-module
capability declaration. The current fix is pragmatic and safe
without that infrastructure.
Verification
------------
- Both crash artifacts execute cleanly (no SEGV, graceful -EINVAL).
- Smoke test (1500 fuzzer runs, 12 seconds) passes with normal
coverage growth and no new crashes.
- cadence_ipc4 (the only extended-init-aware module) is unaffected:
it reads ext_data->module_data directly and never uses init_data.
Found-by: IPC4 libFuzzer (ASAN, native_sim)
Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
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 NULL pointer dereference found by the IPC4 libFuzzer harness (native_sim/x86, ASAN-enabled build). The fuzzer discovered two crash inputs (87 and 241 bytes) that trigger SEGV at address 0x28 in volume_init(), crashing on:
where vol (cfg->init_data) is NULL and offset 0x28 (40) matches offsetof(ipc4_peak_volume_module_cfg, config[0].channel_id).
Root cause
module_adapter_init_data() only assigned dst->init_data when the legacy IPC path was active:
The fuzzer crafted a payload with the extended_init bit set in the IPC4 extension word and a well-formed MODULE_DATA typed object in the payload. This satisfied both conditions to skip the assignment, leaving init_data as NULL (from struct zero-initialization). Any subsequent module_init() that dereferences cfg->init_data without a NULL check would crash. Affected modules include volume (confirmed crash), selector, and mux (latent NULL derefs); only src_ipc4 and smart_amp had pre-existing guards.
Fix
Move the init_data assignment outside the conditional so it is always set:
This is safe because module_ext_init_decode() strips the extended init header and typed objects from the spec before module_adapter_init_data() is called (lines 118-122 of the same file advance spec->data past the consumed objects). Therefore cfg always points to the same [base_module_cfg + module-specific data] layout regardless of whether extended_init was set.
The avail flag remains conditional: extended-init-aware modules (cadence_ipc4) set cfg.avail = false during their init and read ext_data->module_data directly, never dereferencing init_data.
Alternatives considered
Adding a NULL check (if (!vol) return -EINVAL) in each affected module (volume_init, selector_init, mux_init). Rejected because it treats the symptom per-module rather than fixing the framework invariant, and would need to be replicated in every future module that reads init_data.
Rejecting extended_init messages for modules that do not support it (capability flag per module). This would be architecturally cleaner but requires a larger refactor and a new per-module capability declaration. The current fix is pragmatic and safe without that infrastructure.
Verification
Found-by: IPC4 libFuzzer (ASAN, native_sim)