[compiler-rt] [ASan] Fix overflow and last byte handling in __asan_region_is_poisoned (PR #183900)
Roman Vinogradov via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 5 07:39:39 PST 2026
================
@@ -19,7 +19,7 @@ int main() {
// On Windows, %p omits %0x and prints hex characters in upper case,
// so we use PRIxPTR instead of %p.
fprintf(stderr, "Expected bad addr: %#" PRIxPTR "\n",
- reinterpret_cast<uintptr_t>(p + offset));
+ reinterpret_cast<uintptr_t>(p + offset - 1));
----------------
vinorom wrote:
No, this is not due to alignment (if I got your comment correctly).
In this test the test author:
1. allocates memory: `char *p = new char;`
2. and calls `memmove(dest, p, offset);` for the region `[p, p+offset)`. A wild offset/size is used for that: `const size_t offset = 0x4567890123456789`.
In other words, the start address `p` of the region is valid but the region ends far beyond the address space due to the wild offset (`end = p+offset: end > kHighMemEnd` => `AddrIsInMem()` returns false).
The test checks that ASan actually complains about the region passed in `memmove()` - it verifies that ASan reports the expected bad address too. The test assumed that the bad address, returned from underlying call to `__asan_region_is_poisoned()`, is exclusive `end` address, i.e. `p+offset`, which doesn't belong to given region. In order to verify the bad address it prints it first into stderr so the test runner can read it first and then check the ASan message on attempt to call `memmove()`.
Before the change `__asan_region_is_poisoned()` returned the exclusive `end` address which is always outside of given region (i.e. `p+offset` in this case) if it is outside of valid memory, but after the change we check and return the last byte of given region (i.e. `p+offset-1`) which is always part of given region.
This test failed after my change and then I had to adjust it.
It is caused by this change in `__asan_region_is_poisoned`:
```c++
uptr end = beg + size;
if (!AddrIsInMem(end))
return end;
```
=>
```c++
uptr last = beg + size - 1;
if (!AddrIsInMem(last))
return last;
```
According to the documentation for this function it is supposed to return either 0 (entire region is unpoisoned/addressable) or first/some poisoned byte of the given region. `End` address doesn't belong to the region. The `last` address may belong to addressable/unpoisoned region but its `end` address can be outside of VAS.
Please let me know if returning exclusive `end` address (beyond given region) was a design decision, so I make it backward compatible. But to me it looks not quite logical, contradicting to existing documentation (below) and potentially causing crashes due to overflow (I described it in the git message).
```c++
/// Checks if a region is poisoned.
///
/// If at least one byte in <c>[beg, beg+size)</c> is poisoned, returns the
/// address of the first such byte. Otherwise returns 0.
///
/// \param beg Start of memory region.
/// \param size Size of memory region.
/// \returns Address of first poisoned byte.
```
https://github.com/llvm/llvm-project/pull/183900
More information about the llvm-commits
mailing list