From c648176619858b40f0cc4c74cf68afba1e414388 Mon Sep 17 00:00:00 2001 From: Shrushti P K Date: Fri, 16 Oct 2026 17:27:33 +0530 Subject: [PATCH] math: iir: reject series count that does not divide num_sections iir_delay_size_df1() is the only check an IIR response header gets before eq_iir, sound_dose and the tdfb direction emphasis filter size the delay lines (4 * num_sections words per channel) and point iir->coef at the blob biquads. It accepts any num_sections_in_series in [1, num_sections]. iir_df1() and its HiFi, ARM and RISC-V variants step the outer loop by num_sections_in_series until it reaches num_sections, so they run ceil(num_sections / nseries) * nseries biquads. When the series count does not divide num_sections the filter reads coefficients past the end of the blob and writes filter state past the delay line. Both values come from the bytes control blob. With num_sections = 11 and num_sections_in_series = 10, 20 biquads run: ASan reports a heap read past the 332 byte blob copy and a heap write past the 352 byte two channel delay allocation in iir_df1(). Require num_sections to be a multiple of num_sections_in_series. Do the same in iir_delay_size_df2t(), which has the identical check. The tuning scripts always set num_sections_in_series to num_sections, so generated blobs are not affected. Add a cmocka case for the validator. Signed-off-by: Shrushti P K --- src/math/iir_df1.c | 6 +++- src/math/iir_df2t.c | 6 +++- test/cmocka/src/audio/eq_iir/eq_iir_process.c | 28 ++++++++++++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/math/iir_df1.c b/src/math/iir_df1.c index 5e4d13b8cc70..6a2bde2f5ddd 100644 --- a/src/math/iir_df1.c +++ b/src/math/iir_df1.c @@ -21,8 +21,12 @@ int iir_delay_size_df1(struct sof_eq_iir_header *config) if (!n || n > SOF_EQ_IIR_BIQUADS_MAX) return -EINVAL; + /* The filter runs num_sections_in_series biquads per parallel + * branch, so the sections must split evenly into branches. + */ if (!config->num_sections_in_series || - config->num_sections_in_series > n) + config->num_sections_in_series > n || + n % config->num_sections_in_series) return -EINVAL; return 4 * n * sizeof(int32_t); diff --git a/src/math/iir_df2t.c b/src/math/iir_df2t.c index ed2b50a21a4e..8eac5c26aa07 100644 --- a/src/math/iir_df2t.c +++ b/src/math/iir_df2t.c @@ -21,8 +21,12 @@ int iir_delay_size_df2t(struct sof_eq_iir_header *config) if (!n || n > SOF_EQ_IIR_BIQUADS_MAX) return -EINVAL; + /* The filter runs num_sections_in_series biquads per parallel + * branch, so the sections must split evenly into branches. + */ if (!config->num_sections_in_series || - config->num_sections_in_series > n) + config->num_sections_in_series > n || + n % config->num_sections_in_series) return -EINVAL; return 2 * n * sizeof(int64_t); diff --git a/test/cmocka/src/audio/eq_iir/eq_iir_process.c b/test/cmocka/src/audio/eq_iir/eq_iir_process.c index f69eaa39c129..6b30a64c5217 100644 --- a/test/cmocka/src/audio/eq_iir/eq_iir_process.c +++ b/test/cmocka/src/audio/eq_iir/eq_iir_process.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "../../util.h" #include "../../../include/cmocka_chirp_2ch.h" @@ -517,11 +519,29 @@ static struct test_parameters parameters[] = { }; +static void test_iir_delay_size_df1_series(void **state) +{ + struct sof_eq_iir_header hdr = { .num_sections = 11 }; + const int size = 4 * 11 * sizeof(int32_t); + + (void)state; + + hdr.num_sections_in_series = 11; + assert_int_equal(iir_delay_size_df1(&hdr), size); + + hdr.num_sections_in_series = 1; + assert_int_equal(iir_delay_size_df1(&hdr), size); + + /* iir_df1() would run 20 biquads against 11 sections of state */ + hdr.num_sections_in_series = 10; + assert_int_equal(iir_delay_size_df1(&hdr), -EINVAL); +} + int main(void) { int i; - struct CMUnitTest tests[ARRAY_SIZE(parameters)]; + struct CMUnitTest tests[ARRAY_SIZE(parameters) + 1]; for (i = 0; i < ARRAY_SIZE(parameters); i++) { tests[i].name = "test_audio_eq_iir"; @@ -531,6 +551,12 @@ int main(void) tests[i].initial_state = ¶meters[i]; } + tests[i].name = "test_iir_delay_size_df1_series"; + tests[i].test_func = test_iir_delay_size_df1_series; + tests[i].setup_func = NULL; + tests[i].teardown_func = NULL; + tests[i].initial_state = NULL; + cmocka_set_message_output(CM_OUTPUT_TAP); return cmocka_run_group_tests(tests, setup_group, NULL);