From dbdd50398f0a0eeebd1c2ebd9d6d7fe8da8eaac8 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Mon, 15 Jun 2026 14:29:30 +0300 Subject: [PATCH] math: auditory: guard mod_psy_get_mel_filterbank() against zero divisors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mel-bin loop in mod_psy_get_mel_filterbank() divides by delta_cl and delta_rc on every FFT bin, and by (right_hz - left_hz) when slaney normalization is enabled. With unusual configurations these denominators can become zero or negative — for example when start_freq >= end_freq, when the requested mel_bins is large enough that integer division truncates the mel step to zero, or at extreme inputs where psy_mel_to_hz saturates and right_hz no longer exceeds left_hz. Reject such configurations explicitly. After computing mel_step, return -EINVAL if it is not positive, which also covers both delta values since they equal mel_step. In the slaney_normalize branch, also verify right_hz > left_hz before computing the scale factor. Signed-off-by: Seppo Ingalsuo --- src/include/sof/math/auditory.h | 1 + src/math/auditory/auditory.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/include/sof/math/auditory.h b/src/include/sof/math/auditory.h index bd707dc5079a..a68f0468d591 100644 --- a/src/include/sof/math/auditory.h +++ b/src/include/sof/math/auditory.h @@ -14,6 +14,7 @@ #include #include +#define AUDITORY_MAX_MEL_BANDS 256 #define AUDITORY_EPS_Q31 1 /* Smallest nonzero Q1.31 value */ #define AUDITORY_LOG2_2P25_Q16 Q_CONVERT_FLOAT(25.0, 16) /* log2(2^25) */ diff --git a/src/math/auditory/auditory.c b/src/math/auditory/auditory.c index 57641c46aa5a..0abd10de9308 100644 --- a/src/math/auditory/auditory.c +++ b/src/math/auditory/auditory.c @@ -12,10 +12,13 @@ #include #include #include +#include #include #include #include +LOG_MODULE_REGISTER(math_auditory, CONFIG_SOF_LOG_LEVEL); + #define ONE_Q16 Q_CONVERT_FLOAT(1, 16) #define ONE_Q20 Q_CONVERT_FLOAT(1, 20) #define ONE_Q30 Q_CONVERT_FLOAT(1, 30) @@ -117,6 +120,16 @@ int mod_psy_get_mel_filterbank(struct processing_module *mod, struct psy_mel_fil if (!fb->scratch_data1 || !fb->scratch_data2) return -ENOMEM; + /* fb->mel_bins is used both as the loop bound and as part of the + * (mel_bins + 1) divisor below. Reject non-positive values up front to + * avoid a divide by zero (mel_bins == -1) and to keep mel_step + * meaningful. + */ + if (fb->mel_bins <= 0 || fb->mel_bins > AUDITORY_MAX_MEL_BANDS) { + comp_cl_err(mod->dev, "Invalid mel_bins %d", fb->mel_bins); + return -EINVAL; + } + /* Log power can be log, or log10 or dB, get multiply coef to convert * log to desired format. */ @@ -149,6 +162,17 @@ int mod_psy_get_mel_filterbank(struct processing_module *mod, struct psy_mel_fil mel_start = psy_hz_to_mel(fb->start_freq); mel_end = psy_hz_to_mel(fb->end_freq); mel_step = (mel_end - mel_start) / (fb->mel_bins + 1); + /* delta_cl / delta_rc below are both equal to mel_step; guard against + * a non-positive step (start_freq >= end_freq, or so many bins that + * the integer division truncates to zero) before using them as + * divisors. + */ + if (mel_step <= 0) { + comp_cl_err(mod->dev, "Invalid mel_step %d (start_freq=%d end_freq=%d mel_bins=%d)", + mel_step, fb->start_freq, fb->end_freq, fb->mel_bins); + return -EINVAL; + } + for (i = 0; i < fb->mel_bins; i++) { left_mel = mel_start + i * mel_step; center_mel = mel_start + (i + 1) * mel_step; @@ -160,6 +184,11 @@ int mod_psy_get_mel_filterbank(struct processing_module *mod, struct psy_mel_fil if (fb->slaney_normalize) { left_hz = psy_mel_to_hz(left_mel); right_hz = psy_mel_to_hz(right_mel); + if (right_hz <= left_hz) { + comp_cl_err(mod->dev, "Invalid Hz range left=%d right=%d at mel bin %d", + left_hz, right_hz, i); + return -EINVAL; + } scale = Q_SHIFT_RND(TWO_Q29 / (right_hz - left_hz), 29, 16); /* Q16.16*/ if (i == 0) { scale_inv = Q_SHIFT_LEFT(ONE_Q30 / scale, 14, 16);