Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/whisper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3189,6 +3189,14 @@ static bool log_mel_spectrogram(
int64_t stage_1_pad = WHISPER_SAMPLE_RATE * 30;
int64_t stage_2_pad = frame_size / 2;

// The reflective padding below reads samples[1 .. stage_2_pad], so an input
// shorter than stage_2_pad + 1 would read past the end of the buffer.
if (n_samples < stage_2_pad + 1) {
WHISPER_LOG_ERROR("%s: audio too short: %d samples, need at least %d\n",
__func__, n_samples, (int) stage_2_pad + 1);
return false;
}

// Initialize a vector and copy data from C array to it.
std::vector<float> samples_padded;
samples_padded.resize(n_samples + stage_1_pad + stage_2_pad * 2);
Expand Down
10 changes: 10 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,13 @@ add_parakeet_transcription_test(
tests/parakeet-expected-diffusion-output.txt
0.95)

# log_mel_spectrogram out-of-bounds read on very short audio (issue #3923)
set(MEL_OOB_TEST test-log-mel-oob)
add_executable(${MEL_OOB_TEST} ${MEL_OOB_TEST}.cpp)
target_include_directories(${MEL_OOB_TEST} PRIVATE ../include ../ggml/include ../examples)
target_link_libraries(${MEL_OOB_TEST} PRIVATE common)
target_compile_definitions(${MEL_OOB_TEST} PRIVATE
WHISPER_MODEL_PATH="${PROJECT_SOURCE_DIR}/models/for-tests-ggml-tiny.bin")
add_test(NAME ${MEL_OOB_TEST} COMMAND ${MEL_OOB_TEST})
set_tests_properties(${MEL_OOB_TEST} PROPERTIES LABELS "unit;gh")

35 changes: 35 additions & 0 deletions tests/test-log-mel-oob.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "whisper.h"

#include <string>
#include <vector>

#ifdef NDEBUG
#undef NDEBUG
#endif
#include <cassert>

// Regression test for the heap out-of-bounds read in log_mel_spectrogram().
// Reflective padding reads samples[1 .. WHISPER_N_FFT/2], so an input shorter
// than WHISPER_N_FFT/2 + 1 (201) samples used to read past the end of the
// caller's buffer. Such inputs must now be rejected with a non-zero return.

int main() {
std::string model_path = WHISPER_MODEL_PATH;

struct whisper_context_params cparams = whisper_context_default_params();
struct whisper_context * ctx = whisper_init_from_file_with_params(model_path.c_str(), cparams);
assert(ctx != nullptr);

std::vector<float> samples(256, 0.0f);

// Shorter than the reflective-pad window: must be rejected, not read OOB.
assert(whisper_pcm_to_mel(ctx, samples.data(), 4, 1) != 0);
assert(whisper_pcm_to_mel(ctx, samples.data(), 200, 1) != 0);

// Long enough to pad safely: must still succeed.
assert(whisper_pcm_to_mel(ctx, samples.data(), 201, 1) == 0);

whisper_free(ctx);

return 0;
}