[compiler-rt] 2c82588 - [dfsan] Use the sanitizer allocator to reduce memory cost

Jianzhou Zhao via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 6 15:10:15 PDT 2021


Author: Jianzhou Zhao
Date: 2021-06-06T22:09:31Z
New Revision: 2c82588dacac52ba8168214c6814ce22277d6e88

URL: https://github.com/llvm/llvm-project/commit/2c82588dacac52ba8168214c6814ce22277d6e88
DIFF: https://github.com/llvm/llvm-project/commit/2c82588dacac52ba8168214c6814ce22277d6e88.diff

LOG: [dfsan] Use the sanitizer allocator to reduce memory cost

dfsan does not use sanitizer allocator as others. In practice,
we let it use glibc's allocator since tcmalloc needs more work
to be working with dfsan well. With glibc, we observe large
memory leakage. This could relate to two things:

1) glibc allocator has limitation: for example, tcmalloc can reduce memory footprint 2x easily

2) glibc may call unmmap directly as an internal system call by using system call number. so DFSan has no way to release shadow spaces for those unmmap.

Using sanitizer allocator addresses the above issues
1) its memory management is close to tcmalloc

2) we can register callback when sanitizer allocator calls unmmap, so dfsan can release shadow spaces correctly.

Our experiment with internal server-based application proved that with the change, in a-few-day run, memory usage leakage is close to what tcmalloc does w/o dfsan.

This change mainly follows MSan's code.

1) define allocator callbacks at dfsan_allocator.h|cpp

2) mark allocator APIs to be discard

3) intercept allocator APIs

4) make dfsan_set_label consistent with MSan's SetShadow when setting 0 labels, define dfsan_release_meta_memory when unmap is called

5) add flags about whether zeroing memory after malloc/free. dfsan works at byte-level, so bit-level oparations can cause reading undefined shadow. See D96842. zeroing memory after malloc helps this. About zeroing after free, reading after free is definitely UB, but if user code does so, it is hard to debug an overtainting caused by this w/o running MSan. So we add the flag to help debugging.

This change will be split to small changes for review. Before that, a question is
"this code shares a lot of with MSan, for example, dfsan_allocator.* and dfsan_new_delete.*.
Does it make sense to unify the code at sanitizer_common? will that introduce some
maintenance issue?"

Reviewed By: morehouse

Differential Revision: https://reviews.llvm.org/D101204

Added: 
    compiler-rt/lib/dfsan/dfsan_new_delete.cpp
    compiler-rt/test/dfsan/interceptors.c

Modified: 
    compiler-rt/lib/dfsan/CMakeLists.txt
    compiler-rt/lib/dfsan/dfsan.cpp
    compiler-rt/lib/dfsan/dfsan.h
    compiler-rt/lib/dfsan/dfsan_custom.cpp
    compiler-rt/lib/dfsan/dfsan_interceptors.cpp
    compiler-rt/lib/dfsan/done_abilist.txt
    compiler-rt/test/dfsan/custom.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/dfsan/CMakeLists.txt b/compiler-rt/lib/dfsan/CMakeLists.txt
index 5c8e7fdfd3352..b06ae472f073d 100644
--- a/compiler-rt/lib/dfsan/CMakeLists.txt
+++ b/compiler-rt/lib/dfsan/CMakeLists.txt
@@ -7,6 +7,7 @@ set(DFSAN_RTL_SOURCES
   dfsan_chained_origin_depot.cpp
   dfsan_custom.cpp
   dfsan_interceptors.cpp
+  dfsan_new_delete.cpp
   dfsan_thread.cpp
   )
 

diff  --git a/compiler-rt/lib/dfsan/dfsan.cpp b/compiler-rt/lib/dfsan/dfsan.cpp
index 124ff97b03b3f..438abe66429a3 100644
--- a/compiler-rt/lib/dfsan/dfsan.cpp
+++ b/compiler-rt/lib/dfsan/dfsan.cpp
@@ -1113,7 +1113,7 @@ static void DFsanInit(int argc, char **argv, char **envp) {
   if (!(init_addr >= UnusedAddr() && init_addr < AppAddr()))
     MmapFixedNoAccess(UnusedAddr(), AppAddr() - UnusedAddr());
 
-  InitializeInterceptors();
+  initialize_interceptors();
 
   // Register the fini callback to run when the program terminates successfully
   // or it is killed by the runtime.

diff  --git a/compiler-rt/lib/dfsan/dfsan.h b/compiler-rt/lib/dfsan/dfsan.h
index 54048a763be11..2b3a209d9fb2e 100644
--- a/compiler-rt/lib/dfsan/dfsan.h
+++ b/compiler-rt/lib/dfsan/dfsan.h
@@ -65,7 +65,7 @@ namespace __dfsan {
 extern bool dfsan_inited;
 extern bool dfsan_init_is_running;
 
-void InitializeInterceptors();
+void initialize_interceptors();
 
 inline dfsan_label *shadow_for(void *ptr) {
   return (dfsan_label *) ((((uptr) ptr) & ShadowMask()) << 1);

diff  --git a/compiler-rt/lib/dfsan/dfsan_custom.cpp b/compiler-rt/lib/dfsan/dfsan_custom.cpp
index b9e88e2eee15b..3185184f29c8a 100644
--- a/compiler-rt/lib/dfsan/dfsan_custom.cpp
+++ b/compiler-rt/lib/dfsan/dfsan_custom.cpp
@@ -461,24 +461,6 @@ SANITIZER_INTERFACE_ATTRIBUTE int __dfso_strncasecmp(
   return r;
 }
 
-SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_calloc(size_t nmemb, size_t size,
-                                                  dfsan_label nmemb_label,
-                                                  dfsan_label size_label,
-                                                  dfsan_label *ret_label) {
-  void *p = calloc(nmemb, size);
-  dfsan_set_label(0, p, nmemb * size);
-  *ret_label = 0;
-  return p;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE void *__dfso_calloc(
-    size_t nmemb, size_t size, dfsan_label nmemb_label, dfsan_label size_label,
-    dfsan_label *ret_label, dfsan_origin nmemb_origin, dfsan_origin size_origin,
-    dfsan_origin *ret_origin) {
-  void *p = __dfsw_calloc(nmemb, size, nmemb_label, size_label, ret_label);
-  *ret_origin = 0;
-  return p;
-}
 
 SANITIZER_INTERFACE_ATTRIBUTE size_t
 __dfsw_strlen(const char *s, dfsan_label s_label, dfsan_label *ret_label) {

diff  --git a/compiler-rt/lib/dfsan/dfsan_interceptors.cpp b/compiler-rt/lib/dfsan/dfsan_interceptors.cpp
index 7efb182ac8d4f..92be4fc87d493 100644
--- a/compiler-rt/lib/dfsan/dfsan_interceptors.cpp
+++ b/compiler-rt/lib/dfsan/dfsan_interceptors.cpp
@@ -15,8 +15,14 @@
 #include <unistd.h>
 
 #include "dfsan/dfsan.h"
+#include "dfsan/dfsan_thread.h"
 #include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
 #include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_posix.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
 
 using namespace __sanitizer;
 
@@ -26,44 +32,213 @@ bool interceptors_initialized;
 
 }  // namespace
 
-INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
-            int fd, OFF_T offset) {
-  void *res;
+INTERCEPTOR(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size) {
+  return __dfsan::dfsan_reallocarray(ptr, nmemb, size);
+}
+
+INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
+  void *ptr = __dfsan::dfsan_memalign(alignment, size);
+  if (ptr)
+    DTLS_on_libc_memalign(ptr, size);
+  return ptr;
+}
+
+INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
+  return __dfsan::dfsan_aligned_alloc(alignment, size);
+}
+
+static uptr allocated_for_dlsym;
+static const uptr kDlsymAllocPoolSize = 1024;
+static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
+
+static bool IsInDlsymAllocPool(const void *ptr) {
+  uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+  return off < sizeof(alloc_memory_for_dlsym);
+}
+
+static void *AllocateFromLocalPool(uptr size_in_bytes) {
+  uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
+  void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
+  allocated_for_dlsym += size_in_words;
+  CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
+  return mem;
+}
+
+INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
+  if (UNLIKELY(!__dfsan::dfsan_inited))
+    // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+    return AllocateFromLocalPool(nmemb * size);
+  return __dfsan::dfsan_calloc(nmemb, size);
+}
+
+INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
+  if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
+    uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+    uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
+    void *new_ptr;
+    if (UNLIKELY(!__dfsan::dfsan_inited)) {
+      new_ptr = AllocateFromLocalPool(copy_size);
+    } else {
+      copy_size = size;
+      new_ptr = __dfsan::dfsan_malloc(copy_size);
+    }
+    internal_memcpy(new_ptr, ptr, copy_size);
+    return new_ptr;
+  }
+  return __dfsan::dfsan_realloc(ptr, size);
+}
+
+INTERCEPTOR(void *, malloc, SIZE_T size) {
+  if (UNLIKELY(!__dfsan::dfsan_inited))
+    // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
+    return AllocateFromLocalPool(size);
+  return __dfsan::dfsan_malloc(size);
+}
+
+INTERCEPTOR(void, free, void *ptr) {
+  if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr)))
+    return;
+  return __dfsan::dfsan_deallocate(ptr);
+}
+
+INTERCEPTOR(void, cfree, void *ptr) {
+  if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr)))
+    return;
+  return __dfsan::dfsan_deallocate(ptr);
+}
 
-  // interceptors_initialized is set to true during preinit_array, when we're
-  // single-threaded.  So we don't need to worry about accessing it atomically.
-  if (!interceptors_initialized)
-    res = (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
-  else
-    res = REAL(mmap)(addr, length, prot, flags, fd, offset);
+INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
+  CHECK_NE(memptr, 0);
+  int res = __dfsan::dfsan_posix_memalign(memptr, alignment, size);
+  if (!res)
+    dfsan_set_label(0, memptr, sizeof(*memptr));
+  return res;
+}
+
+INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) {
+  return __dfsan::dfsan_memalign(alignment, size);
+}
+
+INTERCEPTOR(void *, valloc, SIZE_T size) { return __dfsan::dfsan_valloc(size); }
+
+INTERCEPTOR(void *, pvalloc, SIZE_T size) {
+  return __dfsan::dfsan_pvalloc(size);
+}
+
+INTERCEPTOR(void, mallinfo, __sanitizer_struct_mallinfo *sret) {
+  internal_memset(sret, 0, sizeof(*sret));
+  dfsan_set_label(0, sret, sizeof(*sret));
+}
+
+INTERCEPTOR(int, mallopt, int cmd, int value) { return 0; }
+
+INTERCEPTOR(void, malloc_stats, void) {
+  // FIXME: implement, but don't call REAL(malloc_stats)!
+}
+
+INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
+  return __sanitizer_get_allocated_size(ptr);
+}
 
-  if (res != (void *)-1)
+#define ENSURE_DFSAN_INITED()               \
+  do {                                      \
+    CHECK(!__dfsan::dfsan_init_is_running); \
+    if (!__dfsan::dfsan_inited) {           \
+      __dfsan::dfsan_init();                \
+    }                                       \
+  } while (0)
+
+#define COMMON_INTERCEPTOR_ENTER(func, ...) \
+  if (__dfsan::dfsan_init_is_running)       \
+    return REAL(func)(__VA_ARGS__);         \
+  ENSURE_DFSAN_INITED();                    \
+  dfsan_set_label(0, __errno_location(), sizeof(int)); /* NOLINT */
+
+INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
+            int fd, OFF_T offset) {
+  if (common_flags()->detect_write_exec)
+    ReportMmapWriteExec(prot);
+  if (!__dfsan::dfsan_inited)
+    return (void *)internal_mmap(addr, length, prot, flags, fd, offset);
+  COMMON_INTERCEPTOR_ENTER(mmap, addr, length, prot, flags, fd, offset);
+  void *res = REAL(mmap)(addr, length, prot, flags, fd, offset);
+  if (res != (void *)-1) {
     dfsan_set_label(0, res, RoundUpTo(length, GetPageSizeCached()));
+  }
   return res;
 }
 
 INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags,
             int fd, OFF64_T offset) {
+  if (common_flags()->detect_write_exec)
+    ReportMmapWriteExec(prot);
+  if (!__dfsan::dfsan_inited)
+    return (void *)internal_mmap(addr, length, prot, flags, fd, offset);
+  COMMON_INTERCEPTOR_ENTER(mmap64, addr, length, prot, flags, fd, offset);
   void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset);
-  if (res != (void *)-1)
+  if (res != (void *)-1) {
     dfsan_set_label(0, res, RoundUpTo(length, GetPageSizeCached()));
+  }
   return res;
 }
 
 INTERCEPTOR(int, munmap, void *addr, SIZE_T length) {
+  if (!__dfsan::dfsan_inited)
+    return internal_munmap(addr, length);
+  COMMON_INTERCEPTOR_ENTER(munmap, addr, length);
   int res = REAL(munmap)(addr, length);
   if (res != -1)
     dfsan_set_label(0, addr, RoundUpTo(length, GetPageSizeCached()));
   return res;
 }
 
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end)           \
+  if (__dfsan::DFsanThread *t = __dfsan::GetCurrentThread()) { \
+    *begin = t->tls_begin();                                   \
+    *end = t->tls_end();                                       \
+  } else {                                                     \
+    *begin = *end = 0;                                         \
+  }
+#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \
+  dfsan_set_label(0, ptr, size)
+
+INTERCEPTOR(void *, __tls_get_addr, void *arg) {
+  COMMON_INTERCEPTOR_ENTER(__tls_get_addr, arg);
+  void *res = REAL(__tls_get_addr)(arg);
+  uptr tls_begin, tls_end;
+  COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end);
+  DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end);
+  if (dtv) {
+    // New DTLS block has been allocated.
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size);
+  }
+  return res;
+}
+
 namespace __dfsan {
-void InitializeInterceptors() {
+void initialize_interceptors() {
   CHECK(!interceptors_initialized);
 
+  INTERCEPT_FUNCTION(aligned_alloc);
+  INTERCEPT_FUNCTION(calloc);
+  INTERCEPT_FUNCTION(cfree);
+  INTERCEPT_FUNCTION(free);
+  INTERCEPT_FUNCTION(mallinfo);
+  INTERCEPT_FUNCTION(malloc);
+  INTERCEPT_FUNCTION(malloc_stats);
+  INTERCEPT_FUNCTION(malloc_usable_size);
+  INTERCEPT_FUNCTION(mallopt);
+  INTERCEPT_FUNCTION(memalign);
   INTERCEPT_FUNCTION(mmap);
   INTERCEPT_FUNCTION(mmap64);
   INTERCEPT_FUNCTION(munmap);
+  INTERCEPT_FUNCTION(posix_memalign);
+  INTERCEPT_FUNCTION(pvalloc);
+  INTERCEPT_FUNCTION(realloc);
+  INTERCEPT_FUNCTION(reallocarray);
+  INTERCEPT_FUNCTION(valloc);
+  INTERCEPT_FUNCTION(__tls_get_addr);
+  INTERCEPT_FUNCTION(__libc_memalign);
 
   interceptors_initialized = true;
 }

diff  --git a/compiler-rt/lib/dfsan/dfsan_new_delete.cpp b/compiler-rt/lib/dfsan/dfsan_new_delete.cpp
new file mode 100644
index 0000000000000..7ac906e81077d
--- /dev/null
+++ b/compiler-rt/lib/dfsan/dfsan_new_delete.cpp
@@ -0,0 +1,124 @@
+//===-- dfsan_new_delete.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of DataflowSanitizer.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+
+#include <stddef.h>
+
+#include "dfsan.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+
+using namespace __dfsan;
+
+// Fake std::nothrow_t and std::align_val_t to avoid including <new>.
+namespace std {
+struct nothrow_t {};
+enum class align_val_t : size_t {};
+}  // namespace std
+
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(nothrow)   \
+  void *res = dfsan_malloc(size);    \
+  if (!nothrow && UNLIKELY(!res)) {  \
+    BufferedStackTrace stack;        \
+    ReportOutOfMemory(size, &stack); \
+  }                                  \
+  return res
+#define OPERATOR_NEW_BODY_ALIGN(nothrow)         \
+  void *res = dfsan_memalign((uptr)align, size); \
+  if (!nothrow && UNLIKELY(!res)) {              \
+    BufferedStackTrace stack;                    \
+    ReportOutOfMemory(size, &stack);             \
+  }                                              \
+  return res;
+
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const &) {
+  OPERATOR_NEW_BODY(true /*nothrow*/);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const &) {
+  OPERATOR_NEW_BODY(true /*nothrow*/);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align) {
+  OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align) {
+  OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align,
+                   std::nothrow_t const &) {
+  OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align,
+                     std::nothrow_t const &) {
+  OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/);
+}
+
+#define OPERATOR_DELETE_BODY \
+  if (ptr)                   \
+  dfsan_deallocate(ptr)
+
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr)NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const &) {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const &) {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size)NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size) NOEXCEPT {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align)NOEXCEPT {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align,
+                     std::nothrow_t const &) {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align,
+                       std::nothrow_t const &) {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size, std::align_val_t align)NOEXCEPT {
+  OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size,
+                       std::align_val_t align) NOEXCEPT {
+  OPERATOR_DELETE_BODY;
+}

diff  --git a/compiler-rt/lib/dfsan/done_abilist.txt b/compiler-rt/lib/dfsan/done_abilist.txt
index 9694af0b95fb4..38aeb881495f7 100644
--- a/compiler-rt/lib/dfsan/done_abilist.txt
+++ b/compiler-rt/lib/dfsan/done_abilist.txt
@@ -41,8 +41,23 @@ fun:dfsan_get_init_origin=discard
 ###############################################################################
 # glibc
 ###############################################################################
+# Functions of memory allocators
+fun:__libc_memalign=discard
+fun:aligned_alloc=discard
+fun:calloc=discard
+fun:cfree=discard
+fun:mallinfo=discard
 fun:malloc=discard
 fun:free=discard
+fun:malloc_stats=discard
+fun:malloc_usable_size=discard
+fun:mallopt=discard
+fun:memalign=discard
+fun:posix_memalign=discard
+fun:pvalloc=discard
+fun:realloc=discard
+fun:reallocarray=discard
+fun:valloc=discard
 
 # Functions that return a value that depends on the input, but the output might
 # not be necessarily data-dependent on the input.
@@ -157,7 +172,6 @@ fun:open=discard
 fun:openat=discard
 fun:pipe=discard
 fun:posix_fadvise=discard
-fun:posix_memalign=discard
 fun:prctl=discard
 fun:printf=discard
 fun:pthread_sigmask=discard
@@ -193,7 +207,6 @@ fun:uselocale=discard
 # Functions that produce output does not depend on the input (need to zero the
 # shadow manually).
 fun:_dl_get_tls_static_info=custom
-fun:calloc=custom
 fun:clock_gettime=custom
 fun:dlopen=custom
 fun:epoll_wait=custom
@@ -401,6 +414,36 @@ fun:__sanitizer_cov_trace_pc*=discard
 fun:__sanitizer_cov_pcs_init=uninstrumented
 fun:__sanitizer_cov_pcs_init=discard
 
+fun:__sanitizer_get_current_allocated_bytes=uninstrumented
+fun:__sanitizer_get_current_allocated_bytes=discard
+fun:__sanitizer_get_heap_size=uninstrumented
+fun:__sanitizer_get_heap_size=discard
+fun:__sanitizer_get_free_bytes=uninstrumented
+fun:__sanitizer_get_free_bytes=discard
+fun:__sanitizer_get_unmapped_bytes=uninstrumented
+fun:__sanitizer_get_unmapped_bytes=discard
+fun:__sanitizer_get_estimated_allocated_size=uninstrumented
+fun:__sanitizer_get_estimated_allocated_size=discard
+fun:__sanitizer_get_ownership=uninstrumented
+fun:__sanitizer_get_ownership=discard
+fun:__sanitizer_get_allocated_size=uninstrumented
+fun:__sanitizer_get_allocated_size=discard
+fun:__sanitizer_print_stack_trace=uninstrumented
+fun:__sanitizer_print_stack_trace=discard
+
+fun:TcmallocSlab_Internal_PushBatch_FixedShift=uninstrumented
+fun:TcmallocSlab_Internal_PushBatch_FixedShift=discard
+fun:TcmallocSlab_Internal_PushBatch_FixedShift_VCPU=uninstrumented
+fun:TcmallocSlab_Internal_PushBatch_FixedShift_VCPU=discard
+fun:TcmallocSlab_Internal_PerCpuCmpxchg64=uninstrumented
+fun:TcmallocSlab_Internal_PerCpuCmpxchg64=discard
+fun:TcmallocSlab_Internal_PerCpuCmpxchg64_VCPU=uninstrumented
+fun:TcmallocSlab_Internal_PerCpuCmpxchg64_VCPU=discard
+fun:TcmallocSlab_Internal_PopBatch_FixedShift=uninstrumented
+fun:TcmallocSlab_Internal_PopBatch_FixedShift=discard
+fun:TcmallocSlab_Internal_PopBatch_FixedShift_VCPU=uninstrumented
+fun:TcmallocSlab_Internal_PopBatch_FixedShift_VCPU=discard
+
 # Ignores the dfsan wrappers.
 fun:__dfsw_*=uninstrumented
 fun:__dfsw_*=discard

diff  --git a/compiler-rt/test/dfsan/custom.cpp b/compiler-rt/test/dfsan/custom.cpp
index 63fa4389e6eaa..d54b40aa7d955 100644
--- a/compiler-rt/test/dfsan/custom.cpp
+++ b/compiler-rt/test/dfsan/custom.cpp
@@ -603,20 +603,6 @@ void test_strchr() {
 #endif
 }
 
-void test_calloc() {
-  // With any luck this sequence of calls will cause calloc to return the same
-  // pointer both times.  This is probably the best we can do to test this
-  // function.
-  char *crv = (char *) calloc(4096, 1);
-  ASSERT_ZERO_LABEL(crv[0]);
-  dfsan_set_label(i_label, crv, 100);
-  free(crv);
-
-  crv = (char *) calloc(4096, 1);
-  ASSERT_ZERO_LABEL(crv[0]);
-  free(crv);
-}
-
 void test_recvmmsg() {
   int sockfds[2];
   int ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sockfds);
@@ -1935,7 +1921,6 @@ int main(void) {
 
   test__dl_get_tls_static_info();
   test_bcmp();
-  test_calloc();
   test_clock_gettime();
   test_ctime_r();
   test_dfsan_set_write_callback();

diff  --git a/compiler-rt/test/dfsan/interceptors.c b/compiler-rt/test/dfsan/interceptors.c
new file mode 100644
index 0000000000000..62a3ad007b4ad
--- /dev/null
+++ b/compiler-rt/test/dfsan/interceptors.c
@@ -0,0 +1,191 @@
+// RUN: %clang_dfsan -mllvm -dfsan-fast-16-labels -mllvm -dfsan-combine-pointer-labels-on-load=false %s -o %t && %run %t
+// RUN: %clang_dfsan -DORIGIN_TRACKING -mllvm -dfsan-fast-16-labels -mllvm -dfsan-track-origins=1 -mllvm -dfsan-combine-pointer-labels-on-load=false %s -o %t && %run %t
+//
+// Tests custom implementations of various glibc functions.
+//
+// REQUIRES: x86_64-target-arch
+
+#include <sanitizer/dfsan_interface.h>
+
+#include <assert.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#define ASSERT_ZERO_LABEL(data) \
+  assert(0 == dfsan_get_label((long) (data)))
+
+#define ASSERT_READ_ZERO_LABEL(ptr, size) \
+  assert(0 == dfsan_read_label(ptr, size))
+
+const int kAlignment = 8;
+const int kSize = 16;
+
+void test_aligned_alloc() {
+  char *p = (char *) aligned_alloc(kAlignment, kSize);
+  ASSERT_ZERO_LABEL(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  free(p);
+}
+
+void test_calloc() {
+  char *p = (char *) calloc(kSize, 1);
+  ASSERT_ZERO_LABEL(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  free(p);
+}
+
+void test_cfree() {
+  // The current glibc does not support cfree.
+}
+
+void test_free() {
+  char *p = (char *) malloc(kSize);
+  dfsan_set_label(1, p, kSize);
+  free(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+}
+
+void test_mallinfo() {
+  struct mallinfo mi = mallinfo();
+  for (int i = 0; i < sizeof(struct mallinfo); ++i) {
+    char c = ((char *)(&mi))[i];
+    assert(!c);
+    ASSERT_ZERO_LABEL(c);
+  }
+}
+
+void test_malloc() {
+  char *p = (char *) malloc(kSize);
+  ASSERT_ZERO_LABEL(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  free(p);
+}
+
+void test_malloc_stats() {
+  // Only ensures it does not crash. Our interceptor of malloc_stats is empty.
+  malloc_stats();
+}
+
+void test_malloc_usable_size() {
+  char *p = (char *) malloc(kSize);
+  size_t s = malloc_usable_size(p);
+  assert(s == kSize);
+  ASSERT_ZERO_LABEL(s);
+  free(p);
+}
+
+void test_mallopt() {
+  int r = mallopt(0, 0);
+  assert(!r);
+  ASSERT_ZERO_LABEL(r);
+}
+
+void test_memalign() {
+  char *p = (char *) memalign(kAlignment, kSize);
+  ASSERT_ZERO_LABEL(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  free(p);
+}
+
+void test_mmap() {
+  char *p = mmap(NULL, kSize, PROT_READ | PROT_WRITE,
+                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  char val = 0xff;
+  dfsan_set_label(1, &val, sizeof(val));
+  memset(p, val, kSize);
+  p = mmap(p, kSize, PROT_READ | PROT_WRITE,
+           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  munmap(p, kSize);
+}
+
+void test_mmap64() {
+  // The current glibc does not support mmap64.
+}
+
+void test_unmmap() {
+  char *p = mmap(NULL, kSize, PROT_READ | PROT_WRITE,
+                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  char val = 0xff;
+  dfsan_set_label(1, &val, sizeof(val));
+  memset(p, val, kSize);
+  munmap(p, kSize);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+}
+
+void test_posix_memalign() {
+  char *p;
+  dfsan_set_label(1, &p, sizeof(p));
+  int r = posix_memalign((void **)&p, kAlignment, kSize);
+  assert(!r);
+  ASSERT_ZERO_LABEL(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  free(p);
+}
+
+void test_pvalloc() {
+  char *p = (char *) pvalloc(kSize);
+  ASSERT_ZERO_LABEL(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  free(p);
+}
+
+void test_realloc() {
+  char *p = (char *) malloc(kSize);
+
+  char *q = (char *) realloc(p, kSize * 2);
+  ASSERT_ZERO_LABEL(q);
+  ASSERT_READ_ZERO_LABEL(q, kSize * 2);
+
+  char *x = (char *) realloc(q, kSize);
+  ASSERT_ZERO_LABEL(x);
+  ASSERT_READ_ZERO_LABEL(x, kSize);
+
+  free(x);
+}
+
+void test_reallocarray() {
+  // The current glibc does not support reallocarray.
+}
+
+void test_valloc() {
+  char *p = (char *) valloc(kSize);
+  ASSERT_ZERO_LABEL(p);
+  ASSERT_READ_ZERO_LABEL(p, kSize);
+  free(p);
+}
+
+void test___libc_memalign() {
+  // The current glibc does not support __libc_memalign.
+}
+
+void test___tls_get_addr() {
+  // The current glibc does not support __tls_get_addr.
+}
+
+int main(void) {
+  // With any luck this sequence of calls will cause allocators to return the
+  // same pointer. This is probably the best we can do to test these functions.
+  test_aligned_alloc();
+  test_calloc();
+  test_cfree();
+  test_free();
+  test_mallinfo();
+  test_malloc();
+  test_malloc_stats();
+  test_malloc_usable_size();
+  test_mallopt();
+  test_memalign();
+  test_mmap();
+  test_mmap64();
+  test_unmmap();
+  test_posix_memalign();
+  test_pvalloc();
+  test_realloc();
+  test_reallocarray();
+  test_valloc();
+  test___libc_memalign();
+  test___tls_get_addr();
+}


        


More information about the llvm-commits mailing list