From ac9f06c37d679d0225a7e0c2995dd4d28e5632fa 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 | 30 +++++++++++++++++-- 3 files changed, 38 insertions(+), 4 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..50f6aefe484d 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,32 @@ 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; + int i, ret; struct CMUnitTest tests[ARRAY_SIZE(parameters)]; + const struct CMUnitTest validator_tests[] = { + cmocka_unit_test(test_iir_delay_size_df1_series), + }; for (i = 0; i < ARRAY_SIZE(parameters); i++) { tests[i].name = "test_audio_eq_iir"; @@ -533,5 +556,8 @@ int main(void) cmocka_set_message_output(CM_OUTPUT_TAP); - return cmocka_run_group_tests(tests, setup_group, NULL); + ret = cmocka_run_group_tests(tests, setup_group, NULL); + ret += cmocka_run_group_tests(validator_tests, NULL, NULL); + + return ret; }