PR #25513 opened by Marti2203
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25513
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25513.patch

`unpack_intraframe()` and `decode_interframe_v4a()` reject a back-reference run 
when `offset + count` (for v4a, `offset + width + count`) is `>=` width*height. 
The same check is applied to `writeoffset`. The copy loops only touch indices 
up to that sum minus one, so a run that ends exactly on the last pixel of the 
frame is valid, but it gets rejected. Decoding of the packet stops with 
"Attempting to write out of bounds", but the error is not propagated and the 
frame is still returned. The end of the frame keeps stale pixels from an 
earlier frame.

The patch changes the four checks to `>`, which matches the checks in 
`decode_interframe_v4()`. The intraframe read check is never the deciding one, 
because intraframe back-references always point backwards. I changed it too so 
the checks stay consistent. The literal-byte and tag 1 checks in the same 
functions were already correct and are not touched.

Verification (master 38c6f36f93, release and ASan builds):
- A synthetic 640x150 HNM4A file (3 frames, 3475 bytes) has a v4a inter frame 
and an intra frame whose last run ends exactly at width*height. Before the fix, 
the decoder logs the error twice, returns 0 and outputs frames with stale 
tails: 80 pixels wrong in frame 2 and 69 in frame 3. After the fix, it logs 
nothing and the tails are decoded correctly (frame 2 tail = the referenced 
rows, frame 3 tail = the run's colour). Both builds are ASan-clean before and 
after.
- No FATE test covers hnm4 today. The new `fate-hnm4-boundary-runs` (test diff 
in the comment below; it needs a sample upload, so it is not in this branch 
yet) fails without the fix and passes with it. It needs the attached sample 
uploaded as `hnm4/boundary-runs.hnm`. The existing qdm2/vmnc tests I ran as a 
sanity check pass unchanged.

This issue was found by an LLM-Enhanced Static Analysis Engine and verified 
manually.

🤖 Generated with [Claude Code](https://claude.com/claude-code)



From 564ceb202c050496b424f934c586ec4dd5ad1412 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Tue, 27 Oct 2026 10:40:15 +0800
Subject: [PATCH] avcodec/hnm4video: fix off-by-one in back-reference bounds
 checks

The back-reference copies in unpack_intraframe() and
decode_interframe_v4a() access at most index
offset + count - 1 (offset + width + count - 1 for v4a), and likewise
for writeoffset, so a run is in bounds as long as that sum does not
exceed width * height. The checks used >= instead and rejected a run
that ends exactly on the last pixel of the frame. Decoding of the
packet then stops with an "out of bounds" error, but the frame is
still returned, so the end of the frame keeps the stale pixels of a
previous frame.

Use > as decode_interframe_v4() already does. The intraframe read check
is never the decisive one, since intraframe back-references always
point backwards, but is changed as well for consistency.

Signed-off-by: Martin Mirchev <[email protected]>
---
 libavcodec/hnm4video.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavcodec/hnm4video.c b/libavcodec/hnm4video.c
index 51e75789d2a..02e343fff5a 100644
--- a/libavcodec/hnm4video.c
+++ b/libavcodec/hnm4video.c
@@ -100,10 +100,10 @@ static void unpack_intraframe(AVCodecContext *avctx, 
const uint8_t *src,
             }
             count  += 2;
             offset += writeoffset;
-            if (offset < 0 || offset + count >= hnm->width * hnm->height) {
+            if (offset < 0 || offset + count > hnm->width * hnm->height) {
                 av_log(avctx, AV_LOG_ERROR, "Attempting to read out of 
bounds\n");
                 break;
-            } else if (writeoffset + count >= hnm->width * hnm->height) {
+            } else if (writeoffset + count > hnm->width * hnm->height) {
                 av_log(avctx, AV_LOG_ERROR,
                        "Attempting to write out of bounds\n");
                 break;
@@ -326,10 +326,10 @@ static void decode_interframe_v4a(AVCodecContext *avctx, 
const uint8_t *src,
                 offset -= 0x10000;
             }
 
-            if (offset + hnm->width + count >= hnm->width * hnm->height) {
+            if (offset + hnm->width + count > hnm->width * hnm->height) {
                 av_log(avctx, AV_LOG_ERROR, "Attempting to read out of 
bounds\n");
                 break;
-            } else if (writeoffset + hnm->width + count >= hnm->width * 
hnm->height) {
+            } else if (writeoffset + hnm->width + count > hnm->width * 
hnm->height) {
                 av_log(avctx, AV_LOG_ERROR, "Attempting to write out of 
bounds\n");
                 break;
             }
-- 
2.54.0

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

Reply via email to