Skip to content

fix(ffmpeg): heap reads after the end, bad packet, checks at open - #5406

Open
lgritz wants to merge 1 commit into
AcademySoftwareFoundation:mainfrom
lgritz:lg-ffmpeg
Open

fix(ffmpeg): heap reads after the end, bad packet, checks at open#5406
lgritz wants to merge 1 commit into
AcademySoftwareFoundation:mainfrom
lgritz:lg-ffmpeg

Conversation

@lgritz

@lgritz lgritz commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

This is from an audit of the FFmpeg reader, looking for bugs and security issues.

Grayscale movies read heap memory after the end of the buffer. The GRAY8 and GRAY16 code asked swscale for a gray frame with one plane, but it left nchannels at 3. Each scanline copy then read three times too many bytes. Now nchannels is 1. This was the intent when we added the gray code (#2349). It also makes 16-bit gray files readable, which they were not before.

A decoder can change the frame size in the middle of a stream. But we make the scale context and the RGB buffer one time, from the header. A smaller frame thus caused a read after its end. Now read_frame() refuses a frame whose size or pixel format is different from the ImageSpec. It also scales from the spec.

Nothing examined the frame size at open. A file of 6 KB can declare a frame of 1.1 GB, and cause an allocation of 2.4 GB. Now check_open() and check_compression_ratio() refuse such a file. The ratio floor stays at 1 GB, because a correct movie with one frame can reach a ratio of 13000:1.

read_frame() gave an uninitialized AVPacket to av_read_frame(), which does not initialize it when it fails. The code then read bad values from the packet, and it could continue forever on a bad stream. Now the code uses av_packet_alloc(), and it stops when the decoder is empty.

There are smaller changes. Examine the result of av_frame_alloc(). Correct a bad nb_frames and start_time. Protect time_stamp() against a time base of zero. Do the frame-count arithmetic in double, because two int64 times can overflow. Report a decode failure, and do not give back the last good frame. Remove an unnecessary av_free() after avformat_close_input().

New test fixtures come from testsuite/ffmpeg/src/make_malformed_movies.py: the gray8 and gray16 tests, a decompression bomb, a truncated stream, and a change of size in the middle of a stream.

Assisted-by: Claude Code / claude-opus-5

This is a security audit of the FFmpeg reader (specs/002-format-security-audit).

Gray movies read heap memory after the end of the buffer. The GRAY8 and
GRAY16 code asked swscale for a gray frame with one plane, but it left
nchannels at 3. Each scanline copy then read three times too many bytes.
Now nchannels is 1. This was the intent when we added the gray code
(AcademySoftwareFoundation#2349). It also makes 16-bit gray files readable, which they were not
before.

A decoder can change the frame size in the middle of a stream. But we make
the scale context and the RGB buffer one time, from the header. A smaller
frame thus caused a read after its end. Now read_frame() refuses a frame
whose size or pixel format is different from the ImageSpec. It also scales
from the spec.

Nothing examined the frame size at open. A file of 6 KB can declare a frame
of 1.1 GB, and cause an allocation of 2.4 GB. Now check_open() and
check_compression_ratio() refuse such a file. The ratio floor stays at
1 GB, because a correct movie with one frame can reach a ratio of 13000:1.

read_frame() gave an uninitialized AVPacket to av_read_frame(), which does
not initialize it when it fails. The code then read bad values from the
packet, and it could continue forever on a bad stream. Now the code uses
av_packet_alloc(), and it stops when the decoder is empty.

There are smaller changes. Examine the result of av_frame_alloc(). Correct
a bad nb_frames and start_time. Protect time_stamp() against a time base of
zero. Do the frame-count arithmetic in double, because two int64 times can
overflow. Report a decode failure, and do not give back the last good
frame. Remove an unnecessary av_free() after avformat_close_input().

New fixtures come from testsuite/ffmpeg/src/make_malformed_movies.py: the
gray8 and gray16 tests, a decompression bomb, a truncated stream, and a
change of size in the middle of a stream.

Assisted-by: Claude Code / claude-opus-5

Signed-off-by: Larry Gritz <lg@larrygritz.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant