Avoid undefined pointer overflow in neon mask opts

When blitting out of a mask with nine patch, the nine patch code may
call the blitters with a mask row bytes of zero, with the intention of
blitting the same row out of the mask into many rows of the destination.

In D32_A8_Opaque_Color_neon and blit_mask_d32_a8_black when
SK_ARM_HAS_NEON it is assumed that the maskRB >= width. If the maskRB
are less than the width, the maskRB -= width underflows, which is
defined, but the later mask += maskRB is not, as this wil be the
addition of a very large integer to the mask pointer, which is
definitely larger than the mask allocation. In practice on a twos
compliment machine with sizeof(size_t) == sizeof(void*) with a flat
address space this works out, but it is undefined behavior.

When calculating the mask adjustment instead calculate and store it as
ptrdiff_t. After ssa, this is effectively the same code but with a
signed add to the mask pointer instead of unsigned.

Discovered with dm with ASAN (with UBSAN) on arm64 Mac.

Change-Id: I4c8967730c802039e192750927e10f13282258b8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/508680
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
Ben Wagner 2022-02-14 15:32:28 -05:00 committed by SkCQ
parent c94eb30c6e
commit b9b550e9bb

View File

@ -50,7 +50,8 @@ namespace SK_OPTS_NS {
const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr;
uint8x8x4_t vpmc;
maskRB -= width;
// Nine patch may set maskRB to 0 to blit the same row repeatedly.
ptrdiff_t mask_adjust = (ptrdiff_t)maskRB - width;
dstRB -= (width << 2);
if (width >= 8) {
@ -100,7 +101,7 @@ namespace SK_OPTS_NS {
}
device = (uint32_t*)((char*)device + dstRB);
mask += maskRB;
mask += mask_adjust;
} while (--height != 0);
}
@ -125,7 +126,8 @@ namespace SK_OPTS_NS {
SkPMColor* SK_RESTRICT device = (SkPMColor*)dst;
const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr;
maskRB -= width;
// Nine patch may set maskRB to 0 to blit the same row repeatedly.
ptrdiff_t mask_adjust = (ptrdiff_t)maskRB - width;
dstRB -= (width << 2);
do {
int w = width;
@ -150,7 +152,7 @@ namespace SK_OPTS_NS {
device += 1;
}
device = (uint32_t*)((char*)device + dstRB);
mask += maskRB;
mask += mask_adjust;
} while (--height != 0);
}