[compiler-rt] [asan] Fix `unknown-crash` reported for multi-byte errors (PR #144480)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 17 20:03:20 PDT 2025
https://github.com/wxwern updated https://github.com/llvm/llvm-project/pull/144480
>From f39a0fed00ce4b252aecd517e98c7b4c7ceac8a7 Mon Sep 17 00:00:00 2001
From: Wern Lim <welim at drwsg.com>
Date: Tue, 17 Jun 2025 14:52:28 +0800
Subject: [PATCH] [asan] Fix 'unknown-crash' reported for multi-byte errors
Given that a reported error by asan spans multiple bytes, asan may
flag the error as an 'unknown-crash' instead of the appropriate error
name.
This error can be reproduced via a partial buffer overflow (on gcc),
which reports 'unknown-crash' instead of 'stack-buffer-overflow' for
the below:
# minimal reprod (should occur on gcc-7 - gcc-15)
# https://godbolt.org/z/abrjrvnzj
#
# gcc -fsanitize=address reprod.c
struct X {
char bytes[16];
};
__attribute__((noinline)) struct X out_of_bounds() {
volatile char bytes[16];
struct X* x_ptr = (struct X*)(bytes + 2);
return *x_ptr;
}
int main() {
struct X x = out_of_bounds();
return x.bytes[0];
}
This is due to a flawed heuristic in asan_errors.cpp, which won't
always locate the appropriate shadow byte that would indicate a
corresponding error. This can happen for any reported errors which
span either: exactly 8 bytes, or 16 and more bytes.
The above example doesn't reproduce the issue on clang as it reports
errors via different pathways:
- gcc-compiled binaries report the starting address and size of the
failing read attempt to asan.
- clang-compiled binaries highlight the first byte access that
overflows the buffer to asan. Note: out-of-scope, but this is also
possibly misleading, as it still reports the full size of the read
attempt, paired with an address that's not the start of the read.
This behavior appears to be identical for all past versions tested.
I'm not aware of a way to replicate this specific issue with clang,
though it might have impacted error reporting in other areas.
This patch resolves this issue via a linear scan of applicable
shadow bytes (instead of the original heuristic, which, at best, only
increments the shadow byte address by 1 for these scenarios).
---
compiler-rt/lib/asan/asan_errors.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/asan/asan_errors.cpp b/compiler-rt/lib/asan/asan_errors.cpp
index 2a207cd06ccac..626b0f294e110 100644
--- a/compiler-rt/lib/asan/asan_errors.cpp
+++ b/compiler-rt/lib/asan/asan_errors.cpp
@@ -437,8 +437,10 @@ ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
bug_descr = "unknown-crash";
if (AddrIsInMem(addr)) {
u8 *shadow_addr = (u8 *)MemToShadow(addr);
- // If we are accessing 16 bytes, look at the second shadow byte.
- if (*shadow_addr == 0 && access_size > ASAN_SHADOW_GRANULARITY)
+ u8 *shadow_addr_upper_bound = (u8 *)MemToShadow(addr + access_size);
+ // If the access could span multiple shadow bytes,
+ // do a sequential scan and look for the first bad shadow byte.
+ while (*shadow_addr == 0 && shadow_addr < shadow_addr_upper_bound)
shadow_addr++;
// If we are in the partial right redzone, look at the next shadow byte.
if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
More information about the llvm-commits
mailing list