diff --git a/compiler-rt/include/sanitizer/allocator_interface.h b/compiler-rt/include/sanitizer/allocator_interface.h --- a/compiler-rt/include/sanitizer/allocator_interface.h +++ b/compiler-rt/include/sanitizer/allocator_interface.h @@ -30,6 +30,12 @@ Requires (get_ownership(p) == true) or (p == 0). */ size_t __sanitizer_get_allocated_size(const volatile void *p); + /* If a pointer lies within an allocation (not necessarily the start), it + will return 1, and store the start and size in the referenced parameters. + Otherwise, it returns 0. */ + int __sanitizer_get_allocation_bounds(const void *p, void **start, + unsigned long long *size); + /* Number of bytes, allocated and not yet freed by the application. */ size_t __sanitizer_get_current_allocated_bytes(void); diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp --- a/compiler-rt/lib/asan/asan_allocator.cpp +++ b/compiler-rt/lib/asan/asan_allocator.cpp @@ -1187,6 +1187,17 @@ return allocated_size; } +int __sanitizer_get_allocation_bounds(const void *p, void **start, unsigned long long *size) { + AsanChunk *m = __asan::instance.GetAsanChunkByAddr((uptr)p); + if (!m) return 0; + if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED) + return 0; + if (m->UsedSize() == 0) return 0; + *start = (void*)(m->Beg()); + *size = m->UsedSize(); + return 1; +} + void __sanitizer_purge_allocator() { GET_STACK_TRACE_MALLOC; instance.Purge(&stack); diff --git a/compiler-rt/lib/dfsan/dfsan_allocator.cpp b/compiler-rt/lib/dfsan/dfsan_allocator.cpp --- a/compiler-rt/lib/dfsan/dfsan_allocator.cpp +++ b/compiler-rt/lib/dfsan/dfsan_allocator.cpp @@ -295,3 +295,20 @@ int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } + +int __sanitizer_get_allocation_bounds(const void *p, void **start, + unsigned long long *size) { + if (!p) + return 0; + const void *beg = allocator.GetBlockBegin(p); + if (beg == nullptr) + return 0; + Metadata *b = (Metadata *)allocator.GetMetaData(beg); + if (b == nullptr) + return 0; + if (b->requested_size == 0) + return 0; + *start = (void *)beg; + *size = b->requested_size; + return 1; +} diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.cpp b/compiler-rt/lib/hwasan/hwasan_allocator.cpp --- a/compiler-rt/lib/hwasan/hwasan_allocator.cpp +++ b/compiler-rt/lib/hwasan/hwasan_allocator.cpp @@ -642,3 +642,20 @@ int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } + +int __sanitizer_get_allocation_bounds(const void *p, void **start, unsigned long long *size) { + const void *untagged_ptr = UntagPtr(p); + if (!untagged_ptr) return 0; + + const void *beg = allocator.GetBlockBegin(untagged_ptr); + if (beg == nullptr) return 0; + + Metadata *b = (Metadata *)allocator.GetMetaData(beg); + if (b->GetRequestedSize() == 0) return 0; + + tag_t tag = GetTagFromPointer((uptr)p); + *start = (void*)AddTagToPointer((uptr)beg, tag); + *size = b->GetRequestedSize(); + + return 1; +} diff --git a/compiler-rt/lib/lsan/lsan_allocator.cpp b/compiler-rt/lib/lsan/lsan_allocator.cpp --- a/compiler-rt/lib/lsan/lsan_allocator.cpp +++ b/compiler-rt/lib/lsan/lsan_allocator.cpp @@ -368,4 +368,18 @@ return GetMallocUsableSize(p); } +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __sanitizer_get_allocation_bounds(const void *p, void **start, unsigned long long *size) { + if (!p) return 0; + const void *beg = allocator.GetBlockBegin(p); + if (beg == nullptr) return 0; + ChunkMetadata *m = Metadata(beg); + if (m == nullptr) return 0; + if (!m->allocated) return 0; + if (m->requested_size == 0) return 0; + *start = (void*)beg; + *size = m->requested_size; + return 1; +} + } // extern "C" diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp --- a/compiler-rt/lib/memprof/memprof_allocator.cpp +++ b/compiler-rt/lib/memprof/memprof_allocator.cpp @@ -703,6 +703,17 @@ return memprof_malloc_usable_size(p, 0, 0); } +int __sanitizer_get_allocation_bounds(const void *p, void **start, unsigned long long *size) { + u64 user_requested_size; + MemprofChunk *m = instance.GetMemprofChunkByAddr((uptr)p, user_requested_size); + if (!m) + return 0; + if (user_requested_size == 0) return 0; + *start = (void*)m->Beg(); + *size = user_requested_size; + return 1; +} + int __memprof_profile_dump() { instance.FinishAndWrite(); // In the future we may want to return non-zero if there are any errors diff --git a/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp --- a/compiler-rt/lib/msan/msan_allocator.cpp +++ b/compiler-rt/lib/msan/msan_allocator.cpp @@ -374,3 +374,15 @@ int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } + +int __sanitizer_get_allocation_bounds(const void *p, void **start, unsigned long long *size) { + if (!p) return 0; + const void *beg = allocator.GetBlockBegin(p); + if (beg == nullptr) return 0; + Metadata *b = (Metadata *)allocator.GetMetaData(beg); + if (b == nullptr) return 0; + if (b->requested_size == 0) return 0; + *start = (void*)beg; + *size = b->requested_size; + return 1; +} diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h @@ -37,6 +37,10 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_free_hook(void *ptr); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int +__sanitizer_get_allocation_bounds(const void *p, void **start, + unsigned long long *size); + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_purge_allocator(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc @@ -33,6 +33,7 @@ INTERFACE_FUNCTION(__sanitizer_symbolize_pc) // Allocator interface. INTERFACE_FUNCTION(__sanitizer_get_allocated_size) +INTERFACE_FUNCTION(__sanitizer_get_allocation_bounds) INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes) INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_free_bytes) diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp --- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp @@ -434,6 +434,26 @@ return user_alloc_usable_size(p); } +int __sanitizer_get_allocation_bounds(const void *p, void **start, unsigned long long *size) { + if (p == 0 || !IsAppMem((uptr)p)) + return 0; + const void *beg = allocator()->GetBlockBegin(p); + if (beg == nullptr) return 0; + + MBlock *b = ctx->metamap.GetBlock((uptr)beg); + if (!b) + return 0; // Not a valid pointer. + + *start = (void*)beg; + + if (b->siz == 0) { + *size = 1; // Zero-sized allocations are actually 1 byte. + } else { + *size = b->siz; + } + return 1; +} + void __tsan_on_thread_idle() { ThreadState *thr = cur_thread(); allocator()->SwallowCache(&thr->proc()->alloc_cache); diff --git a/compiler-rt/test/sanitizer_common/TestCases/get_allocation_bounds.cpp b/compiler-rt/test/sanitizer_common/TestCases/get_allocation_bounds.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/get_allocation_bounds.cpp @@ -0,0 +1,60 @@ +// RUN: %clangxx -O0 -g %s -o %t && %run %t + +// UBSan does not have its own allocator +// UNSUPPORTED: ubsan + +#include +#include +#include +#include +#include + +// Based on lib/msan/tests/msan_test.cpp::get_allocated_size_and_ownership +int main(void) { + char *array = reinterpret_cast(malloc(100)); + int *int_ptr = new int; + printf("array: %p\n", array); + printf("int_ptr: %p\n", int_ptr); + + // Bogus values to unpoison these. Calling __sanitizer_get_allocation_bounds + // does not unpoison them. + void *start = NULL; + unsigned long long size = 666; + for (int i = 0; i < 100; i++) { + printf("i: %d\n", i); + + printf("Bounds ok? %d\n", + __sanitizer_get_allocation_bounds(array + i, &start, &size)); + fflush(stdout); + assert(__sanitizer_get_allocation_bounds(array + i, &start, &size)); + printf("Size: %llu\n", size); + fflush(stdout); + printf("Start: %p\n", start); + fflush(stdout); + assert(array == start); + assert(100U == size); + } + + assert(__sanitizer_get_allocation_bounds(int_ptr, &start, &size)); + assert(int_ptr == start); + assert(sizeof(*int_ptr) == size); + + void *wild_addr = reinterpret_cast(4096 * 160); + assert(!__sanitizer_get_allocation_bounds(wild_addr, &start, &size)); + + wild_addr = reinterpret_cast(0x1); + assert(!__sanitizer_get_allocation_bounds(wild_addr, &start, &size)); + + // NULL is a valid argument for GetAllocatedSize but is not owned. + assert(!__sanitizer_get_allocation_bounds(NULL, &start, &size)); + + free(array); + for (int i = 0; i < 100; i++) { + assert(!__sanitizer_get_allocation_bounds(array + i, &start, &size)); + } + + delete int_ptr; + assert(!__sanitizer_get_allocation_bounds(int_ptr, &start, &size)); + + return 0; +}