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

In guess_mv() and related error concealment routines in 
libavcodec/error_resilience.c:
1. Pointers such as cur_pic.f, last_pic.f, cur_pic.motion_val[0], 
last_pic.motion_val[0],
   cur_pic.ref_index[0], er_temp_buffer, error_status_table, and decode_mb can 
be NULL or
   uninitialized when encountering corrupted or truncated bitstreams.
2. Boundary calculations for mb_xy in add_blocklist() and guess_mv() can access 
memory past
   allocated block/macroblock boundaries (max_blocks = mb_stride * mb_height).
3. Frame dimensions between cur_pic and last_pic may mismatch upon mid-stream 
dynamic
   resolution changes.

Add NULL pointer guards, ensure frame dimensions match before motion vector 
estimation,
and validate macroblock index bounds across add_blocklist() and reconstruction 
passes,
preventing SIGSEGV crashes and heap buffer overflows during error resilience.

Signed-off-by: yongdev <[email protected]>

# Summary of changes

Briefly describe what this PR does and why.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From 1c5701a10b2b2e971f40c1dcb0b796d2253836d3 Mon Sep 17 00:00:00 2001
From: yongdev <[email protected]>
Date: Thu, 20 Aug 2026 15:40:07 +0000
Subject: [PATCH] avcodec/error_resilience: fix null pointer dereferences and
 out-of-bounds access in error concealment

In guess_mv() and related error concealment routines in 
libavcodec/error_resilience.c:
1. Pointers such as cur_pic.f, last_pic.f, cur_pic.motion_val[0], 
last_pic.motion_val[0],
   cur_pic.ref_index[0], er_temp_buffer, error_status_table, and decode_mb can 
be NULL or
   uninitialized when encountering corrupted or truncated bitstreams.
2. Boundary calculations for mb_xy in add_blocklist() and guess_mv() can access 
memory past
   allocated block/macroblock boundaries (max_blocks = mb_stride * mb_height).
3. Frame dimensions between cur_pic and last_pic may mismatch upon mid-stream 
dynamic
   resolution changes.

Add NULL pointer guards, ensure frame dimensions match before motion vector 
estimation,
and validate macroblock index bounds across add_blocklist() and reconstruction 
passes,
preventing SIGSEGV crashes and heap buffer overflows during error resilience.

Signed-off-by: yongdev <[email protected]>
---
 libavcodec/error_resilience.c | 102 +++++++++++++++++++++++-----------
 1 file changed, 69 insertions(+), 33 deletions(-)

diff --git a/libavcodec/error_resilience.c b/libavcodec/error_resilience.c
index 8cf5bc6a3c..777a60cf0c 100644
--- a/libavcodec/error_resilience.c
+++ b/libavcodec/error_resilience.c
@@ -398,13 +398,15 @@ static void v_block_filter(ERContext *s, uint8_t *dst, 
int w, int h,
 #define MV_CHANGED   4
 #define MV_UNCHANGED 2
 #define MV_LISTED    1
-static av_always_inline void add_blocklist(int (*blocklist)[2], int 
*blocklist_length, uint8_t *fixed, int mb_x, int mb_y, int mb_xy)
+static av_always_inline void add_blocklist(int (*blocklist)[2], int 
*blocklist_length, uint8_t *fixed, int mb_x, int mb_y, int mb_xy, int 
max_blocks)
 {
-    if (fixed[mb_xy])
+    if (mb_xy < 0 || mb_xy >= max_blocks || fixed[mb_xy])
         return;
     fixed[mb_xy] = MV_LISTED;
-    blocklist[ *blocklist_length   ][0] = mb_x;
-    blocklist[(*blocklist_length)++][1] = mb_y;
+    if (*blocklist_length < max_blocks) {
+        blocklist[ *blocklist_length   ][0] = mb_x;
+        blocklist[(*blocklist_length)++][1] = mb_y;
+    }
 }
 
 static void guess_mv(ERContext *s)
@@ -418,9 +420,22 @@ static void guess_mv(ERContext *s)
     int mb_x, mb_y;
     ptrdiff_t mot_step, mot_stride;
     int blocklist_length, next_blocklist_length;
+    const int max_blocks = s->mb_stride * s->mb_height;
 
-    if (s->last_pic.f && s->last_pic.f->data[0])
-        mb_height = FFMIN(mb_height, (s->last_pic.f->height+15)>>4);
+    if (!s->cur_pic.f || !s->cur_pic.f->data[0] ||
+        !s->last_pic.f || !s->last_pic.f->data[0])
+        return;
+    if (!s->cur_pic.motion_val[0] || !s->last_pic.motion_val[0])
+        return;
+    if (!s->er_temp_buffer || !s->error_status_table || !s->mb_index2xy || 
!s->cur_pic.mb_type)
+        return;
+    if (s->mb_width <= 0 || s->mb_height <= 0 || s->mb_stride <= 0)
+        return;
+    if (s->last_pic.f->width != s->cur_pic.f->width ||
+        s->last_pic.f->height != s->cur_pic.f->height)
+        return;
+
+    mb_height = FFMIN(mb_height, (s->last_pic.f->height+15)>>4);
     if (s->next_pic.f && s->next_pic.f->data[0])
         mb_height = FFMIN(mb_height, (s->next_pic.f->height+15)>>4);
 
@@ -437,10 +452,15 @@ static void guess_mv(ERContext *s)
         else
             ff_thread_progress_await(s->last_pic.progress, mb_height - 1);
     }
-    for (i = 0; i < mb_width * mb_height; i++) {
+    for (i = 0; i < mb_width * mb_height && i < s->mb_num; i++) {
         const int mb_xy = s->mb_index2xy[i];
         int f = 0;
-        int error = s->error_status_table[mb_xy];
+        int error;
+
+        if (mb_xy < 0 || mb_xy >= max_blocks)
+            continue;
+
+        error = s->error_status_table[mb_xy];
 
         if (IS_INTRA(s->cur_pic.mb_type[mb_xy]))
             f = MV_FROZEN; // intra // FIXME check
@@ -453,10 +473,13 @@ static void guess_mv(ERContext *s)
         else if(s->last_pic.f->data[0] && s->last_pic.motion_val[0]){
             const int mb_y= mb_xy / s->mb_stride;
             const int mb_x= mb_xy % s->mb_stride;
-            const int mot_index= (mb_x + mb_y*mot_stride) * mot_step;
-            s->cur_pic.motion_val[0][mot_index][0]= 
s->last_pic.motion_val[0][mot_index][0];
-            s->cur_pic.motion_val[0][mot_index][1]= 
s->last_pic.motion_val[0][mot_index][1];
-            s->cur_pic.ref_index[0][4*mb_xy]      = 
s->last_pic.ref_index[0][4*mb_xy];
+            if (mb_x < mb_width && mb_y < mb_height) {
+                const int mot_index= (mb_x + mb_y*mot_stride) * mot_step;
+                s->cur_pic.motion_val[0][mot_index][0]= 
s->last_pic.motion_val[0][mot_index][0];
+                s->cur_pic.motion_val[0][mot_index][1]= 
s->last_pic.motion_val[0][mot_index][1];
+                if (s->cur_pic.ref_index[0] && s->last_pic.ref_index[0])
+                    s->cur_pic.ref_index[0][4*mb_xy]  = 
s->last_pic.ref_index[0][4*mb_xy];
+            }
         }
     }
 
@@ -467,6 +490,8 @@ static void guess_mv(ERContext *s)
                 const int mb_xy = mb_x + mb_y * s->mb_stride;
                 int mv_dir = (s->last_pic.f && s->last_pic.f->data[0]) ? 
MV_DIR_FORWARD : MV_DIR_BACKWARD;
 
+                if (mb_xy < 0 || mb_xy >= max_blocks)
+                    continue;
                 if (IS_INTRA(s->cur_pic.mb_type[mb_xy]))
                     continue;
                 if (!(s->error_status_table[mb_xy] & ER_MV_ERROR))
@@ -474,8 +499,9 @@ static void guess_mv(ERContext *s)
 
                 s->mv[0][0][0] = 0;
                 s->mv[0][0][1] = 0;
-                s->decode_mb(s->opaque, 0, mv_dir, MV_TYPE_16X16, &s->mv,
-                             mb_x, mb_y, 0, 0);
+                if (s->decode_mb)
+                    s->decode_mb(s->opaque, 0, mv_dir, MV_TYPE_16X16, &s->mv,
+                                 mb_x, mb_y, 0, 0);
             }
         }
         return;
@@ -485,11 +511,11 @@ static void guess_mv(ERContext *s)
     for (mb_y = 0; mb_y < mb_height; mb_y++) {
         for (mb_x = 0; mb_x < mb_width; mb_x++) {
             const int mb_xy = mb_x + mb_y * mb_stride;
-            if (fixed[mb_xy] == MV_FROZEN) {
-                if (mb_x)               add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x - 1, mb_y, mb_xy - 1);
-                if (mb_y)               add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x, mb_y - 1, mb_xy - mb_stride);
-                if (mb_x+1 < mb_width)  add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x + 1, mb_y, mb_xy + 1);
-                if (mb_y+1 < mb_height) add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x, mb_y + 1, mb_xy + mb_stride);
+            if (mb_xy < max_blocks && fixed[mb_xy] == MV_FROZEN) {
+                if (mb_x)               add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x - 1, mb_y, mb_xy - 1, max_blocks);
+                if (mb_y)               add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x, mb_y - 1, mb_xy - mb_stride, max_blocks);
+                if (mb_x+1 < mb_width)  add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x + 1, mb_y, mb_xy + 1, max_blocks);
+                if (mb_y+1 < mb_height) add_blocklist(blocklist, 
&blocklist_length, fixed, mb_x, mb_y + 1, mb_xy + mb_stride, max_blocks);
             }
         }
     }
@@ -548,7 +574,7 @@ static void guess_mv(ERContext *s)
                     mv_predictor[pred_count][1] =
                         s->cur_pic.motion_val[0][mot_index - mot_step][1];
                     ref[pred_count] =
-                        s->cur_pic.ref_index[0][4 * (mb_xy - 1)];
+                        s->cur_pic.ref_index[0] ? s->cur_pic.ref_index[0][4 * 
(mb_xy - 1)] : 0;
                     pred_count++;
                 }
                 if (mb_x + 1 < mb_width && fixed[mb_xy + 1] > 1) {
@@ -557,7 +583,7 @@ static void guess_mv(ERContext *s)
                     mv_predictor[pred_count][1] =
                         s->cur_pic.motion_val[0][mot_index + mot_step][1];
                     ref[pred_count] =
-                        s->cur_pic.ref_index[0][4 * (mb_xy + 1)];
+                        s->cur_pic.ref_index[0] ? s->cur_pic.ref_index[0][4 * 
(mb_xy + 1)] : 0;
                     pred_count++;
                 }
                 if (mb_y > 0 && fixed[mb_xy - mb_stride] > 1) {
@@ -566,7 +592,7 @@ static void guess_mv(ERContext *s)
                     mv_predictor[pred_count][1] =
                         s->cur_pic.motion_val[0][mot_index - mot_stride * 
mot_step][1];
                     ref[pred_count] =
-                        s->cur_pic.ref_index[0][4 * (mb_xy - s->mb_stride)];
+                        s->cur_pic.ref_index[0] ? s->cur_pic.ref_index[0][4 * 
(mb_xy - s->mb_stride)] : 0;
                     pred_count++;
                 }
                 if (mb_y + 1<mb_height && fixed[mb_xy + mb_stride] > 1) {
@@ -575,7 +601,7 @@ static void guess_mv(ERContext *s)
                     mv_predictor[pred_count][1] =
                         s->cur_pic.motion_val[0][mot_index + mot_stride * 
mot_step][1];
                     ref[pred_count] =
-                        s->cur_pic.ref_index[0][4 * (mb_xy + s->mb_stride)];
+                        s->cur_pic.ref_index[0] ? s->cur_pic.ref_index[0][4 * 
(mb_xy + s->mb_stride)] : 0;
                     pred_count++;
                 }
                 if (pred_count == 0)
@@ -634,7 +660,7 @@ skip_mean_and_median:
 
                 prev_x   = s->cur_pic.motion_val[0][mot_index][0];
                 prev_y   = s->cur_pic.motion_val[0][mot_index][1];
-                prev_ref = s->cur_pic.ref_index[0][4 * mb_xy];
+                prev_ref = s->cur_pic.ref_index[0] ? s->cur_pic.ref_index[0][4 
* mb_xy] : 0;
 
                 /* last MV */
                 mv_predictor[pred_count][0] = prev_x;
@@ -659,8 +685,9 @@ skip_mean_and_median:
                     if (ref[j] < 0)
                         continue;
 
-                    s->decode_mb(s->opaque, ref[j], MV_DIR_FORWARD,
-                                 MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0);
+                    if (s->decode_mb)
+                        s->decode_mb(s->opaque, ref[j], MV_DIR_FORWARD,
+                                     MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0);
 
                     if (mb_x > 0 && fixed[mb_xy - 1] > 1) {
                         int k;
@@ -700,8 +727,9 @@ skip_mean_and_median:
                         s->cur_pic.motion_val[0][mot_index + i + j * 
mot_stride][1] = s->mv[0][0][1];
                     }
 
-                s->decode_mb(s->opaque, ref[best_pred], MV_DIR_FORWARD,
-                             MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0);
+                if (s->decode_mb && ref[best_pred] >= 0)
+                    s->decode_mb(s->opaque, ref[best_pred], MV_DIR_FORWARD,
+                                 MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0);
 
 
                 if (s->mv[0][0][0] != prev_x || s->mv[0][0][1] != prev_y) {
@@ -722,16 +750,16 @@ skip_mean_and_median:
             const int mb_y = blocklist[blocklist_index][1];
             const int mb_xy = mb_x + mb_y * mb_stride;
 
-            if (fixed[mb_xy] & (MV_CHANGED|MV_UNCHANGED|MV_FROZEN)) {
+            if (mb_xy < max_blocks && (fixed[mb_xy] & 
(MV_CHANGED|MV_UNCHANGED|MV_FROZEN))) {
                 fixed[mb_xy] = MV_FROZEN;
                 if (mb_x > 0)
-                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x - 1, mb_y, mb_xy - 1);
+                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x - 1, mb_y, mb_xy - 1, max_blocks);
                 if (mb_y > 0)
-                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x, mb_y - 1, mb_xy - mb_stride);
+                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x, mb_y - 1, mb_xy - mb_stride, max_blocks);
                 if (mb_x + 1 < mb_width)
-                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x + 1, mb_y, mb_xy + 1);
+                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x + 1, mb_y, mb_xy + 1, max_blocks);
                 if (mb_y + 1 < mb_height)
-                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x, mb_y + 1, mb_xy + mb_stride);
+                    add_blocklist(next_blocklist, &next_blocklist_length, 
fixed, mb_x, mb_y + 1, mb_xy + mb_stride, max_blocks);
             }
         }
         av_assert0(next_blocklist_length <= mb_height * mb_width);
@@ -1169,6 +1197,8 @@ void ff_er_frame_end(ERContext *s, int 
*decode_error_flags)
                 continue; // inter with damaged MV
             if (!(error & ER_AC_ERROR))
                 continue; // undamaged inter
+            if (!s->cur_pic.motion_val[dir])
+                continue;
 
             if (IS_8X8(mb_type)) {
                 int mb_index = mb_x * 2 + mb_y * 2 * s->b8_stride;
@@ -1253,6 +1283,9 @@ void ff_er_frame_end(ERContext *s, int 
*decode_error_flags)
             // if (error & ER_MV_ERROR)
             //     continue; // inter data damaged FIXME is this good?
 
+            if (!s->cur_pic.f || !s->cur_pic.f->data[0] || 
!s->cur_pic.f->data[1] || !s->cur_pic.f->data[2])
+                continue;
+
             dest_y  = s->cur_pic.f->data[0] + mb_x * 16 + mb_y * 16 * 
linesize[0];
             dest_cb = s->cur_pic.f->data[1] + mb_x *  8 + mb_y *  8 * 
linesize[1];
             dest_cr = s->cur_pic.f->data[2] + mb_x *  8 + mb_y *  8 * 
linesize[2];
@@ -1309,6 +1342,9 @@ void ff_er_frame_end(ERContext *s, int 
*decode_error_flags)
             if (!(error & ER_AC_ERROR))
                 continue; // undamaged
 
+            if (!s->cur_pic.f || !s->cur_pic.f->data[0] || 
!s->cur_pic.f->data[1] || !s->cur_pic.f->data[2])
+                continue;
+
             dest_y  = s->cur_pic.f->data[0] + mb_x * 16 + mb_y * 16 * 
linesize[0];
             dest_cb = s->cur_pic.f->data[1] + mb_x *  8 + mb_y *  8 * 
linesize[1];
             dest_cr = s->cur_pic.f->data[2] + mb_x *  8 + mb_y *  8 * 
linesize[2];
-- 
2.52.0

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

Reply via email to