[compiler-rt] r305569 - [Sanitizers] Secondary allocator respects allocator_may_return_null=1.

Alex Shlyapnikov via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 16 11:48:09 PDT 2017


Author: alekseyshl
Date: Fri Jun 16 13:48:08 2017
New Revision: 305569

URL: http://llvm.org/viewvc/llvm-project?rev=305569&view=rev
Log:
[Sanitizers] Secondary allocator respects allocator_may_return_null=1.

Summary:
Context: https://github.com/google/sanitizers/issues/740.

Making secondary allocator to respect allocator_may_return_null=1 flag
and return nullptr when "out of memory" happens.

More changes in primary allocator and operator new will follow.

Reviewers: eugenis

Subscribers: kubamracek, llvm-commits

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

Added:
    compiler-rt/trunk/test/asan/TestCases/Linux/allocator_oom_test.cc
Modified:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator_secondary.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator_secondary.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator_secondary.h?rev=305569&r1=305568&r2=305569&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator_secondary.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_allocator_secondary.h Fri Jun 16 13:48:08 2017
@@ -36,9 +36,12 @@ class LargeMmapAllocator {
     if (alignment > page_size_)
       map_size += alignment;
     // Overflow.
-    if (map_size < size) return ReturnNullOrDieOnBadRequest();
+    if (map_size < size)
+      return ReturnNullOrDieOnBadRequest();
     uptr map_beg = reinterpret_cast<uptr>(
-        MmapOrDie(map_size, "LargeMmapAllocator"));
+        MmapOrDieOnFatalError(map_size, "LargeMmapAllocator"));
+    if (!map_beg)
+      return ReturnNullOrDieOnOOM();
     CHECK(IsAligned(map_beg, page_size_));
     MapUnmapCallback().OnMap(map_beg, map_size);
     uptr map_end = map_beg + map_size;

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h?rev=305569&r1=305568&r2=305569&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_common.h Fri Jun 16 13:48:08 2017
@@ -85,6 +85,9 @@ INLINE void *MmapOrDieQuietly(uptr size,
   return MmapOrDie(size, mem_type, /*raw_report*/ true);
 }
 void UnmapOrDie(void *addr, uptr size);
+// Behaves just like MmapOrDie, but tolerates out of memory condition, in that
+// case returns nullptr.
+void *MmapOrDieOnFatalError(uptr size, const char *mem_type);
 void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
                          const char *name = nullptr);
 void *MmapNoReserveOrDie(uptr size, const char *mem_type);

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.cc?rev=305569&r1=305568&r2=305569&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.cc Fri Jun 16 13:48:08 2017
@@ -22,6 +22,7 @@
 #include "sanitizer_procmaps.h"
 #include "sanitizer_stacktrace.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <sys/mman.h>
@@ -145,6 +146,21 @@ void UnmapOrDie(void *addr, uptr size) {
   DecreaseTotalMmap(size);
 }
 
+void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
+  size = RoundUpTo(size, GetPageSizeCached());
+  uptr res = internal_mmap(nullptr, size,
+                           PROT_READ | PROT_WRITE,
+                           MAP_PRIVATE | MAP_ANON, -1, 0);
+  int reserrno;
+  if (internal_iserror(res, &reserrno)) {
+    if (reserrno == ENOMEM)
+      return nullptr;
+    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
+  }
+  IncreaseTotalMmap(size);
+  return (void *)res;
+}
+
 // We want to map a chunk of address space aligned to 'alignment'.
 // We do it by maping a bit more and then unmaping redundant pieces.
 // We probably can do it with fewer syscalls in some OS-dependent way.

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc?rev=305569&r1=305568&r2=305569&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_win.cc Fri Jun 16 13:48:08 2017
@@ -131,6 +131,16 @@ void UnmapOrDie(void *addr, uptr size) {
   }
 }
 
+void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
+  void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+  if (rv == 0) {
+    error_t last_error = GetLastError();
+    if (last_error != ERROR_NOT_ENOUGH_MEMORY)
+      ReportMmapFailureAndDie(size, mem_type, "allocate", last_error);
+  }
+  return rv;
+}
+
 // We want to map a chunk of address space aligned to 'alignment'.
 void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
   CHECK(IsPowerOfTwo(size));

Added: compiler-rt/trunk/test/asan/TestCases/Linux/allocator_oom_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/allocator_oom_test.cc?rev=305569&view=auto
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Linux/allocator_oom_test.cc (added)
+++ compiler-rt/trunk/test/asan/TestCases/Linux/allocator_oom_test.cc Fri Jun 16 13:48:08 2017
@@ -0,0 +1,82 @@
+// Test the behavior of malloc/calloc/realloc when the allocation causes OOM
+// in the secondary allocator.
+// By default (allocator_may_return_null=0) the process should crash.
+// With allocator_may_return_null=1 the allocator should return 0.
+// Set the limit to 20.5T on 64 bits to account for ASan shadow memory,
+// allocator buffers etc. so that the test allocation of ~1T will trigger OOM.
+// Limit this test to Linux since we're relying on allocator internal
+// limits (shadow memory size, allocation limits etc.)
+
+// RUN: %clangxx_asan -O0 %s -o %t
+// RUN: ulimit -v 22024290304
+// RUN: not %run %t malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-MALLOC,CHECK-CRASH
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-MALLOC,CHECK-CRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1     %run %t malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-MALLOC,CHECK-NULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-CALLOC,CHECK-CRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1     %run %t calloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-CALLOC,CHECK-NULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-REALLOC,CHECK-CRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1     %run %t realloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-REALLOC,CHECK-NULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-MALLOC-REALLOC,CHECK-CRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1     %run %t realloc-after-malloc 2>&1 \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-MALLOC-REALLOC,CHECK-NULL
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+int main(int argc, char **argv) {
+  assert(argc == 2);
+  const char *action = argv[1];
+  fprintf(stderr, "%s:\n", action);
+
+  // Allocate just a bit less than max allocation size enforced by ASan's
+  // allocator (currently 1T and 3G).
+  const size_t size =
+#if __LP64__
+      (1ULL << 40) - (1ULL << 30);
+#else
+      (3ULL << 30) - (1ULL << 20);
+#endif
+
+  void *x = 0;
+
+  if (!strcmp(action, "malloc")) {
+    x = malloc(size);
+  } else if (!strcmp(action, "calloc")) {
+    x = calloc(size / 4, 4);
+  } else if (!strcmp(action, "realloc")) {
+    x = realloc(0, size);
+  } else if (!strcmp(action, "realloc-after-malloc")) {
+    char *t = (char*)malloc(100);
+    *t = 42;
+    x = realloc(t, size);
+    assert(*t == 42);
+    free(t);
+  } else {
+    assert(0);
+  }
+
+  // The NULL pointer is printed differently on different systems, while (long)0
+  // is always the same.
+  fprintf(stderr, "x: %lx\n", (long)x);
+  free(x);
+
+  return x != 0;
+}
+
+// CHECK-MALLOC: malloc:
+// CHECK-CALLOC: calloc:
+// CHECK-REALLOC: realloc:
+// CHECK-MALLOC-REALLOC: realloc-after-malloc:
+
+// CHECK-CRASH: AddressSanitizer's allocator is terminating the process
+// CHECK-NULL: x: 0




More information about the llvm-commits mailing list