I'm fuzzing a C parser on arm64 Android and hit a case where the two sanitizers disagree. ASan reports a heap-buffer-overflow READ; HWASan on the same source, same input, reports nothing at all.
Reduced to this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
for (size_t n = 1; n <= 8; n++) {
unsigned char *p = malloc(n);
memset(p, 'A', n);
/* 4-byte read starting one byte before the end: crosses the boundary */
volatile int r = memcmp(p + n - 1, "FILE", 4);
printf("size=%zu end_offset_in_granule=%zu memcmp=%d\n",
n, (size_t)(((uintptr_t)(p + n)) % 16), r);
free(p);
}
}
Built with NDK 27 / clang 18:
# ASan
clang --target=aarch64-linux-android30 -O1 -g \
-fsanitize=address -fsanitize-recover=address t.c -o t_asan
# HWASan
clang --target=aarch64-linux-android35 -O1 -g \
-fsanitize=hwaddress -fsanitize-recover=hwaddress -static-libsan t.c -o t_hwasan
Results on a physical arm64 device:
build reports ASan 2 × heap-buffer-overflow HWASan 0
Every allocation ends at granule offset 1–8:
size=1 end_offset_in_granule=1 memcmp=-1 ... size=8 end_offset_in_granule=8 memcmp=-1
My hypothesis: HWASan tags memory in 16-byte granules, so for a malloc(n) where n isn't a multiple of 16, the bytes between the end of the allocation and the end of its granule carry the same tag. A 4-byte read starting at p + n - 1 therefore stays inside that granule, the pointer tag still matches the memory tag, and there is no mismatch to detect. ASan uses byte-granular redzones, so it sees it immediately.
Question: Is that the correct explanation, and is it documented as an accepted limitation rather than a bug? Specifically:
Is 16 bytes the tag granule on Android arm64, and is it configurable? Is there an option that makes HWASan catch intra-granule over-reads — something analogous to ASan's redzones? Is the guidance for this to run both sanitizers, or is there a reason to prefer one for boundary over-reads specifically?
I'm asking because the two are often described as interchangeable with HWASan as the strictly better one, and this looks like a class of bug where that isn't true.
https://github.com/infectedcoffee/MISC - holds my bug hunter report (more info) :)
Detailed explanation can be found at Hardware-assisted AddressSanitizer Design Documentation #Comparison with AddressSanitizer. Basically HWASan inflicts less overhead but detection is probablistic as a tradeoff. >* Does not require redzones to detect buffer overflows, but the buffer overflow detection is probabilistic, with roughly 1/(2**TS) chance of missing a bug (6.25% or 0.39% with 4 and 8-bit TS respectively). > >* Does not require quarantine to detect heap-use-after-free, or stack-use-after-return. The detection is similarly probabilistic. > >* The memory overhead of HWASAN is expected to be much smaller than that of AddressSanitizer: 1/TG extra memory for the shadow and some overhead due to TG-aligning all objects.
user7860670