[compiler-rt] 7904bd9 - [sanitizer_common] Create max_allocation_size_mb flag.

Matt Morehouse via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 30 11:26:27 PDT 2019


Author: Matt Morehouse
Date: 2019-10-30T11:26:05-07:00
New Revision: 7904bd9409b86afbec763d0d95cb75ccef559379

URL: https://github.com/llvm/llvm-project/commit/7904bd9409b86afbec763d0d95cb75ccef559379
DIFF: https://github.com/llvm/llvm-project/commit/7904bd9409b86afbec763d0d95cb75ccef559379.diff

LOG: [sanitizer_common] Create max_allocation_size_mb flag.

Summary:
The flag allows the user to specify a maximum allocation size that the
sanitizers will honor.  Any larger allocations will return nullptr or
crash depending on allocator_may_return_null.

Reviewers: kcc, eugenis

Reviewed By: kcc, eugenis

Subscribers: #sanitizers, llvm-commits

Tags: #sanitizers, #llvm

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

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

Modified: 
    compiler-rt/lib/asan/asan_allocator.cpp
    compiler-rt/lib/lsan/lsan_allocator.cpp
    compiler-rt/lib/msan/msan_allocator.cpp
    compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
    compiler-rt/lib/tsan/rtl/tsan_mman.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index c9e9f5a93d0d..dd78247f5a47 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -246,6 +246,7 @@ struct Allocator {
   AllocatorCache fallback_allocator_cache;
   QuarantineCache fallback_quarantine_cache;
 
+  uptr max_user_defined_malloc_size;
   atomic_uint8_t rss_limit_exceeded;
 
   // ------------------- Options --------------------------
@@ -280,6 +281,10 @@ struct Allocator {
     SetAllocatorMayReturnNull(options.may_return_null);
     allocator.InitLinkerInitialized(options.release_to_os_interval_ms);
     SharedInitCode(options);
+    max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
+                                       ? common_flags()->max_allocation_size_mb
+                                             << 20
+                                       : kMaxAllowedMallocSize;
   }
 
   bool RssLimitExceeded() {
@@ -435,14 +440,16 @@ struct Allocator {
       using_primary_allocator = false;
     }
     CHECK(IsAligned(needed_size, min_alignment));
-    if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
+    if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize ||
+        size > max_user_defined_malloc_size) {
       if (AllocatorMayReturnNull()) {
         Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
                (void*)size);
         return nullptr;
       }
-      ReportAllocationSizeTooBig(size, needed_size, kMaxAllowedMallocSize,
-                                 stack);
+      uptr malloc_limit =
+          Min(kMaxAllowedMallocSize, max_user_defined_malloc_size);
+      ReportAllocationSizeTooBig(size, needed_size, malloc_limit, stack);
     }
 
     AsanThread *t = GetCurrentThread();

diff  --git a/compiler-rt/lib/lsan/lsan_allocator.cpp b/compiler-rt/lib/lsan/lsan_allocator.cpp
index 66a81ab350e5..d86c3921395c 100644
--- a/compiler-rt/lib/lsan/lsan_allocator.cpp
+++ b/compiler-rt/lib/lsan/lsan_allocator.cpp
@@ -36,10 +36,17 @@ static const uptr kMaxAllowedMallocSize = 8UL << 30;
 
 static Allocator allocator;
 
+static uptr max_malloc_size;
+
 void InitializeAllocator() {
   SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
   allocator.InitLinkerInitialized(
       common_flags()->allocator_release_to_os_interval_ms);
+  if (common_flags()->max_allocation_size_mb)
+    max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20,
+                          kMaxAllowedMallocSize);
+  else
+    max_malloc_size = kMaxAllowedMallocSize;
 }
 
 void AllocatorThreadFinish() {
@@ -72,14 +79,14 @@ static void *ReportAllocationSizeTooBig(uptr size, const StackTrace &stack) {
     Report("WARNING: LeakSanitizer failed to allocate 0x%zx bytes\n", size);
     return nullptr;
   }
-  ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, &stack);
+  ReportAllocationSizeTooBig(size, max_malloc_size, &stack);
 }
 
 void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
                bool cleared) {
   if (size == 0)
     size = 1;
-  if (size > kMaxAllowedMallocSize)
+  if (size > max_malloc_size)
     return ReportAllocationSizeTooBig(size, stack);
   void *p = allocator.Allocate(GetAllocatorCache(), size, alignment);
   if (UNLIKELY(!p)) {
@@ -117,7 +124,7 @@ void Deallocate(void *p) {
 void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
                  uptr alignment) {
   RegisterDeallocation(p);
-  if (new_size > kMaxAllowedMallocSize) {
+  if (new_size > max_malloc_size) {
     allocator.Deallocate(GetAllocatorCache(), p);
     return ReportAllocationSizeTooBig(new_size, stack);
   }

diff  --git a/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp
index 6aa4e2738075..a08c1a00d2e5 100644
--- a/compiler-rt/lib/msan/msan_allocator.cpp
+++ b/compiler-rt/lib/msan/msan_allocator.cpp
@@ -115,9 +115,16 @@ static Allocator allocator;
 static AllocatorCache fallback_allocator_cache;
 static StaticSpinMutex fallback_mutex;
 
+static uptr max_malloc_size;
+
 void MsanAllocatorInit() {
   SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
   allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
+  if (common_flags()->max_allocation_size_mb)
+    max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20,
+                          kMaxAllowedMallocSize);
+  else
+    max_malloc_size = kMaxAllowedMallocSize;
 }
 
 AllocatorCache *GetAllocatorCache(MsanThreadLocalMallocStorage *ms) {
@@ -132,12 +139,12 @@ void MsanThreadLocalMallocStorage::CommitBack() {
 
 static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment,
                           bool zeroise) {
-  if (size > kMaxAllowedMallocSize) {
+  if (size > max_malloc_size) {
     if (AllocatorMayReturnNull()) {
       Report("WARNING: MemorySanitizer failed to allocate 0x%zx bytes\n", size);
       return nullptr;
     }
-    ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, stack);
+    ReportAllocationSizeTooBig(size, max_malloc_size, stack);
   }
   MsanThread *t = GetCurrentThread();
   void *allocated;

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
index 7d592bdcb61f..065258a5a6e1 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
@@ -132,6 +132,9 @@ COMMON_FLAG(uptr, soft_rss_limit_mb, 0,
             " until the RSS goes below the soft limit."
             " This limit does not affect memory allocations other than"
             " malloc/new.")
+COMMON_FLAG(uptr, max_allocation_size_mb, 0,
+            "If non-zero, malloc/new calls larger than this size will return "
+            "nullptr (or crash if allocator_may_return_null=false).")
 COMMON_FLAG(bool, heap_profile, false, "Experimental heap profiler, asan-only")
 COMMON_FLAG(s32, allocator_release_to_os_interval_ms,
             ((bool)SANITIZER_FUCHSIA || (bool)SANITIZER_WINDOWS) ? -1 : 5000,

diff  --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
index 1b2c0549d399..743e67bf2f7d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
@@ -113,9 +113,16 @@ ScopedGlobalProcessor::~ScopedGlobalProcessor() {
   gp->mtx.Unlock();
 }
 
+static constexpr uptr kMaxAllowedMallocSize = 1ull << 40;
+static uptr max_user_defined_malloc_size;
+
 void InitializeAllocator() {
   SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
   allocator()->Init(common_flags()->allocator_release_to_os_interval_ms);
+  max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
+                                     ? common_flags()->max_allocation_size_mb
+                                           << 20
+                                     : kMaxAllowedMallocSize;
 }
 
 void InitializeAllocatorLate() {
@@ -150,15 +157,17 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
   OutputReport(thr, rep);
 }
 
-static constexpr uptr kMaxAllowedMallocSize = 1ull << 40;
 
 void *user_alloc_internal(ThreadState *thr, uptr pc, uptr sz, uptr align,
                           bool signal) {
-  if (sz >= kMaxAllowedMallocSize || align >= kMaxAllowedMallocSize) {
+  if (sz >= kMaxAllowedMallocSize || align >= kMaxAllowedMallocSize ||
+      sz > max_user_defined_malloc_size) {
     if (AllocatorMayReturnNull())
       return nullptr;
+    uptr malloc_limit =
+        Min(kMaxAllowedMallocSize, max_user_defined_malloc_size);
     GET_STACK_TRACE_FATAL(thr, pc);
-    ReportAllocationSizeTooBig(sz, kMaxAllowedMallocSize, &stack);
+    ReportAllocationSizeTooBig(sz, malloc_limit, &stack);
   }
   void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align);
   if (UNLIKELY(!p)) {

diff  --git a/compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp b/compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp
new file mode 100644
index 000000000000..9cc799a3245d
--- /dev/null
+++ b/compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp
@@ -0,0 +1,127 @@
+// Test the behavior of malloc/calloc/realloc/new when the allocation size
+// exceeds the configured max_allocation_size_mb flag.
+// By default (allocator_may_return_null=0) the process should crash. With
+// allocator_may_return_null=1 the allocator should return nullptr and set errno
+// to the appropriate error code.
+//
+// RUN: %clangxx -O0 %s -o %t
+// RUN: %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-NOTNULL
+// RUN: %env_tool_opts=max_allocation_size_mb=3 %run %t malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-NOTNULL
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \
+// RUN:   not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
+// RUN:   %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \
+// RUN:   not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
+// RUN:   %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \
+// RUN:   not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
+// RUN:   %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \
+// RUN:   not %run %t realloc-after-malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-mrCRASH
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
+// RUN:   %run %t realloc-after-malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-NULL
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \
+// RUN:   not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-nCRASH
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
+// RUN:   not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-nCRASH-OOM
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \
+// RUN:   not %run %t new-nothrow 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-nnCRASH
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
+// RUN:   %run %t new-nothrow 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
+
+// win32 is disabled due to failing errno tests.
+// UNSUPPORTED: ubsan, windows-msvc
+
+#include <assert.h>
+#include <errno.h>
+#include <limits>
+#include <new>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void *allocate(const char *Action, size_t Size) {
+  if (!strcmp(Action, "malloc"))
+    return malloc(Size);
+  if (!strcmp(Action, "calloc"))
+    return calloc((Size + 3) / 4, 4);
+  if (!strcmp(Action, "realloc"))
+    return realloc(nullptr, Size);
+  if (!strcmp(Action, "realloc-after-malloc")) {
+    void *P = malloc(100);
+    if (void *Ret = realloc(P, Size))
+      return Ret;
+    free(P);
+    return nullptr;
+  }
+  if (!strcmp(Action, "new"))
+    return ::operator new(Size);
+  if (!strcmp(Action, "new-nothrow"))
+    return ::operator new(Size, std::nothrow);
+  assert(0);
+}
+
+static void deallocate(const char *Action, void *Ptr) {
+  if (!strcmp(Action, "malloc") || !strcmp(Action, "calloc") ||
+      !strcmp(Action, "realloc") || !strcmp(Action, "realloc-after-malloc"))
+    return free(Ptr);
+  if (!strcmp(Action, "new"))
+    return ::operator delete(Ptr);
+  if (!strcmp(Action, "new-nothrow"))
+    return ::operator delete(Ptr, std::nothrow);
+  assert(0);
+}
+
+int main(int Argc, char **Argv) {
+  assert(Argc == 2);
+  const char *Action = Argv[1];
+  fprintf(stderr, "%s:\n", Action);
+
+  constexpr size_t MaxAllocationSize = size_t{2} << 20;
+
+  // Should succeed when max_allocation_size_mb is set.
+  void *volatile P = allocate(Action, MaxAllocationSize);
+  assert(P);
+  deallocate(Action, P);
+
+  // Should fail when max_allocation_size_mb is set.
+  P = allocate(Action, MaxAllocationSize + 1);
+  // The NULL pointer is printed 
diff erently on 
diff erent systems, while (long)0
+  // is always the same.
+  fprintf(stderr, "errno: %d, P: %lx\n", errno, (long)P);
+  deallocate(Action, P);
+
+  // Should succeed when max_allocation_size_mb is set.
+  P = allocate(Action, MaxAllocationSize);
+  assert(P);
+  deallocate(Action, P);
+
+  return 0;
+}
+
+// CHECK-mCRASH: malloc:
+// CHECK-mCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
+// CHECK-cCRASH: calloc:
+// CHECK-cCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
+// CHECK-rCRASH: realloc:
+// CHECK-rCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
+// CHECK-mrCRASH: realloc-after-malloc:
+// CHECK-mrCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
+// CHECK-nCRASH: new:
+// CHECK-nCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
+// CHECK-nCRASH-OOM: new:
+// CHECK-nCRASH-OOM: {{SUMMARY: .*Sanitizer: out-of-memory}}
+// CHECK-nnCRASH: new-nothrow:
+// CHECK-nnCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
+
+// CHECK-NULL: {{malloc|calloc|calloc-overflow|realloc|realloc-after-malloc|new-nothrow}}
+// CHECK-NULL: errno: 12, P: 0
+//
+// CHECK-NOTNULL-NOT: P: 0


        


More information about the llvm-commits mailing list