[sanitizer] Implement __sanitizer_get_allocated_size_fast
The primary motivation for this change is to allow FreeHooks to obtain
the allocated size of the pointer being freed in a fast, efficient manner.
Differential Revision: https://reviews.llvm.org/D151360
GitOrigin-RevId: 7639265af4547c0330d5949f0da8f92e9b83f6b0
diff --git a/include/sanitizer/allocator_interface.h b/include/sanitizer/allocator_interface.h
index d0cfce7..367e640 100644
--- a/include/sanitizer/allocator_interface.h
+++ b/include/sanitizer/allocator_interface.h
@@ -34,6 +34,10 @@
Requires (get_ownership(p) == true) or (p == 0). */
size_t __sanitizer_get_allocated_size(const volatile void *p);
+ /* Returns the number of bytes reserved for the pointer p.
+ Requires __sanitizer_get_allocated_begin(p) == p. */
+ size_t __sanitizer_get_allocated_size_fast(const volatile void *p);
+
/* Number of bytes, allocated and not yet freed by the application. */
size_t __sanitizer_get_current_allocated_bytes(void);
diff --git a/lib/asan/asan_allocator.cpp b/lib/asan/asan_allocator.cpp
index 19d7777..5f26118 100644
--- a/lib/asan/asan_allocator.cpp
+++ b/lib/asan/asan_allocator.cpp
@@ -798,6 +798,10 @@
return m->UsedSize();
}
+ uptr AllocationSizeFast(uptr p) {
+ return reinterpret_cast<AsanChunk *>(p - kChunkHeaderSize)->UsedSize();
+ }
+
AsanChunkView FindHeapChunkByAddress(uptr addr) {
AsanChunk *m1 = GetAsanChunkByAddr(addr);
sptr offset = 0;
@@ -1198,6 +1202,13 @@
return allocated_size;
}
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = instance.AllocationSizeFast(reinterpret_cast<uptr>(p));
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
+
const void *__sanitizer_get_allocated_begin(const void *p) {
return AllocationBegin(p);
}
diff --git a/lib/dfsan/dfsan_allocator.cpp b/lib/dfsan/dfsan_allocator.cpp
index 3075b6d..a3bed53 100644
--- a/lib/dfsan/dfsan_allocator.cpp
+++ b/lib/dfsan/dfsan_allocator.cpp
@@ -198,6 +198,10 @@
return b->requested_size;
}
+static uptr AllocationSizeFast(const void *p) {
+ return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size;
+}
+
void *dfsan_malloc(uptr size) {
return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/));
}
@@ -313,3 +317,10 @@
}
uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
+
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = AllocationSizeFast(p);
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
diff --git a/lib/hwasan/hwasan_allocator.cpp b/lib/hwasan/hwasan_allocator.cpp
index d3e8266..6e6e635 100644
--- a/lib/hwasan/hwasan_allocator.cpp
+++ b/lib/hwasan/hwasan_allocator.cpp
@@ -442,6 +442,15 @@
return b->GetRequestedSize();
}
+static uptr AllocationSizeFast(const void *p) {
+ const void *untagged_ptr = UntagPtr(p);
+ void *aligned_ptr = reinterpret_cast<void *>(
+ RoundDownTo(reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment));
+ Metadata *meta =
+ reinterpret_cast<Metadata *>(allocator.GetMetaData(aligned_ptr));
+ return meta->GetRequestedSize();
+}
+
void *hwasan_malloc(uptr size, StackTrace *stack) {
return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
}
@@ -680,4 +689,11 @@
uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = AllocationSizeFast(p);
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
+
void __sanitizer_purge_allocator() { allocator.ForceReleaseToOS(); }
diff --git a/lib/lsan/lsan_allocator.cpp b/lib/lsan/lsan_allocator.cpp
index b7c0885..12d579a 100644
--- a/lib/lsan/lsan_allocator.cpp
+++ b/lib/lsan/lsan_allocator.cpp
@@ -172,6 +172,10 @@
return m->requested_size;
}
+uptr GetMallocUsableSizeFast(const void *p) {
+ return Metadata(p)->requested_size;
+}
+
int lsan_posix_memalign(void **memptr, uptr alignment, uptr size,
const StackTrace &stack) {
if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
@@ -386,6 +390,14 @@
}
SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = GetMallocUsableSizeFast(p);
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_purge_allocator() { allocator.ForceReleaseToOS(); }
} // extern "C"
diff --git a/lib/memprof/memprof_allocator.cpp b/lib/memprof/memprof_allocator.cpp
index 1e0d05d..4b68f21 100644
--- a/lib/memprof/memprof_allocator.cpp
+++ b/lib/memprof/memprof_allocator.cpp
@@ -555,6 +555,10 @@
return user_requested_size;
}
+ uptr AllocationSizeFast(uptr p) {
+ return reinterpret_cast<MemprofChunk *>(p - kChunkHeaderSize)->UsedSize();
+ }
+
void Purge(BufferedStackTrace *stack) { allocator.ForceReleaseToOS(); }
void PrintStats() { allocator.PrintStats(); }
@@ -719,6 +723,13 @@
return memprof_malloc_usable_size(p, 0, 0);
}
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = instance.AllocationSizeFast(reinterpret_cast<uptr>(p));
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
+
int __memprof_profile_dump() {
instance.FinishAndWrite();
// In the future we may want to return non-zero if there are any errors
diff --git a/lib/msan/msan_allocator.cpp b/lib/msan/msan_allocator.cpp
index 96fdf7b..be0714d 100644
--- a/lib/msan/msan_allocator.cpp
+++ b/lib/msan/msan_allocator.cpp
@@ -288,6 +288,10 @@
return b->requested_size;
}
+static uptr AllocationSizeFast(const void *p) {
+ return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size;
+}
+
void *msan_malloc(uptr size, StackTrace *stack) {
return SetErrnoOnNull(MsanAllocate(stack, size, sizeof(u64), false));
}
@@ -399,4 +403,11 @@
uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = AllocationSizeFast(p);
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
+
void __sanitizer_purge_allocator() { allocator.ForceReleaseToOS(); }
diff --git a/lib/sanitizer_common/sanitizer_allocator_interface.h b/lib/sanitizer_common/sanitizer_allocator_interface.h
index 8f3b71e..de2b271 100644
--- a/lib/sanitizer_common/sanitizer_allocator_interface.h
+++ b/lib/sanitizer_common/sanitizer_allocator_interface.h
@@ -25,6 +25,8 @@
const void *p);
SANITIZER_INTERFACE_ATTRIBUTE uptr
__sanitizer_get_allocated_size(const void *p);
+SANITIZER_INTERFACE_ATTRIBUTE uptr
+__sanitizer_get_allocated_size_fast(const void *p);
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes();
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_heap_size();
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_free_bytes();
diff --git a/lib/sanitizer_common/sanitizer_common_interface.inc b/lib/sanitizer_common/sanitizer_common_interface.inc
index 01be600..37efb57 100644
--- a/lib/sanitizer_common/sanitizer_common_interface.inc
+++ b/lib/sanitizer_common/sanitizer_common_interface.inc
@@ -34,6 +34,7 @@
// Allocator interface.
INTERFACE_FUNCTION(__sanitizer_get_allocated_begin)
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
+INTERFACE_FUNCTION(__sanitizer_get_allocated_size_fast)
INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
diff --git a/lib/tsan/rtl/tsan_mman.cpp b/lib/tsan/rtl/tsan_mman.cpp
index 94f850e..ac6d005 100644
--- a/lib/tsan/rtl/tsan_mman.cpp
+++ b/lib/tsan/rtl/tsan_mman.cpp
@@ -377,6 +377,13 @@
return b->siz;
}
+uptr user_alloc_usable_size_fast(const void *p) {
+ MBlock *b = ctx->metamap.GetBlock((uptr)p);
+ if (b->siz == 0)
+ return 1; // Zero-sized allocations are actually 1 byte.
+ return b->siz;
+}
+
void invoke_malloc_hook(void *ptr, uptr size) {
ThreadState *thr = cur_thread();
if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
@@ -452,6 +459,13 @@
return user_alloc_usable_size(p);
}
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = user_alloc_usable_size_fast(p);
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
+
void __sanitizer_purge_allocator() {
allocator()->ForceReleaseToOS();
}
diff --git a/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c b/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c
index 07abb1f..0fdec46 100644
--- a/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c
+++ b/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c
@@ -17,12 +17,14 @@
int size = 1;
p = malloc(size);
assert(__sanitizer_get_allocated_size(p) == size);
+ assert(__sanitizer_get_allocated_size_fast(p) == size);
assert(malloc_usable_size(p) == size);
free(p);
size = 1234567;
p = malloc(size);
assert(__sanitizer_get_allocated_size(p) == size);
+ assert(__sanitizer_get_allocated_size_fast(p) == size);
assert(malloc_usable_size(p) == size);
free(p);
return 0;
diff --git a/test/sanitizer_common/TestCases/allocator_interface.cpp b/test/sanitizer_common/TestCases/allocator_interface.cpp
index c2f3044..6f5f056 100644
--- a/test/sanitizer_common/TestCases/allocator_interface.cpp
+++ b/test/sanitizer_common/TestCases/allocator_interface.cpp
@@ -17,6 +17,7 @@
assert(__sanitizer_get_ownership(p));
assert(!__sanitizer_get_ownership(&p));
assert(__sanitizer_get_allocated_size(p) == size);
+ assert(__sanitizer_get_allocated_size_fast(p) == size);
assert(__sanitizer_get_allocated_begin(p) == p);
assert(__sanitizer_get_allocated_begin(p + 1) == p);
assert(__sanitizer_get_current_allocated_bytes() >=
diff --git a/test/sanitizer_common/TestCases/malloc_hook_get_allocated_size_fast.cpp b/test/sanitizer_common/TestCases/malloc_hook_get_allocated_size_fast.cpp
new file mode 100644
index 0000000..7acd2f6
--- /dev/null
+++ b/test/sanitizer_common/TestCases/malloc_hook_get_allocated_size_fast.cpp
@@ -0,0 +1,56 @@
+// RUN: %clangxx -O2 %s -o %t && %run %t 2>&1
+
+// Malloc/free hooks are not supported on Windows.
+// XFAIL: target={{.*windows-msvc.*}}
+
+// Must not be implemented, no other reason to install interceptors.
+// XFAIL: ubsan
+
+// FIXME: Implement.
+// XFAIL: hwasan
+
+#include <assert.h>
+#include <sanitizer/allocator_interface.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+extern "C" {
+const volatile void *global_ptr;
+
+// Note: avoid calling functions that allocate memory in malloc/free
+// to avoid infinite recursion.
+void __sanitizer_malloc_hook(const volatile void *ptr, size_t sz) {
+ if (__sanitizer_get_ownership(ptr) && sz == sizeof(int)) {
+ global_ptr = ptr;
+ assert(__sanitizer_get_allocated_size_fast(ptr) == sizeof(int));
+ }
+}
+void __sanitizer_free_hook(const volatile void *ptr) {
+ if (__sanitizer_get_ownership(ptr) && ptr == global_ptr)
+ assert(__sanitizer_get_allocated_size_fast(ptr) == sizeof(int));
+}
+} // extern "C"
+
+volatile int *x;
+
+// Call this function with uninitialized arguments to poison
+// TLS shadow for function parameters before calling operator
+// new and, eventually, user-provided hook.
+__attribute__((noinline)) void allocate(int *unused1, int *unused2) {
+ x = reinterpret_cast<int *>(malloc(sizeof(int)));
+}
+
+int main() {
+ int *undef1, *undef2;
+ allocate(undef1, undef2);
+
+ // Check that malloc hook was called with correct argument.
+ if (global_ptr != (void *)x) {
+ _exit(1);
+ }
+
+ *x = -8;
+ free((void *)x);
+
+ return 0;
+}