From 3424122838ce6a5438626bbe546aea3f981a2ca6 Mon Sep 17 00:00:00 2001 From: Tomasz Leman Date: Fri, 3 Jul 2026 14:31:35 +0200 Subject: [PATCH] ipc4: helper: reject module creation for non-existent pipeline comp_new_ipc4() copied the host-supplied ppl_instance_id into ipc_config.pipeline_id without verifying that the referenced pipeline exists. The module_adapter creation path then does: ipc_pipe = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, config->pipeline_id, IPC_COMP_IGNORE_REMOTE); if (ipc_pipe) dev->pipeline = ipc_pipe->pipeline; /* skipped when NULL */ so when the pipeline is missing the lookup returns NULL, the assignment is skipped, and the component is created with dev->pipeline left unset. A gateway module such as the copier then dereferences that NULL pointer while wiring the graph, e.g. copier_dai_init() writing pipeline->source_comp / pipeline->sink_comp, giving a NULL-pointer SEGV reachable from a single unprivileged IPC. The case -------- The offending IPC stream carries one meaningful command: a MODULE / INIT_INSTANCE request for a copier-class module (module_id 26, instance_id 36) whose extension.ppl_instance_id names a parent pipeline (id 90) for which no GLB / CREATE_PIPELINE command was ever sent. That pipeline is therefore absent from ipc->comp_list. IPC4 mandates that the host create the pipeline before instantiating any module inside it; here that ordering is violated, so the invariant "an INIT_INSTANCE always targets an existing pipeline" does not hold. The firmware accepted the request anyway, allocated the component, ran module_init() -> copier_init() -> copier_dai_create(), and copier_dai_init() performed a WRITE to the source_comp field (offset 0x54) through the NULL dev->pipeline, faulting on the zero page: ERROR: SEGV on unknown address 0x00000054 (WRITE) copier_dai_init -> copier_dai_create -> copier_init -> module_init -> module_adapter_new_ext -> module_adapter_new -> comp_new_ipc4 -> ipc4_init_module_instance -> ipc4_process_module_message -> ipc_cmd All three copier gateway variants are affected (copier_host_create, copier_dai_create and copier_ipcgtw_create all pass dev->pipeline down), and any future module reading dev->pipeline during init would hit the same latent NULL. Fix --- Rather than guarding the individual copier/module_adapter dereference, reject the request at the common creation entry point: in comp_new_ipc4(), before the component is allocated, look up the parent pipeline and fail with a NULL return (reported to the host as IPC4_MOD_NOT_INITIALIZED) when it does not exist. This fails fast with no partially-initialized component to unwind and enforces the invariant "a live IPC4 component always belongs to a live pipeline" for every module driver, not just the module adapter; the later module_adapter lookup is then guaranteed to succeed. Signed-off-by: Tomasz Leman --- src/ipc/ipc4/helper.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 5881fbbfb0b5..d341200a2d2b 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -145,6 +145,23 @@ __cold struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_i ipc_config.ipc_config_size); return NULL; } + + /* + * The parent pipeline referenced by ppl_instance_id must already exist. + * IPC4 requires the host to create the pipeline (SOF_IPC4_GLB_CREATE_PIPELINE) + * before instantiating any module inside it. Validate the reference here, at + * the common creation entry point, before allocating the component. This + * guarantees the "a live IPC4 component always belongs to a live pipeline" + * invariant for every driver, so no module init path (e.g. the copier writing + * pipeline->source_comp / pipeline->sink_comp) can dereference a NULL + * dev->pipeline. + */ + if (!ipc_get_comp_by_ppl_id(ipc_get(), COMP_TYPE_PIPELINE, ipc_config.pipeline_id, + IPC_COMP_IGNORE_REMOTE)) { + tr_err(&ipc_tr, "comp 0x%x: parent pipeline %u does not exist", comp_id, + (uint32_t)ipc_config.pipeline_id); + return NULL; + } #ifdef CONFIG_DCACHE_LINE_SIZE if (!IS_ENABLED(CONFIG_LIBRARY)) sys_cache_data_invd_range((__sparse_force void __sparse_cache *)