Skip to content

Commit 1e12949

Browse files
committed
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 <liam.r.girdwood@linux.intel.com>
1 parent 3f7738d commit 1e12949

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

src/audio/selector/selector.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,16 +231,85 @@ static int selector_ctrl_set_data(struct comp_dev *dev,
231231
struct sof_ipc_ctrl_data *cdata)
232232
{
233233
struct comp_data *cd = comp_get_drvdata(dev);
234+
struct comp_buffer *src;
234235
struct sof_sel_config *cfg;
236+
uint32_t src_channels;
235237
int ret = 0;
236238

237239
switch (cdata->cmd) {
238240
case SOF_CTRL_CMD_BINARY:
239241
comp_dbg(dev, "SOF_CTRL_CMD_BINARY");
240242

243+
if (cdata->data->size < sizeof(struct sof_sel_config)) {
244+
comp_err(dev, "invalid config blob size %u", cdata->data->size);
245+
return -EINVAL;
246+
}
247+
241248
cfg = (struct sof_sel_config *)
242249
ASSUME_ALIGNED(&cdata->data->data, 4);
243250

251+
/*
252+
* The config validated at .params() time can be replaced here at
253+
* runtime, so re-validate the new channel counts and selected
254+
* channel before accepting them; otherwise an out-of-range value
255+
* later indexes past the source channels in the copy routine.
256+
*/
257+
switch (cfg->in_channels_count) {
258+
case 0:
259+
case SEL_SOURCE_2CH:
260+
case SEL_SOURCE_4CH:
261+
break;
262+
default:
263+
comp_err(dev, "invalid in_channels_count %u",
264+
cfg->in_channels_count);
265+
return -EINVAL;
266+
}
267+
268+
switch (cfg->out_channels_count) {
269+
case 0:
270+
case SEL_SINK_1CH:
271+
case SEL_SINK_2CH:
272+
case SEL_SINK_4CH:
273+
break;
274+
default:
275+
comp_err(dev, "invalid out_channels_count %u",
276+
cfg->out_channels_count);
277+
return -EINVAL;
278+
}
279+
280+
/* sel_channel indexes the source channels, so it must be below
281+
* the source channel count, otherwise the copy routine reads
282+
* past the end of each source frame. The copy routine strides by
283+
* the live producer stream channel count, so when the source is
284+
* already connected that live count is the authority for both the
285+
* selected channel and any fixed input count; before connection
286+
* fall back to the configured input count. Always cap by the
287+
* maximum supported source width.
288+
*/
289+
src = comp_dev_get_first_data_producer(dev);
290+
if (src)
291+
src_channels = audio_stream_get_channels(&src->stream);
292+
else
293+
src_channels = cfg->in_channels_count;
294+
295+
/* A fixed (non-zero) input count must not exceed the live source
296+
* width, otherwise sel_channel could be accepted below the
297+
* requested count yet still index past the actual stream.
298+
*/
299+
if (src && cfg->in_channels_count &&
300+
cfg->in_channels_count > src_channels) {
301+
comp_err(dev, "in_channels_count %u exceeds source channels %u",
302+
cfg->in_channels_count, src_channels);
303+
return -EINVAL;
304+
}
305+
306+
if (cfg->sel_channel >= SEL_SOURCE_4CH ||
307+
(src_channels && cfg->sel_channel >= src_channels)) {
308+
comp_err(dev, "invalid sel_channel %u (source channels %u)",
309+
cfg->sel_channel, src_channels);
310+
return -EINVAL;
311+
}
312+
244313
/* Just set the configuration */
245314
cd->config.in_channels_count = cfg->in_channels_count;
246315
cd->config.out_channels_count = cfg->out_channels_count;

0 commit comments

Comments
 (0)