From 37bb5d5fcb03f976017da3a26370d687177e591a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 13:07:36 +0100 Subject: [PATCH 1/5] smart_amp: bound processed sample count by channel count The feed-forward and feedback paths copy frames * channels samples into fixed intermediate buffers but only checked the frame count. Bound the total sample count against the buffer capacity so an unexpected channel count cannot overflow the buffers. Signed-off-by: Liam Girdwood --- src/audio/smart_amp/smart_amp.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/audio/smart_amp/smart_amp.c b/src/audio/smart_amp/smart_amp.c index f82e3d2f7360..665aec5d27a5 100644 --- a/src/audio/smart_amp/smart_amp.c +++ b/src/audio/smart_amp/smart_amp.c @@ -521,8 +521,15 @@ static int smart_amp_ff_process(struct processing_module *mod, return 0; } - if (frames > SMART_AMP_FF_BUF_DB_SZ) { - comp_err(dev, "feed forward frame size overflow: %u", frames); + /* + * The remap functions write frames * ff_mod.channels samples into + * ff_mod.buf, which holds SMART_AMP_FF_BUF_DB_SZ samples. Bound the + * total sample count, not just the frame count, so an unexpected + * channel count cannot overflow the buffer. + */ + if ((uint64_t)frames * sad->ff_mod.channels > SMART_AMP_FF_BUF_DB_SZ) { + comp_err(dev, "feed forward frame size overflow: %u frames, %u ch", + frames, sad->ff_mod.channels); sad->ff_mod.consumed = frames; return -EINVAL; } @@ -556,8 +563,10 @@ static int smart_amp_fb_process(struct processing_module *mod, return 0; } - if (frames > SMART_AMP_FB_BUF_DB_SZ) { - comp_err(dev, "feedback frame size overflow: %u", frames); + /* bound total samples (frames * channels) against the buffer size */ + if ((uint64_t)frames * sad->fb_mod.channels > SMART_AMP_FB_BUF_DB_SZ) { + comp_err(dev, "feedback frame size overflow: %u frames, %u ch", + frames, sad->fb_mod.channels); sad->fb_mod.consumed = frames; return -EINVAL; } From d55f045418aff1fc527a8e6f47115fe7d5c4393f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 13:07:36 +0100 Subject: [PATCH 2/5] smart_amp: reject out-of-range source channel map entries The remap routines used host-provided channel map entries to index the source frame, guarding only against the -1 unmapped sentinel. Skip any entry outside [0, source channels) so a crafted map cannot read past the source frame. Signed-off-by: Liam Girdwood --- src/audio/smart_amp/smart_amp_generic.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/audio/smart_amp/smart_amp_generic.c b/src/audio/smart_amp/smart_amp_generic.c index 91223eb398bd..ffee36f5d5e7 100644 --- a/src/audio/smart_amp/smart_amp_generic.c +++ b/src/audio/smart_amp/smart_amp_generic.c @@ -35,7 +35,8 @@ static void remap_s32_to_s32(struct smart_amp_mod_stream *src_mod, uint32_t fram n = MIN(num_samples_remaining, nmax); for (ch = 0; ch < src_mod->channels; ch++) { - if (chan_map[ch] == -1) + /* skip unmapped (-1) and out-of-range source channels */ + if (chan_map[ch] < 0 || chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; @@ -103,7 +104,8 @@ static void remap_s16_to_s16(struct smart_amp_mod_stream *src_mod, uint32_t fram n = MIN(num_samples_remaining, nmax); for (ch = 0; ch < src_mod->channels; ch++) { - if (chan_map[ch] == -1) + /* skip unmapped (-1) and out-of-range source channels */ + if (chan_map[ch] < 0 || chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; @@ -147,7 +149,8 @@ static void remap_s16_to_b32(struct smart_amp_mod_stream *src_mod, uint32_t fram n = MIN(num_samples_remaining, nmax); for (ch = 0; ch < src_mod->channels; ch++) { - if (chan_map[ch] == -1) + /* skip unmapped (-1) and out-of-range source channels */ + if (chan_map[ch] < 0 || chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; From a58f562543e15c0a09ec43310b7b5fae337e54f8 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 13:07:36 +0100 Subject: [PATCH 3/5] smart_amp: bound calibration read offset to data size The fragmented calibration get path advanced a read offset by a host-controlled index without checking it against the calibration data size, allowing reads past the buffer. Reject offsets at or beyond the data size and fragments that would extend past it. Signed-off-by: Liam Girdwood --- src/audio/smart_amp/smart_amp_maxim_dsm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/audio/smart_amp/smart_amp_maxim_dsm.c b/src/audio/smart_amp/smart_amp_maxim_dsm.c index b9289861aa8d..2a74e16fe2b2 100644 --- a/src/audio/smart_amp/smart_amp_maxim_dsm.c +++ b/src/audio/smart_amp/smart_amp_maxim_dsm.c @@ -325,6 +325,17 @@ static int maxim_dsm_get_param(struct smart_amp_mod_struct_t *hspk, return -EINVAL; } + /* Host controls msg_index and therefore data_pos; make sure the + * fragment stays inside caldata->data to avoid leaking adjacent + * heap back to the host. + */ + if (caldata->data_pos >= caldata->data_size || + bs > caldata->data_size - caldata->data_pos) { + comp_err(dev, "[DSM] invalid data_pos %u, size %zu, total %u", + caldata->data_pos, bs, caldata->data_size); + return -EINVAL; + } + /* copy required size of data */ ret = memcpy_s(cdata->data->data, size, (char *)caldata->data + caldata->data_pos, From 5d318d91684ad73105747c8d928571d82928a578 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 14:30:37 +0100 Subject: [PATCH 4/5] smart_amp: bound get-config copy by config struct size The config read-back used the stored config size as the memcpy source length from a fixed-size struct; a host-set oversized size read adjacent heap. Bound the length by the struct size as well as the destination. Signed-off-by: Liam Girdwood --- src/audio/smart_amp/smart_amp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/audio/smart_amp/smart_amp.c b/src/audio/smart_amp/smart_amp.c index 665aec5d27a5..373ad2ce84a8 100644 --- a/src/audio/smart_amp/smart_amp.c +++ b/src/audio/smart_amp/smart_amp.c @@ -289,7 +289,11 @@ static int smart_amp_get_config(struct processing_module *mod, comp_dbg(dev, "actual blob size = %zu, expected blob size = %zu", bs, sizeof(struct sof_smart_amp_config)); - if (bs == 0 || bs > size) + /* bs is the host-set config.size and is used as the memcpy source + * length from the fixed-size sad->config, so bound it by the struct + * size as well as the destination buffer + */ + if (bs == 0 || bs > size || bs > sizeof(struct sof_smart_amp_config)) return -EINVAL; ret = memcpy_s(cdata->data->data, size, &sad->config, bs); From 85ae4cbd02fb25bad44b1974bb63298fde66b88a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 14:31:24 +0100 Subject: [PATCH 5/5] smart_amp_test: bound channel counts to platform max The sample component took its channel counts from the streams and used them to index the platform-sized channel map without bounds. Reject counts above the platform maximum. Signed-off-by: Liam Girdwood --- src/samples/audio/smart_amp_test_ipc3.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/samples/audio/smart_amp_test_ipc3.c b/src/samples/audio/smart_amp_test_ipc3.c index 3e69bb5f308e..a3dc939a509a 100644 --- a/src/samples/audio/smart_amp_test_ipc3.c +++ b/src/samples/audio/smart_amp_test_ipc3.c @@ -518,6 +518,16 @@ static int smart_amp_prepare(struct comp_dev *dev) sad->in_channels = audio_stream_get_channels(&sad->source_buf->stream); + /* out_channels bounds the processing loop that indexes the + * PLATFORM_MAX_CHANNELS-sized channel map, so reject a larger count + */ + if (sad->out_channels > PLATFORM_MAX_CHANNELS || + sad->in_channels > PLATFORM_MAX_CHANNELS) { + comp_err(dev, "invalid channel count, in %u out %u", + sad->in_channels, sad->out_channels); + return -EINVAL; + } + if (sad->feedback_buf) { audio_stream_set_channels(&sad->feedback_buf->stream, sad->config.feedback_channels);