From cceaf11ad516434359beb4d72bc82c3a9535965e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 13:08:12 +0100 Subject: [PATCH] selector: re-validate configuration on control set A new configuration accepted at runtime through the control set path was copied in without the validation done when parameters are applied, so a later out-of-range channel count could overflow the channel table. Validate the blob size and channel fields before accepting it. Signed-off-by: Liam Girdwood --- src/audio/selector/selector.c | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/audio/selector/selector.c b/src/audio/selector/selector.c index 6afc92d8bdfc..cddc453abbeb 100644 --- a/src/audio/selector/selector.c +++ b/src/audio/selector/selector.c @@ -232,15 +232,75 @@ static int selector_ctrl_set_data(struct comp_dev *dev, { struct comp_data *cd = comp_get_drvdata(dev); struct sof_sel_config *cfg; + uint32_t live_channels; int ret = 0; switch (cdata->cmd) { case SOF_CTRL_CMD_BINARY: comp_dbg(dev, "SOF_CTRL_CMD_BINARY"); + if (cdata->data->size < sizeof(struct sof_sel_config)) { + comp_err(dev, "invalid config blob size %u", cdata->data->size); + return -EINVAL; + } + cfg = (struct sof_sel_config *) ASSUME_ALIGNED(&cdata->data->data, 4); + /* + * The config validated at .params() time can be replaced here at + * runtime, so re-validate the new channel counts and selected + * channel before accepting them; otherwise an out-of-range value + * later indexes past the source channels in the copy routine. + */ + switch (cfg->in_channels_count) { + case 0: + case SEL_SOURCE_2CH: + case SEL_SOURCE_4CH: + break; + default: + comp_err(dev, "invalid in_channels_count %u", + cfg->in_channels_count); + return -EINVAL; + } + + switch (cfg->out_channels_count) { + case 0: + case SEL_SINK_1CH: + case SEL_SINK_2CH: + case SEL_SINK_4CH: + break; + default: + comp_err(dev, "invalid out_channels_count %u", + cfg->out_channels_count); + return -EINVAL; + } + + /* sel_channel indexes the source channels, so it must be below + * the source channel count, otherwise the copy routine reads + * past the end of each source frame. Bound it by the maximum + * supported source channel count, and by the actual input count: + * the configured value when fixed (non-zero), or the live source + * stream channel count when the input count varies (zero) and the + * source is already connected. + */ + live_channels = cfg->in_channels_count; + if (!live_channels) { + struct comp_buffer *src = + comp_dev_get_first_data_producer(dev); + + if (src) + live_channels = + audio_stream_get_channels(&src->stream); + } + + if (cfg->sel_channel >= SEL_SOURCE_4CH || + (live_channels && cfg->sel_channel >= live_channels)) { + comp_err(dev, "invalid sel_channel %u (in_channels_count %u)", + cfg->sel_channel, cfg->in_channels_count); + return -EINVAL; + } + /* Just set the configuration */ cd->config.in_channels_count = cfg->in_channels_count; cd->config.out_channels_count = cfg->out_channels_count;