whisper: validate mel filter dimensions to prevent integer overflow

The mel filter loading code in whisper_model_load() multiplies
filters.n_mel * filters.n_fft (both int32_t) without bounds checking
or overflow protection before passing the result to filters.data.resize().

When the model file declares dimensions whose product exceeds INT32_MAX
(e.g. n_mel=65537, n_fft=65536), the multiplication overflows int32_t
to a small value, causing an undersized heap allocation. Subsequent
reads in log_mel_spectrogram_worker_thread() then access filter data
out of bounds (heap-buffer-overflow READ).

This commit:

- Adds explicit bounds validation for n_mel and n_fft against
  reasonable upper limits (1024 and WHISPER_N_FFT respectively)
- Adds consistency check between filters.n_mel and hparams.n_mels,
  promoting an existing downstream assert to upstream validation
- Uses size_t for the allocation size computation to avoid the
  int32_t overflow even within the validated bounds

The pre-existing assert at line ~3114 (n_fft == 1 + (frame_size / 2))
is a no-op in release builds (-DNDEBUG), so it does not protect
against this in production deployments.

Signed-off-by: Sebastian Alba <Sebasjosue84@gmail.com>
This commit is contained in:
Sebastian Alba 2026-04-27 19:38:16 -06:00
parent fc674574ca
commit a075929b91
1 changed files with 17 additions and 1 deletions

View File

@ -1580,7 +1580,23 @@ static bool whisper_model_load(struct whisper_model_loader * loader, whisper_con
read_safe(loader, filters.n_mel);
read_safe(loader, filters.n_fft);
filters.data.resize(filters.n_mel * filters.n_fft);
// Validate dimensions to prevent integer overflow in the resize() multiplication
// below and to ensure consistency with hparams.n_mels.
if (filters.n_mel <= 0 || filters.n_mel > 1024 ||
filters.n_fft <= 0 || filters.n_fft > WHISPER_N_FFT) {
WHISPER_LOG_ERROR("%s: invalid mel filter dimensions (n_mel=%d, n_fft=%d)\n",
__func__, filters.n_mel, filters.n_fft);
return false;
}
if (filters.n_mel != hparams.n_mels) {
WHISPER_LOG_ERROR("%s: mel filter n_mel=%d does not match hparams n_mels=%d\n",
__func__, filters.n_mel, hparams.n_mels);
return false;
}
const size_t total_size = (size_t)filters.n_mel * (size_t)filters.n_fft;
filters.data.resize(total_size);
loader->read(loader->context, filters.data.data(), filters.data.size() * sizeof(float));
BYTESWAP_FILTERS(filters);
}