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),