Commit 0ba703305c for aom

commit 0ba703305c4b8cd5125dd7d8bc258b7416514845
Author: Marco Paniconi <marpan@google.com>
Date:   Thu Oct 29 14:25:27 2026 +0000

    Require 2-byte aligned planes and strides for hbd

    High bit-depth image planes store 16-bit samples, and image2yuvconfig()
    stores plane pointers via CONVERT_TO_BYTEPTR() (>> 1) and halves the
    byte strides y_stride and uv_stride via >>= 1.

    Reject high bit-depth images whose plane pointer or byte stride
    is not 2-byte aligned in validate_img().

    Also fix Encode1x1() in test/encode_small_width_height_test.cc to double
    the byte strides y_stride and uv_stride (rather than y_height and
    uv_height) when bitdepth > 8.

    Port of this libvpx fix:
    https://chromium-review.googlesource.com/c/webm/libvpx/+/8668125

    Added unittest EncodeAPI.HighbdMisalignedPlanesAndStrides.

    Bug: 578678096
    Change-Id: I0a6d68803b5f9483e5ca795412774dbf83806f6c

diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index f3d14e1328..a76a22d0df 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -1029,6 +1029,18 @@ static aom_codec_err_t validate_img(aom_codec_alg_priv_t *ctx,
 #endif

 #if CONFIG_AV1_HIGHBITDEPTH
+  if (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) {
+    // Note there is no high bitdepth version of NV12 defined. If one is
+    // added, `num_planes` should be 2 in that case.
+    const int num_planes = ctx->cfg.monochrome ? 1 : 3;
+    // Validate that it is safe to cast each row pointer to `unsigned short *`.
+    for (int plane = 0; plane < num_planes; ++plane) {
+      if ((((uintptr_t)img->planes[plane]) & 1) || (img->stride[plane] & 1)) {
+        ERROR("High bit-depth image planes and strides must be 2-byte aligned");
+      }
+    }
+  }
+
   if (ctx->extra_cfg.validate_hbd_input &&
       (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH)) {
     const unsigned int bit_depth = ctx->cfg.g_bit_depth;
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 73c7d7679b..c6900a1bd9 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -4773,6 +4773,81 @@ TEST(EncodeAPI, Avg16x16IntegerOverflow) {

   EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
 }
+
+// Verifies that validate_img() rejects high bit-depth images whose plane
+// pointers or byte strides are not 2-byte aligned.
+// Bug: 578678096
+TEST(EncodeAPI, HighbdMisalignedPlanesAndStrides) {
+  aom_codec_iface_t *const iface = aom_codec_av1_cx();
+  aom_codec_ctx_t enc;
+  aom_codec_enc_cfg_t cfg;
+
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
+            AOM_CODEC_OK);
+
+  const unsigned int kWidth = 64;
+  const unsigned int kHeight = 64;
+  cfg.g_w = kWidth;
+  cfg.g_h = kHeight;
+  cfg.g_bit_depth = AOM_BITS_10;
+  cfg.g_input_bit_depth = 10;
+  cfg.g_lag_in_frames = 0;
+
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, AOM_CODEC_USE_HIGHBITDEPTH),
+            AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 6), AOM_CODEC_OK);
+
+  std::vector<uint16_t> buf(kWidth * kHeight * 3, 0);
+  uint8_t *const base = reinterpret_cast<uint8_t *>(buf.data());
+
+  aom_image_t img;
+  memset(&img, 0, sizeof(img));
+  ASSERT_EQ(aom_img_wrap(&img, AOM_IMG_FMT_I42016, kWidth, kHeight, 2, base),
+            &img);
+  uint8_t *const orig_planes[3] = { img.planes[AOM_PLANE_Y],
+                                    img.planes[AOM_PLANE_U],
+                                    img.planes[AOM_PLANE_V] };
+  const int orig_strides[3] = { img.stride[AOM_PLANE_Y],
+                                img.stride[AOM_PLANE_U],
+                                img.stride[AOM_PLANE_V] };
+  auto reset_valid_img = [&]() {
+    for (int plane = 0; plane < 3; ++plane) {
+      img.planes[plane] = orig_planes[plane];
+      img.stride[plane] = orig_strides[plane];
+    }
+  };
+
+  // 1. Odd plane pointers on Y, U, or V must be rejected.
+  for (int plane = 0; plane < 3; ++plane) {
+    reset_valid_img();
+    img.planes[plane] += 1;
+    EXPECT_EQ(
+        aom_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1, /*flags=*/0),
+        AOM_CODEC_INVALID_PARAM);
+  }
+
+  // 2. Odd byte strides on Y or U/V must be rejected.
+  reset_valid_img();
+  img.stride[AOM_PLANE_Y] += 1;
+  EXPECT_EQ(
+      aom_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1, /*flags=*/0),
+      AOM_CODEC_INVALID_PARAM);
+
+  reset_valid_img();
+  img.stride[AOM_PLANE_U] += 1;
+  img.stride[AOM_PLANE_V] += 1;
+  EXPECT_EQ(
+      aom_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1, /*flags=*/0),
+      AOM_CODEC_INVALID_PARAM);
+
+  // 3. Valid 2-byte aligned planes and strides succeed.
+  reset_valid_img();
+  EXPECT_EQ(
+      aom_codec_encode(&enc, &img, /*pts=*/0, /*duration=*/1, /*flags=*/0),
+      AOM_CODEC_OK);
+
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
 #endif  // CONFIG_AV1_HIGHBITDEPTH

 // A key frame whose luma is a block of noise repeated over the frame. IntraBC
diff --git a/test/encode_small_width_height_test.cc b/test/encode_small_width_height_test.cc
index d74d613e74..3f15057770 100644
--- a/test/encode_small_width_height_test.cc
+++ b/test/encode_small_width_height_test.cc
@@ -200,13 +200,13 @@ void Encode1x1(aom_img_fmt fmt, int bitdepth, aom_codec_flags_t flags) {
   img.x_chroma_shift = 1;
   img.y_chroma_shift = 1;
   img.bps = 12;
-  const int y_stride = kWidth;
-  const int uv_stride = (kWidth + 1) >> 1;
-  int y_height = kHeight;
-  int uv_height = (kHeight + 1) >> 1;
+  int y_stride = kWidth;
+  int uv_stride = (kWidth + 1) >> 1;
+  const int y_height = kHeight;
+  const int uv_height = (kHeight + 1) >> 1;
   if (bitdepth > 8) {
-    y_height <<= 1;
-    uv_height <<= 1;
+    y_stride <<= 1;
+    uv_stride <<= 1;
   }
   img.stride[AOM_PLANE_Y] = y_stride;
   img.stride[AOM_PLANE_U] = img.stride[AOM_PLANE_V] = uv_stride;