From bcaab3609805ea10fb3d9ac0c9d947b4c3563948 Mon Sep 17 00:00:00 2001
From: Seungha Yang <seungha@centricular.com>
Date: Sat, 15 Mar 2025 23:48:52 +0900
Subject: [PATCH] h265parser: Fix num_long_term_pics bound check

As defined in the spec 7.4.7.1, calculates allowed maximum
value of num_long_term_pics

Fixes ZDI-CAN-26596

Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4285
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8885>

Upstream-Status: Backport [https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/bcaab3609805ea10fb3d9ac0c9d947b4c3563948]
CVE: CVE-2025-3887
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
---
 gst-libs/gst/codecparsers/gsth265parser.c | 40 +++++++++++++++++++++--
 1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/gst-libs/gst/codecparsers/gsth265parser.c b/gst-libs/gst/codecparsers/gsth265parser.c
index 5d5a2db..abcc05d 100644
--- a/gst-libs/gst/codecparsers/gsth265parser.c
+++ b/gst-libs/gst/codecparsers/gsth265parser.c
@@ -2779,6 +2779,8 @@ gst_h265_parser_parse_slice_hdr (GstH265Parser * parser,
       READ_UINT8 (&nr, slice->colour_plane_id, 2);
 
     if (!GST_H265_IS_NAL_TYPE_IDR (nalu->type)) {
+      const GstH265ShortTermRefPicSet *ref_pic_sets = NULL;
+
       READ_UINT16 (&nr, slice->pic_order_cnt_lsb,
           (sps->log2_max_pic_order_cnt_lsb_minus4 + 4));
 
@@ -2795,23 +2797,55 @@ gst_h265_parser_parse_slice_hdr (GstH265Parser * parser,
         slice->short_term_ref_pic_set_size =
             (nal_reader_get_pos (&nr) - pos) -
             (8 * (nal_reader_get_epb_count (&nr) - epb_pos));
+
+	ref_pic_sets = &slice->short_term_ref_pic_sets;
       } else if (sps->num_short_term_ref_pic_sets > 1) {
         const guint n = ceil_log2 (sps->num_short_term_ref_pic_sets);
         READ_UINT8 (&nr, slice->short_term_ref_pic_set_idx, n);
         CHECK_ALLOWED_MAX (slice->short_term_ref_pic_set_idx,
             sps->num_short_term_ref_pic_sets - 1);
+	ref_pic_sets =
+	    &sps->short_term_ref_pic_set[slice->short_term_ref_pic_set_idx];
+      } else {
+	ref_pic_sets = &sps->short_term_ref_pic_set[0];
       }
 
       if (sps->long_term_ref_pics_present_flag) {
         guint32 limit;
         guint pos = nal_reader_get_pos (&nr);
         guint epb_pos = nal_reader_get_epb_count (&nr);
+	gint max_num_long_term_pics = 0;
+	gint TwoVersionsOfCurrDecPicFlag = 0;
 
-        if (sps->num_long_term_ref_pics_sps > 0)
+        if (sps->num_long_term_ref_pics_sps > 0) {
           READ_UE_MAX (&nr, slice->num_long_term_sps,
               sps->num_long_term_ref_pics_sps);
-
-        READ_UE_MAX (&nr, slice->num_long_term_pics, 16);
+	}
+
+	/* 7.4.3.3.3 */
+	if (pps->pps_scc_extension_flag &&
+	    pps->pps_scc_extension_params.pps_curr_pic_ref_enabled_flag &&
+	    (sps->sample_adaptive_offset_enabled_flag ||
+	        !pps->deblocking_filter_disabled_flag ||
+		pps->deblocking_filter_override_enabled_flag)) {
+	  TwoVersionsOfCurrDecPicFlag = 1;
+	}
+
+	/* Calculated upper bound num_long_term_pics can have. 7.4.7.1 */
+	max_num_long_term_pics =
+	    /* sps_max_dec_pic_buffering_minus1[TemporalId], allowed max is
+	     * MaxDpbSize - 1 */
+	    MAX_DPB_SIZE - 1
+	    - (gint) slice->num_long_term_sps
+	    - (gint) ref_pic_sets->NumNegativePics
+	    - (gint) ref_pic_sets->NumPositivePics -
+	    TwoVersionsOfCurrDecPicFlag;
+	if (max_num_long_term_pics < 0) {
+	  GST_WARNING ("Invalid stream, too many reference pictures");
+	  goto error;
+	}
+
+        READ_UE_MAX (&nr, slice->num_long_term_pics, max_num_long_term_pics);
         limit = slice->num_long_term_sps + slice->num_long_term_pics;
         for (i = 0; i < limit; i++) {
           if (i < slice->num_long_term_sps) {
-- 
2.25.1

