From aca3c55272bc319f276ce4a78317c05a36e3b5ed Mon Sep 17 00:00:00 2001 From: Tomasz Leman Date: Mon, 8 Jun 2026 15:23:11 +0200 Subject: [PATCH 1/3] audio: kpb: cast operands to size_t before width-widening multiplies Six multiplications in the key-phrase buffer compute their product at 32-bit width and only then assign it to a wider size_t result. If the operands are large the overflow has already occurred before widening. The KPB sizing math is partly driven by externally-influenced values (cli->drain_req, the configured channel count, sampling frequency and container width), so this is a real overflow surface rather than a purely theoretical one. Cast the leading operand to size_t in each expression so the whole product is evaluated at the destination width: - kpb_micselect_copy16/32(): loop bound samples_per_chan * in_channels - kpb_init_draining(): drain_req and bytes_per_ms - adjust_drain_interval(): pipeline_period - validate_host_params(): bytes_per_ms No functional change on in-range inputs; only the intermediate arithmetic width changes. Found-by: CodeQL 2.24.2 (codeql/cpp-queries cpp-security-extended), rule cpp/integer-multiplication-cast-to-long. Run with database build-mode=none over sof/src (host clang cannot target the Xtensa production build), 867 files / 98 queries. Findings at kpb.c:1117,1148,1610,1619,1791,2397. AI-triaged: findings manually cross-referenced against clang-tidy bugprone-implicit-widening-of-multiplication-result and semgrep raptor-integer-truncation on the same surface, and confirmed the operand types (uint32_t / macro constants) against struct sof_kpb_config and struct kpb_client before fixing. Signed-off-by: Tomasz Leman --- src/audio/kpb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/audio/kpb.c b/src/audio/kpb.c index 9dca05630da1..1591c21885b5 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -1107,7 +1107,7 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, const int16_t *in_data; int16_t *out_data; - const uint32_t samples_per_chan = size / (sizeof(uint16_t) * micsel_channels); + const size_t samples_per_chan = size / (sizeof(uint16_t) * micsel_channels); for (ch = 0; ch < micsel_channels; ch++) { out_samples = 0; @@ -1138,7 +1138,7 @@ static void kpb_micselect_copy32(struct comp_buffer *sink, uint16_t ch; const int32_t *in_data; int32_t *out_data; - const uint32_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); + const size_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); for (ch = 0; ch < micsel_channels; ch++) { out_samples = 0; @@ -1607,7 +1607,7 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) struct comp_data *kpb = comp_get_drvdata(dev); bool is_sink_ready = (comp_buffer_get_sink_state(kpb->host_sink) == COMP_STATE_ACTIVE); size_t sample_width = kpb->config.sampling_width; - size_t drain_req = cli->drain_req * kpb->config.channels * + size_t drain_req = (size_t)cli->drain_req * kpb->config.channels * (kpb->config.sampling_freq / 1000) * (KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8); struct history_buffer *buff = kpb->hd.c_hb; @@ -1616,7 +1616,7 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) size_t local_buffered; size_t drain_interval; size_t host_period_size = kpb->host_period_size; - size_t bytes_per_ms = KPB_SAMPLES_PER_MS * + size_t bytes_per_ms = (size_t)KPB_SAMPLES_PER_MS * (KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8) * kpb->config.channels; size_t period_bytes_limit; @@ -1788,7 +1788,7 @@ static void adjust_drain_interval(struct comp_data *kpb, struct draining_data *d /* average drained bytes per second */ actual_pace = (size_t)k_ms_to_cyc_ceil64(1000) / elapsed * drained; - pipeline_period = KPB_SAMPLES_PER_MS * + pipeline_period = (size_t)KPB_SAMPLES_PER_MS * (KPB_SAMPLE_CONTAINER_SIZE(dd->sample_width) / 8) * kpb->config.channels; /* desired draining pace in bytes per second */ optimal_pace = pipeline_period * KPB_DRAIN_NUM_OF_PPL_PERIODS_AT_ONCE * 1000; @@ -2394,7 +2394,7 @@ static inline bool validate_host_params(struct comp_dev *dev, */ struct comp_data *kpb = comp_get_drvdata(dev); size_t sample_width = kpb->config.sampling_width; - size_t bytes_per_ms = KPB_SAMPLES_PER_MS * + size_t bytes_per_ms = (size_t)KPB_SAMPLES_PER_MS * (KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8) * kpb->config.channels; size_t pipeline_period_size = (dev->pipeline->period / 1000) From a46760296c92241caf2fdc3a1cdecd73bb9a1362 Mon Sep 17 00:00:00 2001 From: Tomasz Leman Date: Mon, 8 Jun 2026 15:31:54 +0200 Subject: [PATCH 2/3] audio: kpb: widen channel loop counter to match micsel_channels The four kpb_micselect_copy16/32() variants declare the channel loop counter "ch" as uint16_t while iterating "for (ch = 0; ch < ... micsel_channels; ch++)", where micsel_channels is uint32_t. The loop condition promotes ch to a wider type for the comparison, so if micsel_channels ever exceeds UINT16_MAX the counter wraps at 65536 and the loop fails to terminate. The same narrow counter would also truncate channel indexing. Declare ch as uint32_t so its width matches the bound it is compared against and the channel count it indexes. KPB_MAX_MICSEL_CHANNELS keeps the real value small, so this is hardening rather than an observed runaway, but the type mismatch is removed at the source. CodeQL flagged the two non-HiFi variants (kpb.c:1112,1143); the two KPB_HIFI3 variants (kpb.c:1050,1084) carry the identical pattern and are fixed in the same change for consistency. Found-by: CodeQL 2.24.2 (codeql/cpp-queries cpp-security-extended), rule cpp/comparison-with-wider-type. Run with database build-mode=none over sof/src, 867 files / 98 queries. AI-triaged: traced ch and micsel_channels declarations across all four micselect copy functions and confirmed the bound is uint32_t before widening the counter; extended the fix to the HiFi3 variants the tool did not reach in this build configuration. Signed-off-by: Tomasz Leman --- src/audio/kpb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/audio/kpb.c b/src/audio/kpb.c index 1591c21885b5..696ee0e1042d 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -1033,7 +1033,7 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - uint16_t ch; + uint32_t ch; size_t i; AE_SETCBEGIN0(audio_stream_get_addr(ostream)); @@ -1066,7 +1066,7 @@ static void kpb_micselect_copy32(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - uint16_t ch; + uint32_t ch; size_t i; AE_SETCBEGIN0(audio_stream_get_addr(ostream)); @@ -1103,7 +1103,7 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, buffer_stream_invalidate(source, size); size_t out_samples; - uint16_t ch; + uint32_t ch; const int16_t *in_data; int16_t *out_data; @@ -1135,7 +1135,7 @@ static void kpb_micselect_copy32(struct comp_buffer *sink, buffer_stream_invalidate(source, size); size_t out_samples; - uint16_t ch; + uint32_t ch; const int32_t *in_data; int32_t *out_data; const size_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); From 32254a8f6b2bab11bb019a53756d87c532030368 Mon Sep 17 00:00:00 2001 From: Tomasz Leman Date: Tue, 9 Jun 2026 17:08:52 +0200 Subject: [PATCH 3/3] audio: kpb: fix buffer_stream_invalidate size in micselect copy helpers In all four kpb_micselect_copy16/32 variants (HiFi3 and generic), the call to buffer_stream_invalidate(source, size) used the output byte count (produced_bytes) as the invalidation size. When in_channels > micsel_channels, the actual input span read by the copy loop is: samples_per_chan * in_channels * sample_size which is larger than the output span (size). Under-invalidating the source buffer on CONFIG_INCOHERENT platforms can cause stale cache reads. Fix: compute samples_per_chan before the invalidate call and pass the correct input span to buffer_stream_invalidate() in all four helpers. Signed-off-by: Tomasz Leman --- src/audio/kpb.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/audio/kpb.c b/src/audio/kpb.c index 696ee0e1042d..71fdca29e73e 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -1039,12 +1039,13 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, AE_SETCBEGIN0(audio_stream_get_addr(ostream)); AE_SETCEND0(audio_stream_get_end_addr(ostream)); - buffer_stream_invalidate(source, size); + const size_t samples_per_chan = size / (sizeof(ae_int16) * micsel_channels); + + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(ae_int16)); const ae_int16 *in_ptr = audio_stream_get_rptr(istream); ae_int16x4 d16 = AE_ZERO16(); const size_t in_offset = in_channels * sizeof(ae_int16); const size_t out_offset = micsel_channels * sizeof(ae_int16); - const size_t samples_per_chan = size / (sizeof(uint16_t) * micsel_channels); ae_int16 *out_ptr; for (ch = 0; ch < micsel_channels; ch++) { @@ -1072,13 +1073,14 @@ static void kpb_micselect_copy32(struct comp_buffer *sink, AE_SETCBEGIN0(audio_stream_get_addr(ostream)); AE_SETCEND0(audio_stream_get_end_addr(ostream)); - buffer_stream_invalidate(source, size); + const size_t samples_per_chan = size / (sizeof(ae_int32) * micsel_channels); + + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(ae_int32)); const ae_int32 *in_ptr = audio_stream_get_rptr(istream); ae_int32x2 d32 = AE_ZERO32(); const size_t in_offset = in_channels * sizeof(ae_int32); const size_t out_offset = micsel_channels * sizeof(ae_int32); - const size_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); ae_int32 *out_ptr; for (ch = 0; ch < micsel_channels; ch++) { @@ -1100,8 +1102,6 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - - buffer_stream_invalidate(source, size); size_t out_samples; uint32_t ch; @@ -1109,6 +1109,8 @@ static void kpb_micselect_copy16(struct comp_buffer *sink, int16_t *out_data; const size_t samples_per_chan = size / (sizeof(uint16_t) * micsel_channels); + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(uint16_t)); + for (ch = 0; ch < micsel_channels; ch++) { out_samples = 0; in_data = audio_stream_get_rptr(istream); @@ -1132,14 +1134,14 @@ static void kpb_micselect_copy32(struct comp_buffer *sink, { struct audio_stream *istream = &source->stream; struct audio_stream *ostream = &sink->stream; - - buffer_stream_invalidate(source, size); size_t out_samples; uint32_t ch; const int32_t *in_data; int32_t *out_data; const size_t samples_per_chan = size / (sizeof(uint32_t) * micsel_channels); + buffer_stream_invalidate(source, samples_per_chan * in_channels * sizeof(uint32_t)); + for (ch = 0; ch < micsel_channels; ch++) { out_samples = 0; in_data = audio_stream_get_rptr(istream);