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


>From 85bff0a31c3abb6cb09b4832c5dbf5fa95017e16 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Sun, 22 Sep 2024 11:39:14 +0800
Subject: [PATCH 01/24] avcodec/vvc: Don't use large array on stack

tmp_array in dmvr_hv takes 33024 bytes on stack, which can be
dangerous.

(cherry picked from commit 240c16bbc6ef723139793fea6e94516dfb058db9)
---
 libavcodec/vvc/inter_template.c | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/libavcodec/vvc/inter_template.c b/libavcodec/vvc/inter_template.c
index c073a73e76..aee4994c17 100644
--- a/libavcodec/vvc/inter_template.c
+++ b/libavcodec/vvc/inter_template.c
@@ -472,6 +472,9 @@ static void FUNC(apply_bdof)(uint8_t *_dst, const ptrdiff_t 
_dst_stride, const i
     (filter[0] * src[x] +                                                      
 \
      filter[1] * src[x + stride])
 
+#define DMVR_FILTER2(filter, src0, src1)        \
+    (filter[0] * src0 + filter[1] * src1)
+
 //8.5.3.2.2 Luma sample bilinear interpolation process
 static void FUNC(dmvr)(int16_t *dst, const uint8_t *_src, const ptrdiff_t 
_src_stride,
     const int height, const intptr_t mx, const intptr_t my, const int width)
@@ -541,31 +544,31 @@ static void FUNC(dmvr_v)(int16_t *dst, const uint8_t 
*_src, const ptrdiff_t _src
 static void FUNC(dmvr_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t 
_src_stride,
     const int height, const intptr_t mx, const intptr_t my, const int width)
 {
-    int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE];
-    int16_t *tmp                = tmp_array;
+    int16_t tmp_array[MAX_PB_SIZE * 2];
+    int16_t *tmp0               = tmp_array;
+    int16_t *tmp1               = tmp_array + MAX_PB_SIZE;
     const pixel *src            = (const pixel*)_src;
     const ptrdiff_t src_stride  = _src_stride / sizeof(pixel);
-    const int8_t *filter        = ff_vvc_inter_luma_dmvr_filters[mx];
+    const int8_t *filter_x      = ff_vvc_inter_luma_dmvr_filters[mx];
+    const int8_t *filter_y      = ff_vvc_inter_luma_dmvr_filters[my];
     const int shift1            = BIT_DEPTH - 6;
     const int offset1           = 1 << (shift1 - 1);
     const int shift2            = 4;
     const int offset2           = 1 << (shift2 - 1);
 
     src   -= BILINEAR_EXTRA_BEFORE * src_stride;
-    for (int y = 0; y < height + BILINEAR_EXTRA; y++) {
-        for (int x = 0; x < width; x++)
-            tmp[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1;
-        src += src_stride;
-        tmp += MAX_PB_SIZE;
-    }
+    for (int x = 0; x < width; x++)
+        tmp0[x] = (DMVR_FILTER2(filter_x, src[x], src[x + 1]) + offset1) >> 
shift1;
+    src += src_stride;
 
-    tmp    = tmp_array + BILINEAR_EXTRA_BEFORE * MAX_PB_SIZE;
-    filter = ff_vvc_inter_luma_dmvr_filters[my];
-    for (int y = 0; y < height; y++) {
-        for (int x = 0; x < width; x++)
-            dst[x] = (DMVR_FILTER(tmp, MAX_PB_SIZE) + offset2) >> shift2;
-        tmp += MAX_PB_SIZE;
+    for (int y = 1; y < height + BILINEAR_EXTRA; y++) {
+        for (int x = 0; x < width; x++) {
+            tmp1[x] = (DMVR_FILTER2(filter_x, src[x], src[x + 1]) + offset1) 
>> shift1;
+            dst[x] = (DMVR_FILTER2(filter_y, tmp0[x], tmp1[x]) + offset2) >> 
shift2;
+        }
+        src += src_stride;
         dst += MAX_PB_SIZE;
+        FFSWAP(int16_t *, tmp0, tmp1);
     }
 }
 
-- 
2.49.1


>From f23038f8bd8fde62538e8ebbb4b10a03e92404e0 Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Mon, 14 Oct 2024 23:25:32 +0800
Subject: [PATCH 02/24] avcodec/vvc/thread: Check frame to be non NULL

Fixes: NULL pointer dereference
Fixes: 
71303/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_VVC_fuzzer-4875859050168320

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Reported-by: Michael Niedermayer <[email protected]>
(cherry picked from commit b61141056939acfd67e51dbd05c2c8c729d6b31b)
---
 libavcodec/vvc/dec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/vvc/dec.c b/libavcodec/vvc/dec.c
index edf2607f50..522fa8416e 100644
--- a/libavcodec/vvc/dec.c
+++ b/libavcodec/vvc/dec.c
@@ -1000,7 +1000,7 @@ static int vvc_decode_frame(AVCodecContext *avctx, 
AVFrame *output,
     if (ret < 0)
         return ret;
 
-    if (!fc->ft)
+    if (!fc->ft || !fc->ref)
         return avpkt->size;
 
     ret = submit_frame(s, fc, output, got_output);
-- 
2.49.1


>From 749428672fae2acd93d54431738df8eff37181ce Mon Sep 17 00:00:00 2001
From: Fei Wang <[email protected]>
Date: Mon, 28 Oct 2024 16:55:22 +0800
Subject: [PATCH 03/24] lavc/vvc_refs: Define VVC_FRAME_FLAG* to h header

So that hardware decoder can use the flags too.

Signed-off-by: Fei Wang <[email protected]>
(cherry picked from commit 15a75e8e0425309fdc5a2772ebf622b3705f914a)
---
 libavcodec/vvc/refs.c | 4 ----
 libavcodec/vvc/refs.h | 5 +++++
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/libavcodec/vvc/refs.c b/libavcodec/vvc/refs.c
index bf093bea98..358e11242a 100644
--- a/libavcodec/vvc/refs.c
+++ b/libavcodec/vvc/refs.c
@@ -29,10 +29,6 @@
 
 #include "refs.h"
 
-#define VVC_FRAME_FLAG_OUTPUT    (1 << 0)
-#define VVC_FRAME_FLAG_SHORT_REF (1 << 1)
-#define VVC_FRAME_FLAG_LONG_REF  (1 << 2)
-#define VVC_FRAME_FLAG_BUMPING   (1 << 3)
 
 typedef struct FrameProgress {
     atomic_int progress[VVC_PROGRESS_LAST];
diff --git a/libavcodec/vvc/refs.h b/libavcodec/vvc/refs.h
index 8ae33d4a9a..e2271ab381 100644
--- a/libavcodec/vvc/refs.h
+++ b/libavcodec/vvc/refs.h
@@ -25,6 +25,11 @@
 
 #include "dec.h"
 
+#define VVC_FRAME_FLAG_OUTPUT    (1 << 0)
+#define VVC_FRAME_FLAG_SHORT_REF (1 << 1)
+#define VVC_FRAME_FLAG_LONG_REF  (1 << 2)
+#define VVC_FRAME_FLAG_BUMPING   (1 << 3)
+
 int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, struct AVFrame 
*out, int no_output_of_prior_pics_flag, int flush);
 void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc);
 int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, struct AVFrame 
**frame);
-- 
2.49.1


>From 1dd8ddd35b543a5f78ba0b8d5f0c8c5aa3a7e105 Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Sat, 23 Nov 2024 17:32:32 +0800
Subject: [PATCH 04/24] avcodec/vvcdec: ensure every CTU belongs to a slice

According to section 6.3.3 "Spatial or component-wise partitionings,"
CTUs should fully cover slices with no overlaps, gaps, or additions.
No overlaps are ensured by task_init_parse.
No gaps and no additions are ensured by this patch.

Co-authored-by: Frank Plowman <[email protected]>
(cherry picked from commit 5c5a08ecb53b1b9d97512ce398fa395c3fb8c1a8)
---
 libavcodec/vvc/thread.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libavcodec/vvc/thread.c b/libavcodec/vvc/thread.c
index 86a7753c6a..7ff759d889 100644
--- a/libavcodec/vvc/thread.c
+++ b/libavcodec/vvc/thread.c
@@ -822,6 +822,13 @@ int ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc)
             }
         }
     }
+    for (int rs = 0; rs < ft->ctu_count; rs++) {
+        const VVCTask *t = ft->tasks + rs;
+        if (!t->sc) {
+            av_log(s->avctx, AV_LOG_ERROR, "frame %5d, CTU(%d, %d) not belong 
to any slice\r\n", (int)fc->decode_order, t->rx, t->ry);
+            return AVERROR_INVALIDDATA;
+        }
+    }
     frame_thread_add_score(s, ft, 0, 0, VVC_TASK_STAGE_INIT);
 
     return 0;
-- 
2.49.1


>From bedd05abb9fc33f1982685fba9940416de52b2b2 Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Sat, 23 Nov 2024 17:32:35 +0800
Subject: [PATCH 05/24] avcodec/vvcdec: inter_data, check the return value from
 hls_merge_data

Reported-by: Frank Plowman <[email protected]>
(cherry picked from commit ba89c5b989938fad751ac451d1f6ef813bb42460)
---
 libavcodec/vvc/ctu.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libavcodec/vvc/ctu.c b/libavcodec/vvc/ctu.c
index b33ad576cf..634c553814 100644
--- a/libavcodec/vvc/ctu.c
+++ b/libavcodec/vvc/ctu.c
@@ -1778,13 +1778,16 @@ static int inter_data(VVCLocalContext *lc)
         pu->general_merge_flag = ff_vvc_general_merge_flag(lc);
 
     if (pu->general_merge_flag) {
-        hls_merge_data(lc);
+        ret = hls_merge_data(lc);
     } else if (cu->pred_mode == MODE_IBC){
         ret = mvp_data_ibc(lc);
     } else {
         ret = mvp_data(lc);
     }
 
+    if (ret)
+        return ret;
+
     if (cu->pred_mode == MODE_IBC)
     {
         ff_vvc_update_hmvp(lc, mi);
-- 
2.49.1


>From efe5267958667828f9c1c2776771c9ca031419fe Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Sat, 23 Nov 2024 17:32:37 +0800
Subject: [PATCH 06/24] avcodec/vvcdec: schedule next stage only if the current
 stage reports no error

If the current stage reports an error, some variables may not be correctly 
initialized.
Scheduling the next stage could lead to the use of uninitialized variables.

(cherry picked from commit eb67e60cb004c17ecbbd43c6e3c6732c19de5d33)
---
 libavcodec/vvc/thread.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vvc/thread.c b/libavcodec/vvc/thread.c
index 7ff759d889..6a623e62b1 100644
--- a/libavcodec/vvc/thread.c
+++ b/libavcodec/vvc/thread.c
@@ -655,9 +655,9 @@ static void task_run_stage(VVCTask *t, VVCContext *s, 
VVCLocalContext *lc)
                 "frame %5d, %s(%3d, %3d) failed with %d\r\n",
                 (int)fc->decode_order, task_name[stage], t->rx, t->ry, ret);
         }
+        if (!ret)
+            task_stage_done(t, s);
     }
-
-    task_stage_done(t, s);
     return;
 }
 
-- 
2.49.1


>From 5836dc0622b7c081086b0a047aad265051ea7ee5 Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Sat, 23 Nov 2024 17:32:33 +0800
Subject: [PATCH 07/24] avcodec/cbs_h266: improve readability in subpicture
 parser

(cherry picked from commit e06515b092225f7d954aa64aedde98df097515ee)
---
 libavcodec/cbs_h266_syntax_template.c | 102 +++++++++++++-------------
 1 file changed, 50 insertions(+), 52 deletions(-)

diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index d33629b06c..191e73edd5 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -1057,7 +1057,7 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
                      H266RawSPS *current)
 {
     CodedBitstreamH266Context *h266 = ctx->priv_data;
-    int err, i, j;
+    int err, i, j, max_width_minus1, max_height_minus1;
     unsigned int ctb_log2_size_y, min_cb_log2_size_y,
                  min_qt_log2_size_intra_y, min_qt_log2_size_inter_y,
                  ctb_size_y, max_num_merge_cand, tmp_width_val, tmp_height_val;
@@ -1130,6 +1130,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
                     ctb_log2_size_y);
     tmp_height_val = 
AV_CEIL_RSHIFT(current->sps_pic_height_max_in_luma_samples,
                     ctb_log2_size_y);
+    max_width_minus1  = tmp_width_val - 1;
+    max_height_minus1 = tmp_height_val - 1;
 
     flag(sps_subpic_info_present_flag);
     if (current->sps_subpic_info_present_flag) {
@@ -1147,11 +1149,11 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
             if (current->sps_pic_width_max_in_luma_samples > ctb_size_y)
                 ubs(wlen, sps_subpic_width_minus1[0], 1, 0);
             else
-                infer(sps_subpic_width_minus1[0], tmp_width_val - 1);
+                infer(sps_subpic_width_minus1[0], max_width_minus1);
             if (current->sps_pic_height_max_in_luma_samples > ctb_size_y)
                 ubs(hlen, sps_subpic_height_minus1[0], 1, 0);
             else
-                infer(sps_subpic_height_minus1[0], tmp_height_val - 1);
+                infer(sps_subpic_height_minus1[0], max_height_minus1);
             if (!current->sps_independent_subpics_flag) {
                 flags(sps_subpic_treated_as_pic_flag[0], 1, 0);
                 flags(sps_loop_filter_across_subpic_enabled_flag[0], 1, 0);
@@ -1161,58 +1163,54 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
             }
             for (i = 1; i <= current->sps_num_subpics_minus1; i++) {
                 if (!current->sps_subpic_same_size_flag) {
-                    if (current->sps_pic_width_max_in_luma_samples > 
ctb_size_y) {
-                        const int win_right_edge =
-                            current->sps_pic_width_max_in_luma_samples
-                          - current->sps_conf_win_right_offset * sub_width_c;
-                        us(wlen, sps_subpic_ctu_top_left_x[i], 0,
-                           AV_CEIL_RSHIFT(win_right_edge, ctb_log2_size_y) - 1,
-                           1, i);
-                    } else
+                    const int win_right_edge =
+                        current->sps_pic_width_max_in_luma_samples -
+                        current->sps_conf_win_right_offset * sub_width_c;
+                    const int win_bottom_edge =
+                        current->sps_pic_height_max_in_luma_samples -
+                        current->sps_conf_win_bottom_offset * sub_height_c;
+                    const int win_left_edge =
+                        current->sps_conf_win_left_offset * sub_width_c;
+                    const int win_top_edge =
+                        current->sps_conf_win_top_offset * sub_height_c;
+                    const int win_left_edge_ctus   =
+                        AV_CEIL_RSHIFT(win_left_edge,   ctb_log2_size_y);
+                    const int win_right_edge_ctus  =
+                        AV_CEIL_RSHIFT(win_right_edge,  ctb_log2_size_y);
+                    const int win_top_edge_ctus    =
+                        AV_CEIL_RSHIFT(win_top_edge,    ctb_log2_size_y);
+                    const int win_bottom_edge_ctus =
+                        AV_CEIL_RSHIFT(win_bottom_edge, ctb_log2_size_y);
+                    const int min_width  =
+                        FFMAX(win_left_edge_ctus - 
current->sps_subpic_ctu_top_left_x[i], 0);
+                    const int min_height =
+                        FFMAX(win_top_edge_ctus  - 
current->sps_subpic_ctu_top_left_y[i], 0);
+
+                    if (current->sps_pic_width_max_in_luma_samples > 
ctb_size_y)
+                        us(wlen, sps_subpic_ctu_top_left_x[i], 0, 
win_right_edge_ctus - 1, 1, i);
+                    else
                         infer(sps_subpic_ctu_top_left_x[i], 0);
-                    if (current->sps_pic_height_max_in_luma_samples >
-                        ctb_size_y) {
-                        const int win_bottom_edge =
-                            current->sps_pic_height_max_in_luma_samples
-                          - current->sps_conf_win_bottom_offset * sub_height_c;
-                        us(hlen, sps_subpic_ctu_top_left_y[i], 0,
-                           AV_CEIL_RSHIFT(win_bottom_edge, ctb_log2_size_y) - 
1,
-                           1, i);
-                    } else
+
+                    if (current->sps_pic_height_max_in_luma_samples > 
ctb_size_y)
+                        us(hlen, sps_subpic_ctu_top_left_y[i], 0, 
win_bottom_edge_ctus - 1, 1, i);
+                    else
                         infer(sps_subpic_ctu_top_left_y[i], 0);
+
+                    max_width_minus1  = tmp_width_val  - 
current->sps_subpic_ctu_top_left_x[i] - 1;
+                    max_height_minus1 = tmp_height_val - 
current->sps_subpic_ctu_top_left_y[i] - 1;
+
                     if (i < current->sps_num_subpics_minus1 &&
-                        current->sps_pic_width_max_in_luma_samples >
-                        ctb_size_y) {
-                        const int win_left_edge =
-                            current->sps_conf_win_left_offset * sub_width_c;
-                        const int win_left_edge_ctus =
-                            AV_CEIL_RSHIFT(win_left_edge, ctb_log2_size_y);
-                        us(wlen, sps_subpic_width_minus1[i],
-                           win_left_edge_ctus > 
current->sps_subpic_ctu_top_left_x[i]
-                               ? win_left_edge_ctus - 
current->sps_subpic_ctu_top_left_x[i]
-                               : 0,
-                           MAX_UINT_BITS(wlen), 1, i);
+                        current->sps_pic_width_max_in_luma_samples > 
ctb_size_y) {
+                        us(wlen, sps_subpic_width_minus1[i], min_width, 
MAX_UINT_BITS(wlen), 1, i);
                     } else {
-                        infer(sps_subpic_width_minus1[i],
-                              tmp_width_val -
-                              current->sps_subpic_ctu_top_left_x[i] - 1);
+                        infer(sps_subpic_width_minus1[i], max_width_minus1);
                     }
+
                     if (i < current->sps_num_subpics_minus1 &&
-                        current->sps_pic_height_max_in_luma_samples >
-                        ctb_size_y) {
-                        const int win_top_edge =
-                            current->sps_conf_win_top_offset * sub_height_c;
-                        const int win_top_edge_ctus =
-                            AV_CEIL_RSHIFT(win_top_edge, ctb_log2_size_y);
-                        us(hlen, sps_subpic_height_minus1[i],
-                           win_top_edge_ctus > 
current->sps_subpic_ctu_top_left_y[i]
-                               ? win_top_edge_ctus - 
current->sps_subpic_ctu_top_left_y[i]
-                               : 0,
-                           MAX_UINT_BITS(hlen), 1, i);
+                        current->sps_pic_height_max_in_luma_samples > 
ctb_size_y) {
+                        us(hlen, sps_subpic_height_minus1[i], min_height, 
MAX_UINT_BITS(hlen), 1, i);
                     } else {
-                        infer(sps_subpic_height_minus1[i],
-                              tmp_height_val -
-                              current->sps_subpic_ctu_top_left_y[i] - 1);
+                        infer(sps_subpic_height_minus1[i], max_height_minus1);
                     }
                 } else {
                     int num_subpic_cols = tmp_width_val /
@@ -1245,8 +1243,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
         } else {
             infer(sps_subpic_ctu_top_left_x[0], 0);
             infer(sps_subpic_ctu_top_left_y[0], 0);
-            infer(sps_subpic_width_minus1[0], tmp_width_val - 1);
-            infer(sps_subpic_height_minus1[0], tmp_height_val - 1);
+            infer(sps_subpic_width_minus1[0], max_width_minus1);
+            infer(sps_subpic_height_minus1[0], max_height_minus1);
         }
         ue(sps_subpic_id_len_minus1, 0, 15);
         if ((1 << (current->sps_subpic_id_len_minus1 + 1)) <
@@ -1273,8 +1271,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
         infer(sps_subpic_id_mapping_explicitly_signalled_flag, 0);
         infer(sps_subpic_ctu_top_left_x[0], 0);
         infer(sps_subpic_ctu_top_left_y[0], 0);
-        infer(sps_subpic_width_minus1[0], tmp_width_val - 1);
-        infer(sps_subpic_height_minus1[0], tmp_height_val - 1);
+        infer(sps_subpic_width_minus1[0], max_width_minus1);
+        infer(sps_subpic_height_minus1[0], max_height_minus1);
     }
 
 
-- 
2.49.1


>From 8a33c1f7d98cab9f935eff81431aec5ac93f9439 Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Sat, 23 Nov 2024 17:32:34 +0800
Subject: [PATCH 08/24] avcodec/cbs_h266: stricter validation for subpicture's
 max width and height

Co-authored-by: Frank Plowman <[email protected]>
(cherry picked from commit 98698ed3c24bfd0b1e6e6db943b5f25f6046cee7)
---
 libavcodec/cbs_h266_syntax_template.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index 191e73edd5..bac7129f17 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -1147,11 +1147,11 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
             infer(sps_subpic_ctu_top_left_x[0], 0);
             infer(sps_subpic_ctu_top_left_y[0], 0);
             if (current->sps_pic_width_max_in_luma_samples > ctb_size_y)
-                ubs(wlen, sps_subpic_width_minus1[0], 1, 0);
+                us(wlen, sps_subpic_width_minus1[0], 0, max_width_minus1, 1, 
0);
             else
                 infer(sps_subpic_width_minus1[0], max_width_minus1);
             if (current->sps_pic_height_max_in_luma_samples > ctb_size_y)
-                ubs(hlen, sps_subpic_height_minus1[0], 1, 0);
+                us(hlen, sps_subpic_height_minus1[0], 0, max_width_minus1, 1, 
0);
             else
                 infer(sps_subpic_height_minus1[0], max_height_minus1);
             if (!current->sps_independent_subpics_flag) {
@@ -1201,14 +1201,14 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, 
RWContext *rw,
 
                     if (i < current->sps_num_subpics_minus1 &&
                         current->sps_pic_width_max_in_luma_samples > 
ctb_size_y) {
-                        us(wlen, sps_subpic_width_minus1[i], min_width, 
MAX_UINT_BITS(wlen), 1, i);
+                        us(wlen, sps_subpic_width_minus1[i], min_width, 
max_width_minus1, 1, i);
                     } else {
                         infer(sps_subpic_width_minus1[i], max_width_minus1);
                     }
 
                     if (i < current->sps_num_subpics_minus1 &&
                         current->sps_pic_height_max_in_luma_samples > 
ctb_size_y) {
-                        us(hlen, sps_subpic_height_minus1[i], min_height, 
MAX_UINT_BITS(hlen), 1, i);
+                        us(hlen, sps_subpic_height_minus1[i], min_height, 
max_height_minus1, 1, i);
                     } else {
                         infer(sps_subpic_height_minus1[i], max_height_minus1);
                     }
-- 
2.49.1


>From 49306d0e316b190ca53b23d3a76fa610af509ad0 Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Sat, 23 Nov 2024 17:32:38 +0800
Subject: [PATCH 09/24] avcodec/vvcdec: return error if CTU size > 128

The v3 spec reserves CTU size 256. Currently, we use an uint8_t* table to hold 
cb_width and cb_height.
If a CTU size of 256 is not split, cb_width and cb_height will overflow to 0.
To avoid switching to uint16_t, rejecting CTU size 256 provides a simple 
solution.

(cherry picked from commit 4de67e874697271e189022b03cd619329d54603c)
---
 libavcodec/vvc/ps.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index ff9a6c7a15..e5d854d899 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -649,6 +649,12 @@ static int decode_ps(VVCParamSets *ps, const 
CodedBitstreamH266Context *h266, vo
     if (ret < 0)
         return ret;
 
+    if (rsps->sps_log2_ctu_size_minus5 > 2) {
+        // CTU > 128 are reserved in vvc spec v3
+        av_log(log_ctx, AV_LOG_ERROR, "CTU size > 128. \n");
+        return AVERROR_PATCHWELCOME;
+    }
+
     ret = decode_pps(ps, rpps);
     if (ret < 0)
         return ret;
-- 
2.49.1


>From 839a452dbbfd6c504f37e894c8518b6ecf138908 Mon Sep 17 00:00:00 2001
From: Chris Warrington <[email protected]>
Date: Mon, 2 Dec 2024 14:27:12 +0000
Subject: [PATCH 10/24] avcodec/vvc decode: ALF filtering without CC-ALF

When a stream has ALF filtering enabled but not CC-ALF, the CC-ALF set indexes 
alf->ctb_cc_idc are being read uninitialized during ALF filtering.

This change initializes alf->ctb_cc_idc whenever ALF is enabled.

Ref. https://trac.ffmpeg.org/ticket/11325

(cherry picked from commit f80af3657f162ba79b872bc27ae06d75560d58a9)
---
 libavcodec/vvc/ctu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/vvc/ctu.c b/libavcodec/vvc/ctu.c
index 634c553814..764de25761 100644
--- a/libavcodec/vvc/ctu.c
+++ b/libavcodec/vvc/ctu.c
@@ -2289,6 +2289,7 @@ static void alf_params(VVCLocalContext *lc, const int rx, 
const int ry)
     ALFParams *alf                = &CTB(fc->tab.alf, rx, ry);
 
     alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0;
+    alf->ctb_cc_idc[0] = alf->ctb_cc_idc[1] = 0;
     if (sh->sh_alf_enabled_flag) {
         alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA);
         if (alf->ctb_flag[LUMA]) {
@@ -2319,7 +2320,6 @@ static void alf_params(VVCLocalContext *lc, const int rx, 
const int ry)
         const uint8_t cc_enabled[] = { sh->sh_alf_cc_cb_enabled_flag, 
sh->sh_alf_cc_cr_enabled_flag };
         const uint8_t cc_aps_id[]  = { sh->sh_alf_cc_cb_aps_id, 
sh->sh_alf_cc_cr_aps_id };
         for (int i = 0; i < 2; i++) {
-            alf->ctb_cc_idc[i] = 0;
             if (cc_enabled[i]) {
                 const VVCALF *aps = fc->ps.alf_list[cc_aps_id[i]];
                 alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, 
aps->num_cc_filters[i]);
-- 
2.49.1


>From bda6ce18542842f7c1c4289595c01bf9994ba7d3 Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Fri, 10 Jan 2025 21:29:07 +0000
Subject: [PATCH 11/24] lavc/vvc: Check slice structure

The criteria for slice structure validity is similar to that of
subpicture structure validity that we saw not too long ago [1].
The relationship between tiles and slices must satisfy the following
properties:

* Exhaustivity.  All tiles in a picture must belong to a slice.  The
  tiles cover the picture, so this implies the slices must cover the
  picture.
* Mutual exclusivity.  No tile may belong to more than one slice, i.e.
  slices may not overlap.

In most cases these properties are guaranteed by the syntax.  There is
one noticable exception however: when pps_tile_idx_delta_present_flag is
equal to one, each slice is associated with a syntax element
pps_tile_idx_delta_val[i] which "specifies the difference between the
tile index of the tile containing the first CTU in the ( i + 1 )-th
rectangular slice and the tile index of the tile containing the first
CTU in the i-th rectangular slice" [2].  When these syntax elements are
present, the i-th slice can begin anywhere and the usual guarantees
provided by the syntax are lost.

The patch detects slice structures which violate either of the two
properties above, and are therefore invalid, while building the
slice map.  Should the slice map be determined to be invalid, an
AVERROR_INVALIDDATA is returned.  This prevents issues including
segmentation faults when trying to decode,  invalid bitstreams.

[1]: https://ffmpeg.org//pipermail/ffmpeg-devel/2024-October/334470.html
[2]: H.266 (V3) Section 7.4.3.5, Picture parameter set RBSP semantics

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit 8bd66a8c9587af61c7b46558be3c4ee317c1af5a)
---
 libavcodec/vvc/ps.c | 34 +++++++++++++++++++++++++++-------
 1 file changed, 27 insertions(+), 7 deletions(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index e5d854d899..66f14a059c 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -20,6 +20,7 @@
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
+#include <stdbool.h>
 
 #include "libavcodec/cbs_h266.h"
 #include "libavutil/mem.h"
@@ -460,7 +461,7 @@ static int pps_one_tile_slices(VVCPPS *pps, const int 
tile_idx, int i, int *off)
     return i;
 }
 
-static void pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int 
i, int *off)
+static int pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, 
int *off, bool *tile_in_slice)
 {
     const H266RawPPS *r = pps->r;
     int rx, ry, tile_x, tile_y;
@@ -470,32 +471,51 @@ static void pps_multi_tiles_slice(VVCPPS *pps, const int 
tile_idx, const int i,
     pps->num_ctus_in_slice[i] = 0;
     for (int ty = tile_y; ty <= tile_y + 
r->pps_slice_height_in_tiles_minus1[i]; ty++) {
         for (int tx = tile_x; tx <= tile_x + 
r->pps_slice_width_in_tiles_minus1[i]; tx++) {
+            const int idx = ty * r->num_tile_columns + tx;
+            if (tile_in_slice[idx])
+                return AVERROR_INVALIDDATA;
+            tile_in_slice[idx] = true;
             ctu_xy(&rx, &ry, tx, ty, pps);
             pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, rx, ry,
                 r->col_width_val[tx], r->row_height_val[ty]);
         }
     }
+
+    return 0;
 }
 
-static void pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
+static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
 {
     const H266RawPPS *r = pps->r;
+    bool tile_in_slice[VVC_MAX_TILES_PER_AU] = {false};
     int tile_idx = 0, off = 0;
 
     if (r->pps_single_slice_per_subpic_flag) {
         pps_single_slice_per_subpic(pps, sps, &off);
-        return;
+        return 0;
     }
 
     for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) {
         if (!r->pps_slice_width_in_tiles_minus1[i] &&
             !r->pps_slice_height_in_tiles_minus1[i]) {
+            if (tile_in_slice[tile_idx])
+                return AVERROR_INVALIDDATA;
+            tile_in_slice[tile_idx] = true;
             i = pps_one_tile_slices(pps, tile_idx, i, &off);
         } else {
-            pps_multi_tiles_slice(pps, tile_idx, i, &off);
+            const int ret = pps_multi_tiles_slice(pps, tile_idx, i, &off, 
tile_in_slice);
+            if (ret < 0)
+                return ret;
         }
         tile_idx = next_tile_idx(tile_idx, i, r);
     }
+
+    for (int i = 0; i < r->num_tiles_in_pic; i++) {
+        if (!tile_in_slice[i])
+            return AVERROR_INVALIDDATA;
+    }
+
+    return 0;
 }
 
 static void pps_no_rect_slice(VVCPPS* pps)
@@ -518,9 +538,9 @@ static int pps_slice_map(VVCPPS *pps, const VVCSPS *sps)
         return AVERROR(ENOMEM);
 
     if (pps->r->pps_rect_slice_flag)
-        pps_rect_slice(pps, sps);
-    else
-        pps_no_rect_slice(pps);
+        return pps_rect_slice(pps, sps);
+
+    pps_no_rect_slice(pps);
 
     return 0;
 }
-- 
2.49.1


>From 78bd48ece9f9a117b1338f59891ab73d269af2d7 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Sun, 19 Jan 2025 00:35:06 +0800
Subject: [PATCH 12/24] avcodec/vvc: Add support for output_corrupt/showall
 flags

(cherry picked from commit ea381285e7d9c5fd3f74e19bc699ca98bd653ca5)
---
 libavcodec/vvc/refs.c | 21 ++++++++++++++++++++-
 libavcodec/vvc/refs.h |  1 +
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/libavcodec/vvc/refs.c b/libavcodec/vvc/refs.c
index 358e11242a..c9cf261355 100644
--- a/libavcodec/vvc/refs.c
+++ b/libavcodec/vvc/refs.c
@@ -21,6 +21,7 @@
  */
 
 #include <stdatomic.h>
+#include <stdbool.h>
 
 #include "libavutil/mem.h"
 #include "libavutil/thread.h"
@@ -46,6 +47,8 @@ void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, 
int flags)
         return;
 
     frame->flags &= ~flags;
+    if (!(frame->flags & ~VVC_FRAME_FLAG_CORRUPT))
+        frame->flags = 0;
     if (!frame->flags) {
         av_frame_unref(frame->frame);
         ff_refstruct_unref(&frame->sps);
@@ -242,6 +245,9 @@ int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, 
AVFrame *out, const
         if (nb_output) {
             VVCFrame *frame = &fc->DPB[min_idx];
 
+            if (frame->flags & VVC_FRAME_FLAG_CORRUPT)
+                frame->frame->flags |= AV_FRAME_FLAG_CORRUPT;
+
             ret = av_frame_ref(out, frame->frame);
             if (frame->flags & VVC_FRAME_FLAG_BUMPING)
                 ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT | 
VVC_FRAME_FLAG_BUMPING);
@@ -351,7 +357,7 @@ static VVCFrame *generate_missing_ref(VVCContext *s, 
VVCFrameContext *fc, int po
 
     frame->poc      = poc;
     frame->sequence = s->seq_decode;
-    frame->flags    = 0;
+    frame->flags    = VVC_FRAME_FLAG_CORRUPT;
 
     ff_vvc_report_frame_finished(frame);
 
@@ -386,6 +392,19 @@ static int add_candidate_ref(VVCContext *s, 
VVCFrameContext *fc, RefPicList *lis
     if (ref == fc->ref || list->nb_refs >= VVC_MAX_REF_ENTRIES)
         return AVERROR_INVALIDDATA;
 
+    if (!IS_CVSS(s)) {
+        const bool ref_corrupt = !ref || (ref->flags & VVC_FRAME_FLAG_CORRUPT);
+        const bool recovering  = s->no_output_before_recovery_flag && 
!GDR_IS_RECOVERED(s);
+
+        if (ref_corrupt && !recovering) {
+            if (!(s->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) &&
+                !(s->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL))
+                return AVERROR_INVALIDDATA;
+
+            fc->ref->flags |= VVC_FRAME_FLAG_CORRUPT;
+        }
+    }
+
     if (!ref) {
         ref = generate_missing_ref(s, fc, poc);
         if (!ref)
diff --git a/libavcodec/vvc/refs.h b/libavcodec/vvc/refs.h
index e2271ab381..a3081a76be 100644
--- a/libavcodec/vvc/refs.h
+++ b/libavcodec/vvc/refs.h
@@ -29,6 +29,7 @@
 #define VVC_FRAME_FLAG_SHORT_REF (1 << 1)
 #define VVC_FRAME_FLAG_LONG_REF  (1 << 2)
 #define VVC_FRAME_FLAG_BUMPING   (1 << 3)
+#define VVC_FRAME_FLAG_CORRUPT   (1 << 4)
 
 int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, struct AVFrame 
*out, int no_output_of_prior_pics_flag, int flush);
 void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc);
-- 
2.49.1


>From 056680ed263aaa36550831b2d54049dd918d79c0 Mon Sep 17 00:00:00 2001
From: Nuo Mi <[email protected]>
Date: Sun, 26 Jan 2025 11:10:21 +0800
Subject: [PATCH 13/24] lavc/vvcdec: ensure slices contain nonzero CTUs

fixes https://github.com/ffvvc/tests/tree/main/fuzz/passed/000323.bit

Co-authored-by: Frank Plowman <[email protected]>
(cherry picked from commit ca3550948c4f1d9fe7dbdc24572b92e588846c7b)
---
 libavcodec/vvc/ps.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 66f14a059c..cca6b413e5 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -1224,7 +1224,7 @@ static int sh_alf_aps(const VVCSH *sh, const 
VVCFrameParamSets *fps)
     return 0;
 }
 
-static void sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS 
*pps)
+static int sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS 
*pps)
 {
     const int slice_address     = sh->r->sh_slice_address;
 
@@ -1248,6 +1248,11 @@ static void sh_slice_address(VVCSH *sh, const H266RawSPS 
*sps, const VVCPPS *pps
             sh->num_ctus_in_curr_slice += pps->r->row_height_val[tile_y] * 
pps->r->col_width_val[tile_x];
         }
     }
+
+    if (!sh->num_ctus_in_curr_slice)
+        return  AVERROR_INVALIDDATA;
+
+    return 0;
 }
 
 static void sh_qp_y(VVCSH *sh, const H266RawPPS *pps, const 
H266RawPictureHeader *ph)
@@ -1344,7 +1349,9 @@ static int sh_derive(VVCSH *sh, const VVCFrameParamSets 
*fps)
     const H266RawPictureHeader *ph  = fps->ph.r;
     int ret;
 
-    sh_slice_address(sh, sps, fps->pps);
+    ret = sh_slice_address(sh, sps, fps->pps);
+    if (ret < 0)
+        return ret;
     ret = sh_alf_aps(sh, fps);
     if (ret < 0)
         return ret;
-- 
2.49.1


>From 223fee5bb67b30eb1f1f9aaa930c38c7f9a4e438 Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sat, 8 Feb 2025 21:42:56 +0000
Subject: [PATCH 14/24] lavc/vvc: Set fc->ref to NULL at top of
 decode_nal_units

In the fail: block of decode_nal_units, a check as to whether fc->ref is
nonzero is used.  Before this patch, fc->ref was set to NULL in
frame_context_setup.  The issue is that, by the time frame_context_setup
is called, falliable functions (namely slices_realloc and
ff_vvc_decode_frame_ps) have already been called.  Therefore, there
could arise a situation in which the fc->ref test of decode_nal_units'
fail: block is performed while fc->ref has an invalid value.  This seems
to be particularly prevalent in situations where the FrameContexts are
being reused.  The patch resolves the issue by moving the assignment of
fc->ref to NULL to the very top of decode_nal_units, before any falliable
functions are called.

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit e417f939da2d04abfe6ad1f93aa47be334b66771)
---
 libavcodec/vvc/dec.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libavcodec/vvc/dec.c b/libavcodec/vvc/dec.c
index 522fa8416e..6fefa0deec 100644
--- a/libavcodec/vvc/dec.c
+++ b/libavcodec/vvc/dec.c
@@ -672,8 +672,6 @@ static int frame_context_setup(VVCFrameContext *fc, 
VVCContext *s)
 {
     int ret;
 
-    fc->ref = NULL;
-
     // copy refs from the last frame
     if (s->nb_frames && s->nb_fcs > 1) {
         VVCFrameContext *prev = get_frame_context(s, fc, -1);
@@ -877,6 +875,7 @@ static int decode_nal_units(VVCContext *s, VVCFrameContext 
*fc, AVPacket *avpkt)
     int ret = 0;
     s->last_eos = s->eos;
     s->eos = 0;
+    fc->ref = NULL;
 
     ff_cbs_fragment_reset(frame);
     ret = ff_cbs_read_packet(s->cbc, frame, avpkt);
-- 
2.49.1


>From b844985520fedd26b2bce5237aff4c62123b68ae Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sun, 2 Feb 2025 12:10:48 +0000
Subject: [PATCH 15/24] lavc/vvc: Fix derivation of inverse LMCS idx

The clamping of idxYInv from H.266(V3) section 8.8.2.3 was missing.
This could lead to OOB reads from lmcs->pivot or input_pivot.

I also changed the derivation of the forward LMCS idx to use a shift
rather than a division for speed and as this is actually how the
variable is declared in the specification (8.7.5.2).

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit 392aa9daf2c730465f6aba5ba2b37e02695de9f7)
---
 libavcodec/vvc/ps.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index cca6b413e5..3b972be945 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -786,7 +786,7 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS 
*rlmcs, const H266Raw
 
     //derive lmcs_fwd_lut
     for (uint16_t sample = 0; sample < max; sample++) {
-        const int idx_y = sample / org_cw;
+        const int idx_y = sample >> shift;
         const uint16_t fwd_sample = lmcs_derive_lut_sample(sample, lmcs->pivot,
             input_pivot, scale_coeff, idx_y, max);
         if (bit_depth > 8)
@@ -802,6 +802,7 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS 
*rlmcs, const H266Raw
         uint16_t inv_sample;
         while (i <= lmcs->max_bin_idx && sample >= lmcs->pivot[i + 1])
             i++;
+        i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1);
 
         inv_sample = lmcs_derive_lut_sample(sample, input_pivot, lmcs->pivot,
             inv_scale_coeff, i, max);
-- 
2.49.1


>From 71706b76150c7d24ad0f3c10e21eb6ba5423c7be Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sat, 22 Feb 2025 15:51:54 +0800
Subject: [PATCH 16/24] lavc/vvc: Fix slice map construction for small subpics

In the case pps_subpic_less_than_one_tile_slice is called, the
subpicture is smaller than the tile and so there are multiple
subpictures in the tile.  Of course, then, not all the
subpictures can start in the top-left corner as the code before the
patch does.  Patch fixes this, so each subpicture starts at the
signalled location as is specified in section 6.5.1 of H.266(V3).

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit 93aae172ea909ec96b67077b8b3005886a83d0ae)
---
 libavcodec/vvc/ps.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 3b972be945..0cc02443da 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -404,8 +404,8 @@ static void subpic_tiles(int *tile_x, int *tile_y, int 
*tile_x_end, int *tile_y_
 static void pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS 
*sps, const int i, const int tx, const int ty, int *off)
 {
     pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off,
-        pps->col_bd[tx], pps->row_bd[ty],
-        pps->r->col_width_val[tx], sps->r->sps_subpic_height_minus1[i] + 1);
+        sps->r->sps_subpic_ctu_top_left_x[i], 
sps->r->sps_subpic_ctu_top_left_y[i],
+        sps->r->sps_subpic_width_minus1[i] + 1, 
sps->r->sps_subpic_height_minus1[i] + 1);
 }
 
 static void pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, 
const int tile_y, const int x_end, const int y_end, const int i, int *off)
-- 
2.49.1


>From c94f4aadd6e21dfe7952e09b35f16fe58d655b71 Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sat, 22 Feb 2025 15:51:55 +0800
Subject: [PATCH 17/24] lavc/vvc: Ensure subpictures don't overlap

This is essentially a re-implementation of
https://patchwork.ffmpeg.org/project/ffmpeg/patch/[email protected]/

That patch was not applied last time.  Instead we opted to identify
issues which could be caused by invalid subpicture layouts and remedy
those issues where they manifest, either through error detection or code
hardening.  This was primarily implemented in the set
https://patchwork.ffmpeg.org/project/ffmpeg/list/?series=13381.

This has worked to some degree, however issues with subpicture layouts
continue to crop up from the fuzzer and I've fixed a number of bugs
related to subpicture layouts since then.  I think it's best to return
to the initial plan and simply check if the subpicture layout is valid
initially.

This implementation is also lighter than the first time -- by doing a
bit more logic in pps_subpic_less_than_one_tile_slice, we are able to
store a tile_in_subpic map rather than a ctu_in_subpic map.  This
reduces the size of the map to the point it becomes possible to allocate
it on the stack.  Similar to 8bd66a8c9587af61c7b46558be3c4ee317c1af5a,
the layout is also validated in the slice map construction code, rather
than in the CBS, which avoids duplicating some logic.

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit d5dbcc00d889fb17948b025a468b00ddbea9e058)
---
 libavcodec/vvc/ps.c | 55 ++++++++++++++++++++++++++++++++++++---------
 1 file changed, 45 insertions(+), 10 deletions(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 0cc02443da..01b4d69ceb 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -401,25 +401,50 @@ static void subpic_tiles(int *tile_x, int *tile_y, int 
*tile_x_end, int *tile_y_
         (*tile_y_end)++;
 }
 
-static void pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS 
*sps, const int i, const int tx, const int ty, int *off)
+static bool mark_tile_as_used(bool *tile_in_subpic, const int tx, const int 
ty, const int tile_columns)
 {
+    const size_t tile_idx = ty * tile_columns + tx;
+    if (tile_in_subpic[tile_idx]) {
+        /* the tile is covered by other subpictures */
+        return false;
+    }
+    tile_in_subpic[tile_idx] = true;
+    return true;
+}
+
+static int pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, 
const int i, const int tx, const int ty, int *off, bool *tile_in_subpic)
+{
+    const int subpic_bottom = sps->r->sps_subpic_ctu_top_left_y[i] + 
sps->r->sps_subpic_height_minus1[i];
+    const int tile_bottom = pps->row_bd[ty] + pps->r->row_height_val[ty] - 1;
+    const bool is_final_subpic_in_tile = subpic_bottom == tile_bottom;
+
+    if (is_final_subpic_in_tile && !mark_tile_as_used(tile_in_subpic, tx, ty, 
pps->r->num_tile_columns))
+        return AVERROR_INVALIDDATA;
+
     pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off,
         sps->r->sps_subpic_ctu_top_left_x[i], 
sps->r->sps_subpic_ctu_top_left_y[i],
         sps->r->sps_subpic_width_minus1[i] + 1, 
sps->r->sps_subpic_height_minus1[i] + 1);
+
+    return 0;
 }
 
-static void pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, 
const int tile_y, const int x_end, const int y_end, const int i, int *off)
+static int pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, 
const int tile_y, const int x_end, const int y_end,
+    const int i, int *off, bool *tile_in_subpic)
 {
     for (int ty = tile_y; ty < y_end; ty++) {
         for (int tx = tile_x; tx < x_end; tx++) {
+            if (!mark_tile_as_used(tile_in_subpic, tx, ty, 
pps->r->num_tile_columns))
+                return AVERROR_INVALIDDATA;
+
             pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off,
                 pps->col_bd[tx], pps->row_bd[ty],
                 pps->r->col_width_val[tx], pps->r->row_height_val[ty]);
         }
     }
+    return 0;
 }
 
-static void pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int 
*off)
+static int pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int 
*off, bool *tile_in_subpic)
 {
     int tx, ty, x_end, y_end;
 
@@ -428,19 +453,30 @@ static void pps_subpic_slice(VVCPPS *pps, const VVCSPS 
*sps, const int i, int *o
 
     subpic_tiles(&tx, &ty, &x_end, &y_end, sps, pps, i);
     if (ty + 1 == y_end && sps->r->sps_subpic_height_minus1[i] + 1 < 
pps->r->row_height_val[ty])
-        pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off);
+        return pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off, 
tile_in_subpic);
     else
-        pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, i, off);
+        return pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, 
i, off, tile_in_subpic);
 }
 
-static void pps_single_slice_per_subpic(VVCPPS *pps, const VVCSPS *sps, int 
*off)
+static int pps_single_slice_per_subpic(VVCPPS *pps, const VVCSPS *sps, int 
*off)
 {
     if (!sps->r->sps_subpic_info_present_flag) {
         pps_single_slice_picture(pps, off);
     } else {
-        for (int i = 0; i < pps->r->pps_num_slices_in_pic_minus1 + 1; i++)
-            pps_subpic_slice(pps, sps, i, off);
+        bool tile_in_subpic[VVC_MAX_TILES_PER_AU] = {0};
+        for (int i = 0; i < pps->r->pps_num_slices_in_pic_minus1 + 1; i++) {
+            const int ret = pps_subpic_slice(pps, sps, i, off, tile_in_subpic);
+            if (ret < 0)
+                return ret;
+        }
+
+        // We only use tile_in_subpic to check that the subpictures don't 
overlap
+        // here; we don't use tile_in_subpic to check that the subpictures 
cover
+        // every tile.  It is possible to avoid doing this work here because 
the
+        // covering property of subpictures is already guaranteed by the 
mechanisms
+        // which check every CTU belongs to a slice.
     }
+    return 0;
 }
 
 static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int 
*off)
@@ -491,8 +527,7 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
     int tile_idx = 0, off = 0;
 
     if (r->pps_single_slice_per_subpic_flag) {
-        pps_single_slice_per_subpic(pps, sps, &off);
-        return 0;
+        return pps_single_slice_per_subpic(pps, sps, &off);
     }
 
     for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) {
-- 
2.49.1


>From e2af7ee96d31056681e0839e04b8624479d68b84 Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sun, 23 Feb 2025 12:04:48 +0000
Subject: [PATCH 18/24] lavc/vvc: Fix pps_single_slice_picture

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit 600ad36949a70c0187aa671469ebd81212528d61)
---
 libavcodec/vvc/ps.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 01b4d69ceb..7ed54344c0 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -368,9 +368,10 @@ static int pps_add_ctus(VVCPPS *pps, int *off, const int 
rx, const int ry,
 
 static void pps_single_slice_picture(VVCPPS *pps, int *off)
 {
+    pps->num_ctus_in_slice[0] = 0;
     for (int j = 0; j < pps->r->num_tile_rows; j++) {
         for (int i = 0; i < pps->r->num_tile_columns; i++) {
-            pps->num_ctus_in_slice[0] = pps_add_ctus(pps, off,
+            pps->num_ctus_in_slice[0] += pps_add_ctus(pps, off,
                 pps->col_bd[i], pps->row_bd[j],
                 pps->r->col_width_val[i], pps->r->row_height_val[j]);
         }
-- 
2.49.1


>From 28f72f1f7da29f084e40803795f015f7ef3f67aa Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sun, 18 May 2025 14:13:26 +0100
Subject: [PATCH 19/24] lavc/vvc: Detect subpic overlaps at CTU level

In d5dbcc00d889fb17948b025a468b00ddbea9e058, it was hoped that detection
of subpicture overlaps could be performed at the tile level, so as to
avoid introducing per-CTU checks. Unfortunately since that patch,
fuzzing has indicated there are some structures involving
pps_subpic_one_or_more_tiles_slice where tile-level checking is not
sufficient.  Performing the check at the CTU level should (touch wood)
be the be-all and and-all of this, as CTUs are the lowest common
denominator of the picture partitioning.

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit a18b2c26962480de3aa2df60c2abe80742c0d9ed)
---
 libavcodec/vvc/ps.c | 104 ++++++++++++++++++++++++--------------------
 1 file changed, 57 insertions(+), 47 deletions(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 7ed54344c0..0312f37503 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -359,6 +359,8 @@ static int pps_add_ctus(VVCPPS *pps, int *off, const int 
rx, const int ry,
     int start = *off;
     for (int y = 0; y < h; y++) {
         for (int x = 0; x < w; x++) {
+            if (*off >= pps->ctb_count)
+                return AVERROR_INVALIDDATA;
             pps->ctb_addr_in_slice[*off] = ctu_rs(rx + x, ry + y, pps);
             (*off)++;
         }
@@ -366,16 +368,21 @@ static int pps_add_ctus(VVCPPS *pps, int *off, const int 
rx, const int ry,
     return *off - start;
 }
 
-static void pps_single_slice_picture(VVCPPS *pps, int *off)
+static int pps_single_slice_picture(VVCPPS *pps, int *off)
 {
     pps->num_ctus_in_slice[0] = 0;
     for (int j = 0; j < pps->r->num_tile_rows; j++) {
         for (int i = 0; i < pps->r->num_tile_columns; i++) {
-            pps->num_ctus_in_slice[0] += pps_add_ctus(pps, off,
+            const int ret = pps_add_ctus(pps, off,
                 pps->col_bd[i], pps->row_bd[j],
                 pps->r->col_width_val[i], pps->r->row_height_val[j]);
+            if (ret < 0)
+                return ret;
+            pps->num_ctus_in_slice[0] += ret;
         }
     }
+
+    return 0;
 }
 
 static void subpic_tiles(int *tile_x, int *tile_y, int *tile_x_end, int 
*tile_y_end,
@@ -402,50 +409,36 @@ static void subpic_tiles(int *tile_x, int *tile_y, int 
*tile_x_end, int *tile_y_
         (*tile_y_end)++;
 }
 
-static bool mark_tile_as_used(bool *tile_in_subpic, const int tx, const int 
ty, const int tile_columns)
+static int pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, 
const int i, const int tx, const int ty, int *off)
 {
-    const size_t tile_idx = ty * tile_columns + tx;
-    if (tile_in_subpic[tile_idx]) {
-        /* the tile is covered by other subpictures */
-        return false;
-    }
-    tile_in_subpic[tile_idx] = true;
-    return true;
-}
-
-static int pps_subpic_less_than_one_tile_slice(VVCPPS *pps, const VVCSPS *sps, 
const int i, const int tx, const int ty, int *off, bool *tile_in_subpic)
-{
-    const int subpic_bottom = sps->r->sps_subpic_ctu_top_left_y[i] + 
sps->r->sps_subpic_height_minus1[i];
-    const int tile_bottom = pps->row_bd[ty] + pps->r->row_height_val[ty] - 1;
-    const bool is_final_subpic_in_tile = subpic_bottom == tile_bottom;
-
-    if (is_final_subpic_in_tile && !mark_tile_as_used(tile_in_subpic, tx, ty, 
pps->r->num_tile_columns))
-        return AVERROR_INVALIDDATA;
-
-    pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off,
+    const int ret = pps_add_ctus(pps, off,
         sps->r->sps_subpic_ctu_top_left_x[i], 
sps->r->sps_subpic_ctu_top_left_y[i],
         sps->r->sps_subpic_width_minus1[i] + 1, 
sps->r->sps_subpic_height_minus1[i] + 1);
+    if (ret < 0)
+        return ret;
 
+    pps->num_ctus_in_slice[i] = ret;
     return 0;
 }
 
 static int pps_subpic_one_or_more_tiles_slice(VVCPPS *pps, const int tile_x, 
const int tile_y, const int x_end, const int y_end,
-    const int i, int *off, bool *tile_in_subpic)
+    const int i, int *off)
 {
     for (int ty = tile_y; ty < y_end; ty++) {
         for (int tx = tile_x; tx < x_end; tx++) {
-            if (!mark_tile_as_used(tile_in_subpic, tx, ty, 
pps->r->num_tile_columns))
-                return AVERROR_INVALIDDATA;
-
-            pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off,
+            const int ret = pps_add_ctus(pps, off,
                 pps->col_bd[tx], pps->row_bd[ty],
                 pps->r->col_width_val[tx], pps->r->row_height_val[ty]);
+            if (ret < 0)
+                return ret;
+
+            pps->num_ctus_in_slice[i] += ret;
         }
     }
     return 0;
 }
 
-static int pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int 
*off, bool *tile_in_subpic)
+static int pps_subpic_slice(VVCPPS *pps, const VVCSPS *sps, const int i, int 
*off)
 {
     int tx, ty, x_end, y_end;
 
@@ -454,28 +447,25 @@ static int pps_subpic_slice(VVCPPS *pps, const VVCSPS 
*sps, const int i, int *of
 
     subpic_tiles(&tx, &ty, &x_end, &y_end, sps, pps, i);
     if (ty + 1 == y_end && sps->r->sps_subpic_height_minus1[i] + 1 < 
pps->r->row_height_val[ty])
-        return pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off, 
tile_in_subpic);
+        return pps_subpic_less_than_one_tile_slice(pps, sps, i, tx, ty, off);
     else
-        return pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, 
i, off, tile_in_subpic);
+        return pps_subpic_one_or_more_tiles_slice(pps, tx, ty, x_end, y_end, 
i, off);
 }
 
 static int pps_single_slice_per_subpic(VVCPPS *pps, const VVCSPS *sps, int 
*off)
 {
+    int ret;
+
     if (!sps->r->sps_subpic_info_present_flag) {
-        pps_single_slice_picture(pps, off);
+        ret = pps_single_slice_picture(pps, off);
+        if (ret < 0)
+            return ret;
     } else {
-        bool tile_in_subpic[VVC_MAX_TILES_PER_AU] = {0};
         for (int i = 0; i < pps->r->pps_num_slices_in_pic_minus1 + 1; i++) {
-            const int ret = pps_subpic_slice(pps, sps, i, off, tile_in_subpic);
+            const int ret = pps_subpic_slice(pps, sps, i, off);
             if (ret < 0)
                 return ret;
         }
-
-        // We only use tile_in_subpic to check that the subpictures don't 
overlap
-        // here; we don't use tile_in_subpic to check that the subpictures 
cover
-        // every tile.  It is possible to avoid doing this work here because 
the
-        // covering property of subpictures is already guaranteed by the 
mechanisms
-        // which check every CTU belongs to a slice.
     }
     return 0;
 }
@@ -489,9 +479,13 @@ static int pps_one_tile_slices(VVCPPS *pps, const int 
tile_idx, int i, int *off)
     ctu_xy(&rx, &ry, tile_x, tile_y, pps);
     ctu_y_end = ry + r->row_height_val[tile_y];
     while (ry < ctu_y_end) {
+        int ret;
         pps->slice_start_offset[i] = *off;
-        pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off, rx, ry,
+        ret = pps_add_ctus(pps, off, rx, ry,
             r->col_width_val[tile_x], r->slice_height_in_ctus[i]);
+        if (ret < 0)
+            return ret;
+        pps->num_ctus_in_slice[i] = ret;
         ry += r->slice_height_in_ctus[i++];
     }
     i--;
@@ -508,13 +502,17 @@ static int pps_multi_tiles_slice(VVCPPS *pps, const int 
tile_idx, const int i, i
     pps->num_ctus_in_slice[i] = 0;
     for (int ty = tile_y; ty <= tile_y + 
r->pps_slice_height_in_tiles_minus1[i]; ty++) {
         for (int tx = tile_x; tx <= tile_x + 
r->pps_slice_width_in_tiles_minus1[i]; tx++) {
+            int ret;
             const int idx = ty * r->num_tile_columns + tx;
             if (tile_in_slice[idx])
                 return AVERROR_INVALIDDATA;
             tile_in_slice[idx] = true;
             ctu_xy(&rx, &ry, tx, ty, pps);
-            pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, rx, ry,
+            ret = pps_add_ctus(pps, off, rx, ry,
                 r->col_width_val[tx], r->row_height_val[ty]);
+            if (ret < 0)
+                return ret;
+            pps->num_ctus_in_slice[i] += ret;
         }
     }
 
@@ -525,7 +523,7 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
 {
     const H266RawPPS *r = pps->r;
     bool tile_in_slice[VVC_MAX_TILES_PER_AU] = {false};
-    int tile_idx = 0, off = 0;
+    int tile_idx = 0, off = 0, ret;
 
     if (r->pps_single_slice_per_subpic_flag) {
         return pps_single_slice_per_subpic(pps, sps, &off);
@@ -537,9 +535,12 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
             if (tile_in_slice[tile_idx])
                 return AVERROR_INVALIDDATA;
             tile_in_slice[tile_idx] = true;
-            i = pps_one_tile_slices(pps, tile_idx, i, &off);
+            ret = pps_one_tile_slices(pps, tile_idx, i, &off);
+            if (ret < 0)
+                return ret;
+            i = ret;
         } else {
-            const int ret = pps_multi_tiles_slice(pps, tile_idx, i, &off, 
tile_in_slice);
+            ret = pps_multi_tiles_slice(pps, tile_idx, i, &off, tile_in_slice);
             if (ret < 0)
                 return ret;
         }
@@ -554,21 +555,28 @@ static int pps_rect_slice(VVCPPS *pps, const VVCSPS *sps)
     return 0;
 }
 
-static void pps_no_rect_slice(VVCPPS* pps)
+static int pps_no_rect_slice(VVCPPS* pps)
 {
     const H266RawPPS* r = pps->r;
     int rx, ry, off = 0;
 
     for (int tile_y = 0; tile_y < r->num_tile_rows; tile_y++) {
         for (int tile_x = 0; tile_x < r->num_tile_columns; tile_x++) {
+            int ret;
             ctu_xy(&rx, &ry, tile_x, tile_y, pps);
-            pps_add_ctus(pps, &off, rx, ry, r->col_width_val[tile_x], 
r->row_height_val[tile_y]);
+            ret = pps_add_ctus(pps, &off, rx, ry, r->col_width_val[tile_x], 
r->row_height_val[tile_y]);
+            if (ret < 0)
+                return ret;
         }
     }
+
+    return 0;
 }
 
 static int pps_slice_map(VVCPPS *pps, const VVCSPS *sps)
 {
+    int ret;
+
     pps->ctb_addr_in_slice = av_calloc(pps->ctb_count, 
sizeof(*pps->ctb_addr_in_slice));
     if (!pps->ctb_addr_in_slice)
         return AVERROR(ENOMEM);
@@ -576,7 +584,9 @@ static int pps_slice_map(VVCPPS *pps, const VVCSPS *sps)
     if (pps->r->pps_rect_slice_flag)
         return pps_rect_slice(pps, sps);
 
-    pps_no_rect_slice(pps);
+    ret = pps_no_rect_slice(pps);
+    if (ret < 0)
+        return ret;
 
     return 0;
 }
-- 
2.49.1


>From 31d07bd35e5a62905534520393296a7ee81081ea Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sat, 24 May 2025 13:12:25 +0100
Subject: [PATCH 20/24] lavc/vvc: Fix divide-by-zero in LMCS param derivation

Add three missing requirements on bitstream conformance from 7.4.3.19 of
H.266 (V3).  Issue found using fuzzing.

Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit 0382291811ef69e465c096ddf509e0b1e180a5f1)
---
 libavcodec/vvc/ps.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 0312f37503..032cc3e853 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -800,7 +800,7 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS 
*rlmcs, const H266Raw
     uint16_t input_pivot[LMCS_MAX_BIN_SIZE];
     uint16_t scale_coeff[LMCS_MAX_BIN_SIZE];
     uint16_t inv_scale_coeff[LMCS_MAX_BIN_SIZE];
-    int i, delta_crs;
+    int i, delta_crs, sum_cw = 0;
     if (bit_depth > LMCS_MAX_BIT_DEPTH)
         return AVERROR_PATCHWELCOME;
 
@@ -811,8 +811,12 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS 
*rlmcs, const H266Raw
     lmcs->max_bin_idx = LMCS_MAX_BIN_SIZE - 1 - rlmcs->lmcs_min_bin_idx;
 
     memset(cw, 0, sizeof(cw));
-    for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++)
+    for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) {
         cw[i] = org_cw + (1 - 2 * rlmcs->lmcs_delta_sign_cw_flag[i]) * 
rlmcs->lmcs_delta_abs_cw[i];
+        sum_cw += cw[i];
+    }
+    if (sum_cw > (1 << bit_depth) - 1)
+        return AVERROR_INVALIDDATA;
 
     delta_crs = (1 - 2 * rlmcs->lmcs_delta_sign_crs_flag) * 
rlmcs->lmcs_delta_abs_crs;
 
@@ -820,13 +824,20 @@ static int lmcs_derive_lut(VVCLMCS *lmcs, const 
H266RawAPS *rlmcs, const H266Raw
     for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) {
         input_pivot[i]        = i * org_cw;
         lmcs->pivot[i + 1] = lmcs->pivot[i] + cw[i];
+        if (i >= lmcs->min_bin_idx && i <= lmcs->max_bin_idx &&
+            lmcs->pivot[i] % (1 << (bit_depth - 5)) != 0 &&
+            lmcs->pivot[i] >> (bit_depth - 5) == lmcs->pivot[i + 1] >> 
(bit_depth - 5))
+            return AVERROR_INVALIDDATA;
         scale_coeff[i]        = (cw[i] * (1 << 11) +  off) >> shift;
         if (cw[i] == 0) {
             inv_scale_coeff[i] = 0;
             lmcs->chroma_scale_coeff[i] = (1 << 11);
         } else {
+            const int cw_plus_d = cw[i] + delta_crs;
+            if (cw_plus_d < (org_cw >> 3) || cw_plus_d > ((org_cw << 3) - 1))
+                return AVERROR_INVALIDDATA;
             inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i];
-            lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + 
delta_crs);
+            lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / cw_plus_d;
         }
     }
 
-- 
2.49.1


>From 10a61f27d906237200f3fd68172595fd522ec386 Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Sat, 21 Jun 2025 13:05:14 +0100
Subject: [PATCH 21/24] lavc/vvc: Fix condition for using default scaling
 factor

Add handling here for
sps_scaling_matrix_for_alternative_colour_space_disabled_flag.

Also add parentheses to make behaviour a little more explicit,
where &&'s precedence over || was relied on previously.

Reported-by: Michael Niedermayer <[email protected]>
Signed-off-by: Frank Plowman <[email protected]>
(cherry picked from commit 540a2497d2385f94a661a6bbe0f088636d972783)
---
 libavcodec/vvc/intra.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libavcodec/vvc/intra.c b/libavcodec/vvc/intra.c
index 41ed89c946..c08e79df7e 100644
--- a/libavcodec/vvc/intra.c
+++ b/libavcodec/vvc/intra.c
@@ -390,10 +390,10 @@ static const uint8_t* derive_scale_m(const 
VVCLocalContext *lc, const TransformB
     const int log2_matrix_size = (id < 2) ? 1 : (id < 8) ? 2 : 3;
     uint8_t *p = scale_m;
 
-    
av_assert0(!sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag);
-
     if (!rsh->sh_explicit_scaling_list_used_flag || tb->ts ||
-        sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && 
cu->apply_lfnst_flag[tb->c_idx])
+        (sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && 
cu->apply_lfnst_flag[tb->c_idx]) ||
+        (sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag 
&&
+            sps->r->sps_scaling_matrix_designated_colour_space_flag == 
cu->act_enabled_flag))
         return ff_vvc_default_scale_m;
 
     if (!sl) {
-- 
2.49.1


>From a4029dae4949b0ae3624895bd0d9209421098589 Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Tue, 29 Jul 2025 21:42:26 +0100
Subject: [PATCH 22/24] lavc/vvc: Mark SPS used if multiple CLVSSs use it

Consider the following sequence of NALUs (with some PPSs etc. omitted
for brevity):
1. SPS (ID=0, content=A)
2. IDR (SPS=0)
3. IDR (SPS=0)
4. SPS (ID=0, content=B)
5. TRAIL (SPS=0)

When decode_sps is called for NALU 3., ps->sps_id_used is cleared as
IDRs are one way of forming a CLVSS.  Then, old_sps is non-NULL
containing the result of calling decode_sps for NALU 2.  We haven't
received any SPSs between NALUs 2. and 3., therefore old_sps and rsps
are identical and the function returns.  The issue is that, at this
point, ps->sps_id_used is still zero despite the SPS being used for IDR
3.  This results in the check for conflicting SPSs not working properly
when decode_sps is called for NALU 5., allowing prediction between
pictures with different SPSs and probably all sorts of other
shenanigans.

Patch addresses the problem outlined above by also setting
ps->sps_id_used in the early return case.

(cherry picked from commit f82748d5e0320e33d2bc276517467bcf44b19ac4)
---
 libavcodec/vvc/ps.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vvc/ps.c b/libavcodec/vvc/ps.c
index 032cc3e853..cbbb7eb66f 100644
--- a/libavcodec/vvc/ps.c
+++ b/libavcodec/vvc/ps.c
@@ -239,9 +239,10 @@ static int decode_sps(VVCParamSets *ps, const H266RawSPS 
*rsps, void *log_ctx, i
     }
 
     if (old_sps) {
-        if (old_sps->r == rsps || !memcmp(old_sps->r, rsps, 
sizeof(*old_sps->r)))
+        if (old_sps->r == rsps || !memcmp(old_sps->r, rsps, 
sizeof(*old_sps->r))) {
+            ps->sps_id_used |= (1 << sps_id);
             return 0;
-        else if (ps->sps_id_used & (1 << sps_id))
+        } else if (ps->sps_id_used & (1 << sps_id))
             return AVERROR_INVALIDDATA;
     }
 
-- 
2.49.1


>From e21fb0a3e844fc27c709ad8ff66932ecf2d40814 Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Wed, 24 Dec 2025 15:35:06 +0000
Subject: [PATCH 23/24] lavc/vvc: Error on inter slice with no reference pics

The semantics of sh_num_ref_idx_active_minus1[ i ] state that

When the current slice is a P slice, the value of NumRefIdxActive[ 0 ] shall be 
greater than 0.
When the current slice is a B slice, both NumRefIdxActive[ 0 ] and 
NumRefIdxActive[ 1 ] shall be greater than 0.

Fixes: use of uninitialized memory
Fixes: 
449549597/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_VVC_fuzzer-5600497089445888

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Reported-by: Michael Niedermayer [email protected]
(cherry picked from commit 90f1f797aa9231375e4858df523fbfeda89bfd79)
---
 libavcodec/cbs_h266_syntax_template.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index bac7129f17..078aaf75df 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -3243,6 +3243,12 @@ static int FUNC(slice_header) (CodedBitstreamContext 
*ctx, RWContext *rw,
                     FFMIN(ref_pic_lists->rpl_ref_list[i].num_ref_entries,
                         pps->pps_num_ref_idx_default_active_minus1[i] + 1);
             }
+
+            if (current->num_ref_idx_active[i] <= 0) {
+                av_log(ctx->log_ctx, AV_LOG_ERROR,
+                       "Inter slice but no reference pictures available for 
RPL%d.\n", i);
+                return AVERROR_INVALIDDATA;
+            }
         } else {
             current->num_ref_idx_active[i] = 0;
         }
-- 
2.49.1


>From bfff74732ec655c0d9464119a3be9519a28ca04e Mon Sep 17 00:00:00 2001
From: Frank Plowman <[email protected]>
Date: Mon, 29 Dec 2025 22:14:53 +0000
Subject: [PATCH 24/24] lavc/vvc: Prevent OOB write to slice_top_left_ctu_x in
 PPS CBS

Prior to the fix, in the case of a tile containing multiple slices
(pps_num_exp_slices_in_tile != 0) the number of slices was temporarily
allowed to exceed pps_num_slices_in_pic_minus1+1 and therefore
VVC_MAX_SLICES.  The number of slices was later verified, but while the
current slice index was higher than expected it was used to write to a
array of size VVC_MAX_SLICES, leading to an OOB write.

To rectify this, the patch adds some checks at an earlier stage, to
ensure that the slice index i + j at no point exceeds
pps_num_slices_in_pic_minus1.

Fixes #YWH-PGM40646-30

(cherry picked from commit 72a38c12e5b84ccb30fba88c39ef2a086013af5b)
---
 libavcodec/cbs_h266_syntax_template.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index 078aaf75df..96d0af9764 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -2017,6 +2017,12 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, 
RWContext *rw,
                         slice_top_left_ctu_y[i] = ctu_y;
                     } else {
                         uint16_t slice_height_in_ctus;
+                        int num_uniform_slices;
+
+                        if (i + current->pps_num_exp_slices_in_tile[i] >
+                            current->pps_num_slices_in_pic_minus1 + 1)
+                            return AVERROR_INVALIDDATA;
+
                         for (j = 0; j < current->pps_num_exp_slices_in_tile[i];
                              j++) {
                             ues(pps_exp_slice_height_in_ctus_minus1[i][j], 0,
@@ -2037,6 +2043,13 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, 
RWContext *rw,
                         uniform_slice_height = 1 +
                             (j == 0 ? current->row_height_val[tile_y] - 1:
                             
current->pps_exp_slice_height_in_ctus_minus1[i][j-1]);
+
+                        num_uniform_slices = (remaining_height_in_ctbs_y + 
uniform_slice_height - 1)
+                                           / uniform_slice_height;
+                        if (i + current->pps_num_exp_slices_in_tile[i] + 
num_uniform_slices >
+                            current->pps_num_slices_in_pic_minus1 + 1)
+                            return AVERROR_INVALIDDATA;
+
                         while (remaining_height_in_ctbs_y > 
uniform_slice_height) {
                             current->slice_height_in_ctus[i + j] =
                                                           uniform_slice_height;
-- 
2.49.1

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

Reply via email to