Commit 79401f2946 for aom

commit 79401f2946940984aaf3dc50e42d34be2e7bbac9
Author: Marco Paniconi <marpan@google.com>
Date:   Wed Apr 15 15:13:08 2026 -0700

    Add check to validate allocation size of src_sad_blk_64x64

    Fixes heap buffer overflow in dynamic svc.

    Added unittest to repro the issue.

    Bug: 502735235
    Change-Id: I52e52c8b14a25f453a62d74575ea1cd8c56a3e72

diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index a149da75a3..2d492dd74d 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -753,6 +753,7 @@ static void init_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf) {
   cpi->svc.number_temporal_layers = 1;
   cm->spatial_layer_id = 0;
   cm->temporal_layer_id = 0;
+  cpi->src_sad_blk_alloc_size = 0;
   // Init rtc_ref parameters.
   cpi->ppi->rtc_ref.set_ref_frame_config = 0;
   cpi->ppi->rtc_ref.non_reference_frame = 0;
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index ec95323d63..61153b9fb3 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -3648,6 +3648,11 @@ typedef struct AV1_COMP {
    */
   uint64_t *src_sad_blk_64x64;

+  /*!
+   * Size of allocated buffer to store 64x64 SAD, in units of uint64_t.
+   */
+  int src_sad_blk_alloc_size;
+
   /*!
    * SSE between the current frame and the reconstructed last frame
    * It is only used for CBR mode.
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index c4c0e92d3d..dd2bd1bac5 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -3293,9 +3293,10 @@ void av1_rc_scene_detection_onepass_rt(AV1_COMP *cpi,
   // is changed dynamically without re-alloc of encoder.
   if (width != cm->render_width || height != cm->render_height ||
       cpi->svc.number_spatial_layers > 1 || unscaled_src == NULL ||
-      unscaled_last_src == NULL) {
+      unscaled_last_src == NULL || is_one_pass_rt_lag_params(cpi)) {
     aom_free(cpi->src_sad_blk_64x64);
     cpi->src_sad_blk_64x64 = NULL;
+    cpi->src_sad_blk_alloc_size = 0;
   }
   if (unscaled_src == NULL || unscaled_last_src == NULL) return;
   src_y = unscaled_src->y_buffer;
@@ -3309,6 +3310,7 @@ void av1_rc_scene_detection_onepass_rt(AV1_COMP *cpi,
   if (src_width != last_src_width || src_height != last_src_height) {
     aom_free(cpi->src_sad_blk_64x64);
     cpi->src_sad_blk_64x64 = NULL;
+    cpi->src_sad_blk_alloc_size = 0;
     return;
   }
   rc->high_source_sad = 0;
@@ -3360,10 +3362,12 @@ void av1_rc_scene_detection_onepass_rt(AV1_COMP *cpi,
   // Store blkwise SAD for later use. Disable for spatial layers for now.
   if (width == cm->render_width && height == cm->render_height &&
       cpi->svc.number_spatial_layers == 1 && !is_one_pass_rt_lag_params(cpi)) {
-    if (cpi->src_sad_blk_64x64 == NULL) {
+    if (cpi->src_sad_blk_alloc_size != sb_cols * sb_rows) {
+      aom_free(cpi->src_sad_blk_64x64);
       CHECK_MEM_ERROR(cm, cpi->src_sad_blk_64x64,
                       (uint64_t *)aom_calloc(sb_cols * sb_rows,
                                              sizeof(*cpi->src_sad_blk_64x64)));
+      cpi->src_sad_blk_alloc_size = sb_cols * sb_rows;
     }
   }
   const CommonModeInfoParams *const mi_params = &cpi->common.mi_params;
@@ -3931,6 +3935,7 @@ void av1_get_one_pass_rt_params(AV1_COMP *cpi, FRAME_TYPE *const frame_type,
     } else {
       aom_free(cpi->src_sad_blk_64x64);
       cpi->src_sad_blk_64x64 = NULL;
+      cpi->src_sad_blk_alloc_size = 0;
     }
   }
   if (cpi->sf.rt_sf.rc_compute_spatial_var_sc_kf &&
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 5402644c78..34d92fc039 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -2240,4 +2240,89 @@ TEST(EncodeAPI, DynamicSvcAq3Issue499606109) {
   ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
 }

+TEST(EncodeAPI, DynamicSvcTemporalIssue502735235) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
+            AOM_CODEC_OK);
+
+  // Init at 256x512.
+  cfg.g_w = 256;
+  cfg.g_h = 512;
+  cfg.g_timebase.num = 1;
+  cfg.g_timebase.den = 30;
+  cfg.rc_end_usage = AOM_CBR;
+  cfg.rc_target_bitrate = 1000;
+  cfg.g_lag_in_frames = 0;
+
+  aom_codec_ctx_t codec;
+  ASSERT_EQ(aom_codec_enc_init(&codec, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&codec, AOME_SET_CPUUSED, 10), AOM_CODEC_OK);
+
+  // AV1E_SET_SVC_PARAMS
+  aom_svc_params_t svc_params = {};
+  svc_params.number_spatial_layers = 1;
+  svc_params.number_temporal_layers = 2;
+  svc_params.scaling_factor_num[0] = 1;
+  svc_params.scaling_factor_den[0] = 1;
+  svc_params.framerate_factor[0] = 2;
+  svc_params.framerate_factor[1] = 1;
+  svc_params.max_quantizers[0] = 56;
+  svc_params.min_quantizers[0] = 10;
+  svc_params.max_quantizers[1] = 56;
+  svc_params.min_quantizers[1] = 10;
+  svc_params.layer_target_bitrate[0] = cfg.rc_target_bitrate * 60 / 100;
+  svc_params.layer_target_bitrate[1] = cfg.rc_target_bitrate;
+  EXPECT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_PARAMS, &svc_params),
+            AOM_CODEC_OK);
+
+  // Encode at 256x512. TL0.
+  aom_svc_layer_id_t layer_id = {};
+  layer_id.spatial_layer_id = 0;
+  layer_id.spatial_layer_id = 0;
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id),
+            AOM_CODEC_OK);
+  aom_image_t *raw = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 256, 512, 1);
+  ASSERT_NE(raw, nullptr);
+  ASSERT_EQ(aom_codec_encode(&codec, raw, /*pts=*/0, /*duration=*/1,
+                             /*flags=*/0),
+            AOM_CODEC_OK);
+  aom_img_free(raw);
+
+  // Encode at 256x64 TL1, twice, set keyframe for both.
+  cfg.g_w = 256;
+  cfg.g_h = 64;
+  ASSERT_EQ(aom_codec_enc_config_set(&codec, &cfg), AOM_CODEC_OK);
+  layer_id.spatial_layer_id = 0;
+  layer_id.temporal_layer_id = 1;
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id),
+            AOM_CODEC_OK);
+  raw = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 256, 64, 1);
+  ASSERT_NE(raw, nullptr);
+  ASSERT_EQ(aom_codec_encode(&codec, raw, /*pts=*/1, /*duration=*/1,
+                             /*flags=*/AOM_EFLAG_FORCE_KF),
+            AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_encode(&codec, raw, /*pts=*/2, /*duration=*/1,
+                             /*flags=*/0),
+            AOM_CODEC_OK);
+  aom_img_free(raw);
+
+  // Encode TL0 back at original resolution 256x512.
+  cfg.g_w = 256;
+  cfg.g_h = 512;
+  ASSERT_EQ(aom_codec_enc_config_set(&codec, &cfg), AOM_CODEC_OK);
+  layer_id.spatial_layer_id = 0;
+  layer_id.temporal_layer_id = 0;
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id),
+            AOM_CODEC_OK);
+  raw = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 256, 512, 1);
+  ASSERT_NE(raw, nullptr);
+  ASSERT_EQ(aom_codec_encode(&codec, raw, /*pts=*/3, /*duration=*/1,
+                             /*flags=*/0),
+            AOM_CODEC_OK);
+  aom_img_free(raw);
+
+  ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
+}
+
 }  // namespace