Skip to content

Decoding a stream with no codec context (truncated bitstream) crashes the process (SIGSEGV/SIGBUS) instead of raising #2344

Description

@justinrmiller

Overview

Calling container.decode() / Stream.decode() on a video (or audio) stream whose codec_context is None crashes the entire process with a fatal memory-access fault (SIGSEGV or SIGBUS; EXC_BAD_ACCESS on macOS) instead of raising a catchable Python exception. codec_context is None when the container opens but cannot establish codec parameters — e.g. a bitstream truncated inside its first coded frame. Because the fault is a native memory access inside FFmpeg's multithreaded decoder, no try/except can recover it: one bad input takes down the whole interpreter.

Reproduce

import av, io

# any H.264/mp4 with faststart (moov up front), truncated inside the first frame
data = open("some_faststart_h264.mp4", "rb").read()
truncated = data[:406]

container = av.open(io.BytesIO(truncated))
stream = container.streams.video[0]
assert stream.codec_context is None          # container couldn't build one
for _ in container.decode(stream):           # <-- process dies here
    pass

Exit status is 139 (SIGSEGV) or 138 (SIGBUS) depending on build; a try/except around the loop does not help — the process is gone before any exception is raised.

Environment

  • PyAV 18.0.0 (also reproduced on a source build against the same FFmpeg)
  • FFmpeg 8.1.2, libavcodec 62.28.102
  • macOS 15 / Apple Silicon (arm64), CPython 3.12 and 3.14

Root cause

The decode context PyAV creates for container streams uses FFmpeg's default threading (thread_count = 0 → auto; here 4 threads, thread_type = SLICE). FFmpeg 8.1's slice-multithreaded H.264 decoder performs an out-of-bounds / misaligned read when handed a stream truncated inside its first coded frame. Three observations isolate the threading as the trigger, on the same bytes:

Path Result
ffmpeg CLI (same libav 62.x) graceful — "Output file does not contain any stream", no crash
PyAV, default auto-threaded container.decode() crash (SIGSEGV/SIGBUS)
PyAV, thread_count = 1 / thread_type = "NONE" graceful — decodes 0 frames, no crash

A fault report puts the faulting frame in VideoStream.decodeCodecContext.decode (avcodec_send_packet/avcodec_receive_frame).

It is a class of inputs, not one file: sweeping truncation offsets across several synthetic H.264/mp4 variants (varying duration, GOP, size), decoding each prefix in an isolated subprocess, ~3–4% of arbitrary truncation points crash under the default threading and 0% crash single-threaded. Every crashing cut has stream.codec_context is None.

Suggested fix

The underlying OOB read is arguably an FFmpeg bug in the slice-threaded H.264 decoder (worth reporting upstream too). On the PyAV side, VideoStream.decode / AudioStream.decode are thin passthroughs to self.codec_context.decode(packet) and should not proceed to a native decode when codec_context is None — they can raise a clear ValueError instead. This converts the process-killing crash into a catchable error and is not a perf change for valid streams (they have a codec context and are unaffected; default multithreaded decode is preserved).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions