[compiler-rt] 7639265 - [sanitizer] Implement __sanitizer_get_allocated_size_fast

Jin Xin Ng via llvm-commits llvm-commits at lists.llvm.org
Thu May 25 17:21:43 PDT 2023


Author: Jin Xin Ng
Date: 2023-05-26T00:19:47Z
New Revision: 7639265af4547c0330d5949f0da8f92e9b83f6b0

URL: https://github.com/llvm/llvm-project/commit/7639265af4547c0330d5949f0da8f92e9b83f6b0
DIFF: https://github.com/llvm/llvm-project/commit/7639265af4547c0330d5949f0da8f92e9b83f6b0.diff

LOG: [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

Added: 
    compiler-rt/test/sanitizer_common/TestCases/malloc_hook_get_allocated_size_fast.cpp

Modified: 
    compiler-rt/include/sanitizer/allocator_interface.h
    compiler-rt/lib/asan/asan_allocator.cpp
    compiler-rt/lib/dfsan/dfsan_allocator.cpp
    compiler-rt/lib/hwasan/hwasan_allocator.cpp
    compiler-rt/lib/lsan/lsan_allocator.cpp
    compiler-rt/lib/memprof/memprof_allocator.cpp
    compiler-rt/lib/msan/msan_allocator.cpp
    compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h
    compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc
    compiler-rt/lib/tsan/rtl/tsan_mman.cpp
    compiler-rt/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c
    compiler-rt/test/sanitizer_common/TestCases/allocator_interface.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/include/sanitizer/allocator_interface.h b/compiler-rt/include/sanitizer/allocator_interface.h
index d0cfce79c1aef..367e6409258f1 100644
--- a/compiler-rt/include/sanitizer/allocator_interface.h
+++ b/compiler-rt/include/sanitizer/allocator_interface.h
@@ -34,6 +34,10 @@ extern "C" {
      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/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 19d7777c40282..5f26118d0e67a 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -798,6 +798,10 @@ struct Allocator {
     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 @@ uptr __sanitizer_get_allocated_size(const void *p) {
   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/compiler-rt/lib/dfsan/dfsan_allocator.cpp b/compiler-rt/lib/dfsan/dfsan_allocator.cpp
index 3075b6da1c115..a3bed535dc08b 100644
--- a/compiler-rt/lib/dfsan/dfsan_allocator.cpp
+++ b/compiler-rt/lib/dfsan/dfsan_allocator.cpp
@@ -198,6 +198,10 @@ static uptr AllocationSize(const void *p) {
   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 @@ const void *__sanitizer_get_allocated_begin(const void *p) {
 }
 
 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/compiler-rt/lib/hwasan/hwasan_allocator.cpp b/compiler-rt/lib/hwasan/hwasan_allocator.cpp
index d3e8266726302..6e6e63517b762 100644
--- a/compiler-rt/lib/hwasan/hwasan_allocator.cpp
+++ b/compiler-rt/lib/hwasan/hwasan_allocator.cpp
@@ -442,6 +442,15 @@ static uptr AllocationSize(const void *p) {
   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 @@ const void *__sanitizer_get_allocated_begin(const void *p) {
 
 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/compiler-rt/lib/lsan/lsan_allocator.cpp b/compiler-rt/lib/lsan/lsan_allocator.cpp
index b7c088508f2f6..12d579a9385b1 100644
--- a/compiler-rt/lib/lsan/lsan_allocator.cpp
+++ b/compiler-rt/lib/lsan/lsan_allocator.cpp
@@ -172,6 +172,10 @@ uptr GetMallocUsableSize(const void *p) {
   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))) {
@@ -385,6 +389,14 @@ uptr __sanitizer_get_allocated_size(const void *p) {
   return GetMallocUsableSize(p);
 }
 
+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(); }
 

diff  --git a/compiler-rt/lib/memprof/memprof_allocator.cpp b/compiler-rt/lib/memprof/memprof_allocator.cpp
index 1e0d05d47a6f8..4b68f21272844 100644
--- a/compiler-rt/lib/memprof/memprof_allocator.cpp
+++ b/compiler-rt/lib/memprof/memprof_allocator.cpp
@@ -555,6 +555,10 @@ struct Allocator {
     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 @@ uptr __sanitizer_get_allocated_size(const void *p) {
   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/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp
index 96fdf7bb195f2..be0714dbf47e2 100644
--- a/compiler-rt/lib/msan/msan_allocator.cpp
+++ b/compiler-rt/lib/msan/msan_allocator.cpp
@@ -288,6 +288,10 @@ static uptr AllocationSize(const void *p) {
   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 @@ const void *__sanitizer_get_allocated_begin(const void *p) {
 
 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/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h
index 8f3b71eb6ce74..de2b271fb0ed9 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_interface.h
@@ -25,6 +25,8 @@ SANITIZER_INTERFACE_ATTRIBUTE const 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_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/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc
index 01be600e33ba3..37efb5791d0bf 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc
@@ -34,6 +34,7 @@ INTERFACE_FUNCTION(__sanitizer_symbolize_pc)
 // 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/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
index 94f850eee0275..ac6d005fc1c05 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
@@ -377,6 +377,13 @@ uptr user_alloc_usable_size(const void *p) {
   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 @@ uptr __sanitizer_get_allocated_size(const void *p) {
   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/compiler-rt/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c b/compiler-rt/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c
index 07abb1fa29363..0fdec468e08ae 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c
+++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/malloc_usable_size.c
@@ -17,12 +17,14 @@ int main() {
   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/compiler-rt/test/sanitizer_common/TestCases/allocator_interface.cpp b/compiler-rt/test/sanitizer_common/TestCases/allocator_interface.cpp
index c2f304418f86a..6f5f05639800a 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/allocator_interface.cpp
+++ b/compiler-rt/test/sanitizer_common/TestCases/allocator_interface.cpp
@@ -17,6 +17,7 @@ void Test(int size) {
   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/compiler-rt/test/sanitizer_common/TestCases/malloc_hook_get_allocated_size_fast.cpp b/compiler-rt/test/sanitizer_common/TestCases/malloc_hook_get_allocated_size_fast.cpp
new file mode 100644
index 0000000000000..7acd2f698d642
--- /dev/null
+++ b/compiler-rt/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;
+}


        


More information about the llvm-commits mailing list