Commit 40fa6ebbee for aom

commit 40fa6ebbee34da25112a07f1e73268e777d55c2f
Author: David Sarkisyan <david@srkyn.com>
Date:   Fri Sep 11 15:09:11 2026 -0400

    Validate decoder tile selection against frame bounds

    The decoder stores tile row and column controls before it knows the next
    frame dimensions. Validate them once the tile counts are available and
    before dispatching to any tile decoder. This prevents a one-past
    selection from reaching large-scale tile buffer parsing.

    Add a focused regression for the invalid column case.

    Signed-off-by: David Sarkisyan <david@srkyn.com>

    Change-Id: I2175ced004328ab39e172a87a20839de151f058e

diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index b6ee3ace02..fcb328de40 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -5370,6 +5370,11 @@ void av1_decode_tg_tiles_and_wrapup(AV1Decoder *pbi, const uint8_t *data,

   xd->error_info = cm->error;
   if (initialize_flag) setup_frame_info(pbi);
+  if (pbi->dec_tile_row < -1 || pbi->dec_tile_row >= tiles->rows ||
+      pbi->dec_tile_col < -1 || pbi->dec_tile_col >= tiles->cols) {
+    aom_internal_error(&pbi->error, AOM_CODEC_INVALID_PARAM,
+                       "Invalid tile row or column");
+  }
   const int num_planes = av1_num_planes(cm);

 #if CONFIG_COLLECT_COMPONENT_TIMING
diff --git a/test/av1_ext_tile_test.cc b/test/av1_ext_tile_test.cc
index 1eb6ced758..51a1d320ed 100644
--- a/test/av1_ext_tile_test.cc
+++ b/test/av1_ext_tile_test.cc
@@ -29,6 +29,8 @@ const int kTIleSizeInPixels = (kTileSize << 6);
 // Fake width and height so that they can be multiples of the tile size.
 const int kImgWidth = 704;
 const int kImgHeight = 576;
+const int kInvalidTileImgSize = 256;
+const int kOutOfRangeTileCol = 2;

 // This test tests large scale tile coding case. Non-large-scale tile coding
 // is tested by the tile_independence test.
@@ -78,7 +80,8 @@ class AV1ExtTileTest
                           ::libaom_test::Encoder *encoder) override {
     if (video->frame() == 0) {
       // Encode setting
-      encoder->Control(AOME_SET_CPUUSED, set_cpu_used_);
+      encoder->Control(AOME_SET_CPUUSED,
+                       test_invalid_tile_ ? 8 : set_cpu_used_);
       encoder->Control(AOME_SET_ENABLEAUTOALTREF, 0);
       encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);

@@ -88,8 +91,8 @@ class AV1ExtTileTest
       encoder->Control(AV1E_SET_SUPERBLOCK_SIZE, AOM_SUPERBLOCK_SIZE_64X64);
       // Set tile_columns and tile_rows to MAX values, which guarantees the tile
       // size of 64 x 64 pixels(i.e. 1 SB) for <= 4k resolution.
-      encoder->Control(AV1E_SET_TILE_COLUMNS, 6);
-      encoder->Control(AV1E_SET_TILE_ROWS, 6);
+      encoder->Control(AV1E_SET_TILE_COLUMNS, test_invalid_tile_ ? 1 : 6);
+      encoder->Control(AV1E_SET_TILE_ROWS, test_invalid_tile_ ? 1 : 6);
     } else if (video->frame() == 1) {
       frame_flags_ =
           AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF;
@@ -109,6 +112,16 @@ class AV1ExtTileTest
   }

   void FramePktHook(const aom_codec_cx_pkt_t *pkt) override {
+    if (test_invalid_tile_) {
+      decoder_->Control(AV1_SET_DECODE_TILE_ROW, 0);
+      decoder_->Control(AV1_SET_DECODE_TILE_COL, kOutOfRangeTileCol);
+      EXPECT_EQ(AOM_CODEC_INVALID_PARAM,
+                decoder_->DecodeFrame(
+                    reinterpret_cast<uint8_t *>(pkt->data.frame.buf),
+                    pkt->data.frame.sz));
+      return;
+    }
+
     // Skip decoding 1 frame.
     if (pkt->data.frame.pts == (aom_codec_pts_t)kSkip) return;

@@ -186,12 +199,28 @@ class AV1ExtTileTest
     ASSERT_EQ(md5_, tile_md5_);
   }

+  void TestInvalidTile() {
+    ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv",
+                                         kInvalidTileImgSize,
+                                         kInvalidTileImgSize, 30, 1, 0, 1);
+    cfg_.rc_target_bitrate = 500;
+    cfg_.g_error_resilient = AOM_ERROR_RESILIENT_DEFAULT;
+    cfg_.large_scale_tile = 1;
+    cfg_.g_lag_in_frames = 0;
+    cfg_.g_threads = 1;
+    test_invalid_tile_ = true;
+
+    init_flags_ = AOM_CODEC_USE_PSNR;
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  }
+
   ::libaom_test::TestMode encoding_mode_;
   int set_cpu_used_;
   ::libaom_test::Decoder *decoder_;
   aom_image_t tile_img_;
   std::vector<std::string> md5_;
   std::vector<std::string> tile_md5_;
+  bool test_invalid_tile_ = false;
 };

 TEST_P(AV1ExtTileTest, DecoderResultTest) { TestRoundTrip(); }
@@ -205,6 +234,8 @@ class AV1ExtTileTestLarge : public AV1ExtTileTest {};

 TEST_P(AV1ExtTileTestLarge, DecoderResultTest) { TestRoundTrip(); }

+TEST_P(AV1ExtTileTestLarge, RejectsInvalidDecodeTile) { TestInvalidTile(); }
+
 AV1_INSTANTIATE_TEST_SUITE(
     // Now only test 2-pass mode.
     AV1ExtTileTestLarge, ::testing::Values(::libaom_test::kTwoPassGood),