From 09556cc81fa89e85904b0302ba03cba03d53f694 Mon Sep 17 00:00:00 2001 From: Shrushti P K Date: Fri, 6 Nov 2026 16:14:01 +0530 Subject: [PATCH] audio: module_adapter: cap connected buffers at MODULE_MAX_CONNECTIONS struct processing_module holds CONFIG_MODULE_MAX_CONNECTIONS entries in sinks[] and sources[], but the module adapter stores one entry per connected buffer without bounding the index. On IPC4, module_adapter_update_sinks_sources() runs from module_adapter_bind(), and ipc4_comp_connect() connects a new buffer for every MOD_BIND, so a ninth bind on one side of a module writes sinks[8] over sources[0] and further binds run past the processing_module allocation. On IPC3, module_adapter_sink_src_prepare() does the same at prepare time. The connection count checks in module_adapter_prepare() only covered the audio_stream path removed in a42e9b7, and the max_sources and max_sinks fields are no longer read. Refuse a bind in module_adapter_bind() that brings the buffers connected on that side above CONFIG_MODULE_MAX_CONNECTIONS, and fail module_adapter_sink_src_prepare() with -EINVAL instead of writing past the arrays. A refusal on the sink side goes through the e_sink_bind path of ipc4_comp_connect(), which unbinds the source while the buffer is still connected. The source then refreshes its sinks[] with the buffer that is freed right after. Disconnect first, as ipc4_comp_disconnect() does. --- src/audio/module_adapter/README.md | 2 ++ .../module_adapter/module_adapter_ipc3.c | 10 ++++++++++ .../module_adapter/module_adapter_ipc4.c | 19 +++++++++++++++++++ src/ipc/ipc4/helper.c | 9 ++++++++- 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/audio/module_adapter/README.md b/src/audio/module_adapter/README.md index 9bc4f2b01695..f59ad2b7a41b 100644 --- a/src/audio/module_adapter/README.md +++ b/src/audio/module_adapter/README.md @@ -51,6 +51,8 @@ sequenceDiagram Connection establishes buffer relationships (`comp_buffer`) connecting the component sources and sinks to the rest of the generic pipeline graph. +Each connected buffer takes a slot in `mod->sinks[]` or `mod->sources[]`, which hold `CONFIG_MODULE_MAX_CONNECTIONS` entries each. A bind (IPC4) or prepare (IPC3) that would need more slots fails with `-EINVAL`. + ```mermaid sequenceDiagram participant Pipe as Pipeline diff --git a/src/audio/module_adapter/module_adapter_ipc3.c b/src/audio/module_adapter/module_adapter_ipc3.c index ab3a5db627c2..08625f38061f 100644 --- a/src/audio/module_adapter/module_adapter_ipc3.c +++ b/src/audio/module_adapter/module_adapter_ipc3.c @@ -320,6 +320,11 @@ int module_adapter_sink_src_prepare(struct comp_dev *dev) /* acquire all sink and source buffers, get handlers to sink/source API */ i = 0; comp_dev_for_each_consumer(dev, sink_buffer) { + if (i == CONFIG_MODULE_MAX_CONNECTIONS) { + comp_err(dev, "more than %d sinks connected", + CONFIG_MODULE_MAX_CONNECTIONS); + return -EINVAL; + } mod->sinks[i] = audio_buffer_get_sink(&sink_buffer->audio_buffer); i++; } @@ -327,6 +332,11 @@ int module_adapter_sink_src_prepare(struct comp_dev *dev) i = 0; comp_dev_for_each_producer(dev, source_buffer) { + if (i == CONFIG_MODULE_MAX_CONNECTIONS) { + comp_err(dev, "more than %d sources connected", + CONFIG_MODULE_MAX_CONNECTIONS); + return -EINVAL; + } mod->sources[i] = audio_buffer_get_source(&source_buffer->audio_buffer); i++; } diff --git a/src/audio/module_adapter/module_adapter_ipc4.c b/src/audio/module_adapter/module_adapter_ipc4.c index 6b66c89a8257..00eaa8380bc6 100644 --- a/src/audio/module_adapter/module_adapter_ipc4.c +++ b/src/audio/module_adapter/module_adapter_ipc4.c @@ -471,10 +471,29 @@ __cold static int module_update_source_buffer_params(struct processing_module *m __cold int module_adapter_bind(struct comp_dev *dev, const struct bind_info *bind_data) { struct processing_module *mod = comp_mod(dev); + struct comp_buffer *buffer; + unsigned int count = 0; int ret; assert_can_be_cold(); + /* + * The buffer is already connected here and every connected buffer + * takes a slot in mod->sinks[] or mod->sources[]. + */ + if (bind_data->bind_type == COMP_BIND_TYPE_SINK) { + comp_dev_for_each_consumer(dev, buffer) + count++; + } else { + comp_dev_for_each_producer(dev, buffer) + count++; + } + + if (count > CONFIG_MODULE_MAX_CONNECTIONS) { + comp_err(dev, "%u connections exceed max %d", count, CONFIG_MODULE_MAX_CONNECTIONS); + return -EINVAL; + } + ret = module_update_source_buffer_params(mod, bind_data); if (ret < 0) return ret; diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index d5a0e095b3f4..e5c61f6b3ea5 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -1058,10 +1058,17 @@ __cold int ipc4_comp_connect(struct ipc *ipc, const struct ipc4_module_bind_unbi return IPC4_SUCCESS; e_sink_bind: - /* sink bind was already called */ + /* + * The source is already bound. Disconnect first, as + * ipc4_comp_disconnect() does, so its unbind does not keep the + * buffer in the source's sinks[]. + */ + pipeline_disconnect(sink, buffer, PPL_CONN_DIR_BUFFER_TO_COMP); + pipeline_disconnect(source, buffer, PPL_CONN_DIR_COMP_TO_BUFFER); bind_data.bind_type = COMP_BIND_TYPE_SINK; bind_data.sink = audio_buffer_get_sink(&buffer->audio_buffer); comp_unbind(source, &bind_data); + goto free; e_src_bind: pipeline_disconnect(sink, buffer, PPL_CONN_DIR_BUFFER_TO_COMP); e_sink_connect: