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;