Commit 1b7f9ef3dd for aom

commit 1b7f9ef3dd176cf0792f58ec6a43d29c49325bc0
Author: Jingning Han <jingning@google.com>
Date:   Thu Nov 12 16:12:18 2026 -0800

    Arm: Avoid over-read in 4-wide intrabc convolutions

    av1_convolve_x_sr_intrabc_neon() and the horizontal pass of
    av1_convolve_2d_sr_intrabc_neon() load 8 bytes from src and src + 1
    for blocks 4 or fewer pixels wide, though only w + 1 pixels per row
    are used. The extra pixels lie to the right of the reference block.

    With IntraBC in row based multithreading, as in
    DatarateTestSVC.BasicRateTargetingSVC2TL1SLScreenDropFrame1920x10804Thread,
    those pixels can be in a superblock that another thread is
    reconstructing at the same time. The read values are discarded, but
    ThreadSanitizer reports a data race between the read in
    av1_convolve_2d_sr_intrabc_neon() and the intra prediction writes of
    the other thread.

    Load only 4 bytes per row from src and src + 1, as
    av1_convolve_y_sr_intrabc_neon() already does.

    Change-Id: I3aeb96861d384ed2fb3c0c231d86478961e9944b

diff --git a/av1/common/arm/convolve_neon.c b/av1/common/arm/convolve_neon.c
index fc42f9e6e0..68521f2114 100644
--- a/av1/common/arm/convolve_neon.c
+++ b/av1/common/arm/convolve_neon.c
@@ -1648,13 +1648,15 @@ void av1_convolve_x_sr_intrabc_neon(const uint8_t *src, int src_stride,

   if (w <= 4) {
     do {
-      uint8x8_t s0_0 = vld1_u8(src);
-      uint8x8_t s0_1 = vld1_u8(src + 1);
-      uint8x8_t s1_0 = vld1_u8(src + src_stride);
-      uint8x8_t s1_1 = vld1_u8(src + src_stride + 1);
+      // Load at most 5 pixels per row. A full 8-byte load would read
+      // pixels to the right of the reference block, which may be in a region
+      // that is concurrently being reconstructed by another encoder thread.
+      uint8x8_t s0 = load_unaligned_u8(src, src_stride);
+      uint8x8_t s1 = load_unaligned_u8(src + 1, src_stride);

-      uint8x8_t d0 = vrhadd_u8(s0_0, s0_1);
-      uint8x8_t d1 = vrhadd_u8(s1_0, s1_1);
+      // Row 0 is in the low half of d0, row 1 in the high half.
+      uint8x8_t d0 = vrhadd_u8(s0, s1);
+      uint8x8_t d1 = vext_u8(d0, d0, 4);

       if (w == 2) {
         store_u8_2x1(dst + 0 * dst_stride, d0);
@@ -1806,8 +1808,11 @@ void av1_convolve_2d_sr_intrabc_neon(const uint8_t *src, int src_stride,
   // Horizontal filter.
   if (w <= 4) {
     do {
-      uint8x8_t s0 = vld1_u8(src);
-      uint8x8_t s1 = vld1_u8(src + 1);
+      // Load at most 5 pixels per row. A full 8-byte load would read
+      // pixels to the right of the reference block, which may be in a region
+      // that is concurrently being reconstructed by another encoder thread.
+      uint8x8_t s0 = load_unaligned_u8_4x1(src);
+      uint8x8_t s1 = load_unaligned_u8_4x1(src + 1);

       uint16x4_t sum = vget_low_u16(vaddl_u8(s0, s1));