PR #25339 opened by Daniel Brodie (danielbrodie)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25339
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25339.patch

# Summary of changes

Fixes #25284. `ff_lzf_uncompress()` looped while more than 2 bytes were left, 
so a stream ending in a one-byte literal run or a short back-reference lost its 
last token and the output came out short. liblzf can end a stream that way. The 
`> 2` dates from the original decompressor (0c7707610f). Literal runs are 
already checked against the input since 610d368d9b; this adds the same check 
for back-references, so a truncated stream still returns AVERROR_INVALIDDATA.

Tested on master adfe258a6a:
- the sample attached to #25284 now decodes every frame (before: none), and the 
full 300-frame export it was cut from decodes 300/300 frames, 
framemd5-identical to the LZ4 original;
- all fate-dxv tests pass, including the two that use LZF (dxv-na, dxv-wa);
- make fate (Windows, MSYS2 MINGW64, full fate-suite): the 15 failures also 
fail on unpatched master;
- 200 decodes of damaged copies (random byte changes inside mdat) gave no 
crashes or hangs.

AI disclosure: AI was used for investigation and development of this patch.


>From 8699164b77098d2f541333929c6f0cf7a0d8a141 Mon Sep 17 00:00:00 2001
From: Daniel Brodie <[email protected]>
Date: Wed, 21 Oct 2026 14:43:02 -0400
Subject: [PATCH] avcodec/lzf: decode a final token shorter than 3 bytes

The loop stopped with 2 bytes left, so a stream ending in a one-byte
literal run or a short back-reference lost its last token and the output
came out short. liblzf can end a stream that way. Check that each
back-reference has its bytes instead.

Fixes: #25284
Signed-off-by: Daniel Brodie <[email protected]>
---
 libavcodec/lzf.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libavcodec/lzf.c b/libavcodec/lzf.c
index 1c38c98982e..7cce124efd9 100644
--- a/libavcodec/lzf.c
+++ b/libavcodec/lzf.c
@@ -58,7 +58,7 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, 
size_t *size, unsigned
     uint8_t *p  = *buf;
     int64_t len = 0;
 
-    while (bytestream2_get_bytes_left(gb) > 2) {
+    while (bytestream2_get_bytes_left(gb) > 0) {
         uint8_t s = bytestream2_get_byte(gb);
 
         if (s < LZF_LITERAL_MAX) {
@@ -80,6 +80,9 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, 
size_t *size, unsigned
             int l   = 2 + (s >> 5);
             int off = ((s & 0x1f) << 8) + 1;
 
+            if (bytestream2_get_bytes_left(gb) < 1 + (l == LZF_LONG_BACKREF))
+                return AVERROR_INVALIDDATA;
+
             if (l == LZF_LONG_BACKREF)
                 l += bytestream2_get_byte(gb);
 
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to