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));