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 d1760386c098614d932ab3d729afded831aadda6 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 | 18 +++++++++++++++--- 1 file changed, 15 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..72f81d0e5117 100644 --- a/src/audio/smart_amp/smart_amp_generic.c +++ b/src/audio/smart_amp/smart_amp_generic.c @@ -35,7 +35,11 @@ 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; + * the uint8_t cast folds the negative case into the + * single upper-bound check (-1 becomes 255 >= src_ch) + */ + if ((uint8_t)chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; @@ -103,7 +107,11 @@ 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; + * the uint8_t cast folds the negative case into the + * single upper-bound check (-1 becomes 255 >= src_ch) + */ + if ((uint8_t)chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; @@ -147,7 +155,11 @@ 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; + * the uint8_t cast folds the negative case into the + * single upper-bound check (-1 becomes 255 >= src_ch) + */ + if ((uint8_t)chan_map[ch] >= src_ch) continue; mod_ptr = mod_ptr_base + ch; From 7b958dd960e396357638d29539ea9611fd6f1914 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 | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/audio/smart_amp/smart_amp_maxim_dsm.c b/src/audio/smart_amp/smart_amp_maxim_dsm.c index b9289861aa8d..b06e6cd2f30f 100644 --- a/src/audio/smart_amp/smart_amp_maxim_dsm.c +++ b/src/audio/smart_amp/smart_amp_maxim_dsm.c @@ -321,7 +321,18 @@ static int maxim_dsm_get_param(struct smart_amp_mod_struct_t *hspk, * required size */ if (bs > size) { - comp_err(dev, "[DSM] invalid size %d", bs); + comp_err(dev, "[DSM] invalid size %zu", bs); + 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; } From d46d1d1d839b3a8a7b5870d097e8b51988c6c5bc 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..38577545b36e 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. memcpy_s() below already bounds it against the destination. + */ + if (bs == 0 || bs > sizeof(struct sof_smart_amp_config)) return -EINVAL; ret = memcpy_s(cdata->data->data, size, &sad->config, bs); From fbcdc3b2974d2901d6814351782cfce9ab542436 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);