Commit 18cb9b53f5 for aom

commit 18cb9b53f59290810828a72d89730d932cc301bc
Author: Cheng Chen <chengchen@google.com>
Date:   Mon Sep 28 16:08:46 2026 -0700

    Correctly allocate restoration buffer size

    Adjust the condition when to allocate the restoration buffer size,
    such that is could reallocate when the frame size is changed.

    Bug:565488030

    Change-Id: I319cb6e8bf8a285c349a431c4caa4f38e35305b5

diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 9b73008698..5cc887bee5 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -1036,6 +1036,8 @@ void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf,
     }
   }

+  cm->tiles.large_scale = oxcf->tile_cfg.enable_large_scale_tile;
+  cm->tiles.single_tile_decoding = oxcf->tile_cfg.enable_single_tile_decoding;
   features->interp_filter =
       oxcf->tile_cfg.enable_large_scale_tile ? EIGHTTAP_REGULAR : SWITCHABLE;
   features->switchable_motion_mode = is_switchable_motion_mode_allowed(
@@ -1053,6 +1055,8 @@ void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf,
   int last_height = cm->height;
   cm->width = frm_dim_cfg->width;
   cm->height = frm_dim_cfg->height;
+  cm->superres_upscaled_width = frm_dim_cfg->width;
+  cm->superres_upscaled_height = frm_dim_cfg->height;

   if (cm->width > cpi->data_alloc_width ||
       cm->height > cpi->data_alloc_height || is_sb_size_changed) {
@@ -2720,7 +2724,7 @@ void av1_set_frame_size(AV1_COMP *cpi, int width, int height) {
   if (!is_stat_generation_stage(cpi)) av1_init_cdef_worker(cpi);

 #if !CONFIG_REALTIME_ONLY
-  if (is_restoration_used(cm)) {
+  if (cm->seq_params->enable_restoration) {
     for (int i = 0; i < num_planes; ++i)
       cm->rst_info[i].frame_restoration_type = RESTORE_NONE;

diff --git a/av1/encoder/ethread.c b/av1/encoder/ethread.c
index 581294c01c..dcbc744cb8 100644
--- a/av1/encoder/ethread.c
+++ b/av1/encoder/ethread.c
@@ -912,7 +912,7 @@ void av1_init_mt_sync(AV1_COMP *cpi, int is_first_pass) {
     }

 #if !CONFIG_REALTIME_ONLY
-    if (is_restoration_used(cm)) {
+    if (cm->seq_params->enable_restoration) {
       // Initialize loop restoration MT object.
       AV1LrSync *lr_sync = &mt_info->lr_row_sync;
       int rst_unit_size = cpi->sf.lpf_sf.min_lr_unit_size;
@@ -1333,7 +1333,7 @@ static inline void prepare_fpmt_workers(AV1_PRIMARY *ppi,
             mt_info->cdef_worker->colbuf[plane];
     }
 #if !CONFIG_REALTIME_ONLY
-    if (is_restoration_used(cm)) {
+    if (cm->seq_params->enable_restoration) {
       // Back up the original LR buffers before update.
       int idx = i + mt_info->num_workers - 1;
       assert(idx < mt_info->lr_row_sync.num_workers);
@@ -1407,7 +1407,7 @@ static inline void restore_workers_after_fpmt(AV1_PRIMARY *ppi,
             mt_info->restore_state_buf.cdef_colbuf[plane];
     }
 #if !CONFIG_REALTIME_ONLY
-    if (is_restoration_used(cm)) {
+    if (cm->seq_params->enable_restoration) {
       // Restore the original LR buffers.
       int idx = i + mt_info->num_workers - 1;
       assert(idx < mt_info->lr_row_sync.num_workers);
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 7ac5498719..387e753fb6 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -3292,6 +3292,79 @@ TEST(EncodeAPI, Buganizer558417547) {
   aom_free(cpi_test->mb_weber_stats);
 #endif  // !CONFIG_SHARED
 }
+
+// Regression test for b/565488030: Heap-buffer-overflow in
+// save_deblock_boundary_lines when Frame 0 is coded losslessly (q = 0, setting
+// cm->features.all_lossless = 1) or with large_scale_tile = 1 at a small
+// resolution and Frame 1 increases the resolution via
+// aom_codec_enc_config_set() with large_scale_tile = 0 and lossy coding (q > 0,
+// cm->features.all_lossless = 0) with loop restoration enabled.
+TEST(EncodeAPI, Buganizer565488030) {
+  for (unsigned int threads : { 1u, 4u }) {
+    for (unsigned int large_scale_tile : { 0u, 1u }) {
+      aom_codec_iface_t *const iface = aom_codec_av1_cx();
+      aom_codec_enc_cfg_t cfg;
+      ASSERT_EQ(
+          aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_GOOD_QUALITY),
+          AOM_CODEC_OK);
+
+      cfg.g_w = 4;
+      cfg.g_h = 4;
+      cfg.g_forced_max_frame_width = 256;
+      cfg.g_forced_max_frame_height = 256;
+      cfg.g_threads = threads;
+      cfg.g_lag_in_frames = 0;
+      cfg.large_scale_tile = large_scale_tile;
+      cfg.rc_end_usage = AOM_CBR;
+      cfg.rc_target_bitrate = 3999;
+      cfg.rc_min_quantizer = 0;
+      cfg.rc_max_quantizer = 63;
+
+      aom_codec_ctx_t enc;
+      ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+      ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 4), AOM_CODEC_OK);
+      // large_scale_tile = 1 implicitly disables global motion, so switching
+      // it to 0 below would turn global motion on mid-stream. The reference
+      // frames coded before that have no image pyramid, which trips
+      // assert(buf->buf.y_pyramid) in av1_encode_frame(). That is a separate
+      // issue, so keep global motion off to focus on loop restoration.
+      ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_GLOBAL_MOTION, 0),
+                AOM_CODEC_OK);
+
+      aom_image_t *img_small =
+          aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 4, 4, 16);
+      ASSERT_NE(img_small, nullptr);
+      FillImage(img_small, 128);
+
+      // Frame 0: 4x4 at high CBR bitrate picks q = 0 (all_lossless = 1).
+      EncodeOne(&enc, img_small, 0);
+      aom_img_free(img_small);
+
+      // Frame 1: Reconfigure to 256x256 with large_scale_tile = 0 (picks q > 0,
+      // all_lossless = 0).
+      cfg.g_w = 256;
+      cfg.g_h = 256;
+      cfg.large_scale_tile = 0;
+      ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+
+      aom_image_t *img_large =
+          aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 256, 256, 16);
+      ASSERT_NE(img_large, nullptr);
+      FillImage(img_large, 128);
+
+      EncodeOne(&enc, img_large, 1);
+      aom_img_free(img_large);
+
+      // Flush encoder.
+      ASSERT_EQ(aom_codec_encode(&enc, nullptr, 0, 0, 0), AOM_CODEC_OK);
+      aom_codec_iter_t iter = nullptr;
+      while (aom_codec_get_cx_data(&enc, &iter) != nullptr) {
+      }
+
+      ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+    }
+  }
+}
 #endif  // !CONFIG_REALTIME_ONLY

 }  // namespace