From 415b1cfd57de62da8af9ad8dc567fc9d918dbaa5 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Mon, 3 Apr 2023 21:14:40 +0000 Subject: [PATCH] Add __sanitizer_get_allocated_begin API and implementations This function will return the start of the allocation, if given a pointer that lies within an allocation. Otherwise, it returns NULL. It will be useful for detecting dynamic TLS allocations in glibc >=2.25, which uses malloc (see https://github.com/google/sanitizers/issues/1409#issuecomment-1214244142). Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D147005 --- .../include/sanitizer/allocator_interface.h | 4 ++ compiler-rt/lib/asan/asan_allocator.cpp | 15 +++++ compiler-rt/lib/dfsan/dfsan_allocator.cpp | 18 ++++++ compiler-rt/lib/hwasan/hwasan_allocator.cpp | 21 +++++++ compiler-rt/lib/lsan/lsan_allocator.cpp | 21 +++++++ compiler-rt/lib/memprof/memprof_allocator.cpp | 16 +++++ compiler-rt/lib/msan/msan_allocator.cpp | 19 ++++++ .../sanitizer_allocator_interface.h | 2 + .../sanitizer_allocator_internal.h | 3 +- .../sanitizer_common_interface.inc | 1 + compiler-rt/lib/tsan/rtl/tsan_mman.cpp | 18 ++++++ .../TestCases/get_allocated_begin.cpp | 58 +++++++++++++++++++ 12 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 compiler-rt/test/sanitizer_common/TestCases/get_allocated_begin.cpp diff --git a/compiler-rt/include/sanitizer/allocator_interface.h b/compiler-rt/include/sanitizer/allocator_interface.h index 6226135ef84b3..d846f3f330741 100644 --- a/compiler-rt/include/sanitizer/allocator_interface.h +++ b/compiler-rt/include/sanitizer/allocator_interface.h @@ -26,6 +26,10 @@ extern "C" { is not yet freed. */ int __sanitizer_get_ownership(const volatile void *p); + /* If a pointer lies within an allocation, it will return the start address + of the allocation. Otherwise, it returns nullptr. */ + void *__sanitizer_get_allocated_begin(const void *p); + /* Returns the number of bytes reserved for the pointer p. Requires (get_ownership(p) == true) or (p == 0). */ size_t __sanitizer_get_allocated_size(const volatile void *p); diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp index 4c52a45b875c7..4b65b44a88f91 100644 --- a/compiler-rt/lib/asan/asan_allocator.cpp +++ b/compiler-rt/lib/asan/asan_allocator.cpp @@ -1164,6 +1164,17 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) { // ---------------------- Interface ---------------- {{{1 using namespace __asan; +void *AllocationBegin(const void *p) { + AsanChunk *m = __asan::instance.GetAsanChunkByAddr((uptr)p); + if (!m) + return nullptr; + if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED) + return nullptr; + if (m->UsedSize() == 0) + return nullptr; + return (void *)(m->Beg()); +} + // ASan allocator doesn't reserve extra bytes, so normally we would // just return "size". We don't want to expose our redzone sizes, etc here. uptr __sanitizer_get_estimated_allocated_size(uptr size) { @@ -1187,6 +1198,10 @@ uptr __sanitizer_get_allocated_size(const void *p) { return allocated_size; } +void *__sanitizer_get_allocated_begin(const void *p) { + return AllocationBegin(p); +} + 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 index 5fb8fef213b9a..cebf9983c9490 100644 --- a/compiler-rt/lib/dfsan/dfsan_allocator.cpp +++ b/compiler-rt/lib/dfsan/dfsan_allocator.cpp @@ -174,6 +174,20 @@ void *DFsanCalloc(uptr nmemb, uptr size) { return DFsanAllocate(nmemb * size, sizeof(u64), true /*zeroise*/); } +void *AllocationBegin(const void *p) { + if (!p) + return nullptr; + const void *beg = allocator.GetBlockBegin(p); + if (!beg) + return nullptr; + Metadata *b = (Metadata *)allocator.GetMetaData(beg); + if (!b) + return nullptr; + if (b->requested_size == 0) + return nullptr; + return (void *)beg; +} + static uptr AllocationSize(const void *p) { if (!p) return 0; @@ -294,4 +308,8 @@ uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } +void *__sanitizer_get_allocated_begin(const void *p) { + return AllocationBegin(p); +} + uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.cpp b/compiler-rt/lib/hwasan/hwasan_allocator.cpp index d096a8faa2c7e..8ccdeb23fa995 100644 --- a/compiler-rt/lib/hwasan/hwasan_allocator.cpp +++ b/compiler-rt/lib/hwasan/hwasan_allocator.cpp @@ -397,6 +397,23 @@ HwasanChunkView FindHeapChunkByAddress(uptr address) { return HwasanChunkView(reinterpret_cast(block), metadata); } +void *AllocationBegin(const void *p) { + const void *untagged_ptr = UntagPtr(p); + if (!untagged_ptr) + return nullptr; + + const void *beg = allocator.GetBlockBegin(untagged_ptr); + if (!beg) + return nullptr; + + Metadata *b = (Metadata *)allocator.GetMetaData(beg); + if (b->GetRequestedSize() == 0) + return nullptr; + + tag_t tag = GetTagFromPointer((uptr)p); + return (void *)AddTagToPointer((uptr)beg, tag); +} + static uptr AllocationSize(const void *tagged_ptr) { const void *untagged_ptr = UntagPtr(tagged_ptr); if (!untagged_ptr) return 0; @@ -641,4 +658,8 @@ uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } +void *__sanitizer_get_allocated_begin(const void *p) { + return AllocationBegin(p); +} + uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } diff --git a/compiler-rt/lib/lsan/lsan_allocator.cpp b/compiler-rt/lib/lsan/lsan_allocator.cpp index 37ba363d479dd..d50882657dc33 100644 --- a/compiler-rt/lib/lsan/lsan_allocator.cpp +++ b/compiler-rt/lib/lsan/lsan_allocator.cpp @@ -145,6 +145,22 @@ void GetAllocatorCacheRange(uptr *begin, uptr *end) { *end = *begin + sizeof(AllocatorCache); } +void *GetMallocBegin(const void *p) { + if (!p) + return nullptr; + const void *beg = allocator.GetBlockBegin(p); + if (!beg) + return nullptr; + ChunkMetadata *m = Metadata(beg); + if (!m) + return nullptr; + if (!m->allocated) + return nullptr; + if (m->requested_size == 0) + return nullptr; + return (void *)beg; +} + uptr GetMallocUsableSize(const void *p) { if (!p) return 0; @@ -363,6 +379,11 @@ uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; } +SANITIZER_INTERFACE_ATTRIBUTE +void * __sanitizer_get_allocated_begin(const void *p) { + return GetMallocBegin(p); +} + SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_allocated_size(const void *p) { return GetMallocUsableSize(p); diff --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp index 51c3a66ebd680..80a87d49dfc6e 100644 --- a/compiler-rt/lib/memprof/memprof_allocator.cpp +++ b/compiler-rt/lib/memprof/memprof_allocator.cpp @@ -681,6 +681,18 @@ int memprof_posix_memalign(void **memptr, uptr alignment, uptr size, return 0; } +void *memprof_malloc_begin(const void *p) { + u64 user_requested_size; + MemprofChunk *m = + instance.GetMemprofChunkByAddr((uptr)p, user_requested_size); + if (!m) + return nullptr; + if (user_requested_size == 0) + return nullptr; + + return (void *)m->Beg(); +} + uptr memprof_malloc_usable_size(const void *ptr, uptr pc, uptr bp) { if (!ptr) return 0; @@ -699,6 +711,10 @@ int __sanitizer_get_ownership(const void *p) { return memprof_malloc_usable_size(p, 0, 0) != 0; } +void *__sanitizer_get_allocated_begin(const void *p) { + return memprof_malloc_begin(p); +} + uptr __sanitizer_get_allocated_size(const void *p) { return memprof_malloc_usable_size(p, 0, 0); } diff --git a/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp index 3308ee7053a61..a760a434158a5 100644 --- a/compiler-rt/lib/msan/msan_allocator.cpp +++ b/compiler-rt/lib/msan/msan_allocator.cpp @@ -260,6 +260,21 @@ static void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) { return MsanAllocate(stack, nmemb * size, sizeof(u64), true); } +void *AllocationBegin(const void *p) { + if (!p) + return nullptr; + const void *beg = allocator.GetBlockBegin(p); + if (!beg) + return nullptr; + Metadata *b = (Metadata *)allocator.GetMetaData(beg); + if (!b) + return nullptr; + if (b->requested_size == 0) + return nullptr; + + return (void *)beg; +} + static uptr AllocationSize(const void *p) { if (!p) return 0; const void *beg = allocator.GetBlockBegin(p); @@ -373,4 +388,8 @@ uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } +void *__sanitizer_get_allocated_begin(const void *p) { + return AllocationBegin(p); +} + uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h index c1b27563e2fc7..35c7c97df3299 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h @@ -21,6 +21,8 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_estimated_allocated_size(uptr size); SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void * +__sanitizer_get_allocated_begin(const void *p); SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_allocated_size(const void *p); SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h index 38994736877ac..adbdad5a1ee0c 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h @@ -51,7 +51,8 @@ void InternalFree(void *p, InternalAllocatorCache *cache = nullptr); void InternalAllocatorLock(); void InternalAllocatorUnlock(); InternalAllocator *internal_allocator(); - +int __sanitizer_get_allocation_bounds(const void *p, void **start, + unsigned long long *size); } // namespace __sanitizer #endif // SANITIZER_ALLOCATOR_INTERNAL_H diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc index 958f071e7b5f7..01be600e33ba3 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc @@ -32,6 +32,7 @@ INTERFACE_FUNCTION(__sanitizer_get_module_and_offset_for_pc) INTERFACE_FUNCTION(__sanitizer_symbolize_global) INTERFACE_FUNCTION(__sanitizer_symbolize_pc) // Allocator interface. +INTERFACE_FUNCTION(__sanitizer_get_allocated_begin) INTERFACE_FUNCTION(__sanitizer_get_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes) INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size) diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp index 99fa492265615..9c548dfff91f3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp @@ -352,6 +352,20 @@ void *user_pvalloc(ThreadState *thr, uptr pc, uptr sz) { return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, PageSize)); } +void *user_alloc_begin(const void *p) { + if (p == nullptr || !IsAppMem((uptr)p)) + return nullptr; + const void *beg = allocator()->GetBlockBegin(p); + if (!beg) + return nullptr; + + MBlock *b = ctx->metamap.GetBlock((uptr)beg); + if (!b) + return nullptr; // Not a valid pointer. + + return (void *)beg; +} + uptr user_alloc_usable_size(const void *p) { if (p == 0 || !IsAppMem((uptr)p)) return 0; @@ -430,6 +444,10 @@ int __sanitizer_get_ownership(const void *p) { return allocator()->GetBlockBegin(p) != 0; } +void *__sanitizer_get_allocated_begin(const void *p) { + return user_alloc_begin(p); +} + uptr __sanitizer_get_allocated_size(const void *p) { return user_alloc_usable_size(p); } diff --git a/compiler-rt/test/sanitizer_common/TestCases/get_allocated_begin.cpp b/compiler-rt/test/sanitizer_common/TestCases/get_allocated_begin.cpp new file mode 100644 index 0000000000000..6892a4a7fb282 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/get_allocated_begin.cpp @@ -0,0 +1,58 @@ +// 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) { + int sizes[] = {10, 100, 1000, 10000, 100000, 1000000}; + + for (int i = 0; i < sizeof(sizes) / sizeof(int); i++) { + printf("Testing size %d\n", sizes[i]); + + char *array = reinterpret_cast(malloc(sizes[i])); + int *int_ptr = new int; + printf("array: %p\n", array); + printf("int_ptr: %p\n", int_ptr); + + // Bogus value to unpoison start. Calling __sanitizer_get_allocated_begin + // does not unpoison it. + void *start = NULL; + for (int j = 0; j < sizes[i]; j++) { + printf("j: %d\n", j); + + start = __sanitizer_get_allocated_begin(array + j); + printf("Start: %p (expected: %p)\n", start, array); + fflush(stdout); + assert(array == start); + } + + start = __sanitizer_get_allocated_begin(int_ptr); + assert(int_ptr == start); + + void *wild_addr = reinterpret_cast(4096 * 160); + assert(__sanitizer_get_allocated_begin(wild_addr) == NULL); + + wild_addr = reinterpret_cast(0x1); + assert(__sanitizer_get_allocated_begin(wild_addr) == NULL); + + // NULL is a valid argument for GetAllocatedSize but is not owned. + assert(__sanitizer_get_allocated_begin(NULL) == NULL); + + free(array); + for (int j = 0; j < sizes[i]; j++) { + assert(__sanitizer_get_allocated_begin(array + j) == NULL); + } + + delete int_ptr; + assert(__sanitizer_get_allocated_begin(int_ptr) == NULL); + } + + return 0; +}