summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/asan/asan_allocator.cc11
-rw-r--r--lib/asan/asan_new_delete.cc39
-rw-r--r--lib/lsan/lsan_interceptors.cc23
-rw-r--r--lib/msan/msan_new_delete.cc20
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.cc4
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.h4
-rw-r--r--lib/scudo/scudo_new_delete.cpp9
-rw-r--r--lib/tsan/rtl/tsan_new_delete.cc13
-rw-r--r--test/asan/TestCases/allocator_returns_null.cc107
-rw-r--r--test/msan/allocator_returns_null.cc115
-rw-r--r--test/scudo/sizes.cpp45
-rw-r--r--test/tsan/allocator_returns_null.cc116
12 files changed, 342 insertions, 164 deletions
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index 57651f49c..36348834e 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -160,7 +160,11 @@ struct QuarantineCallback {
}
void *Allocate(uptr size) {
- return get_allocator().Allocate(cache_, size, 1);
+ void *res = get_allocator().Allocate(cache_, size, 1);
+ // TODO(alekseys): Consider making quarantine OOM-friendly.
+ if (UNLIKELY(!res))
+ return DieOnFailure::OnOOM();
+ return res;
}
void Deallocate(void *p) {
@@ -524,8 +528,7 @@ struct Allocator {
// Expects the chunk to already be marked as quarantined by using
// AtomicallySetQuarantineFlagIfAllocated.
- void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack,
- AllocType alloc_type) {
+ void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) {
CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
CHECK_GE(m->alloc_tid, 0);
if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
@@ -603,7 +606,7 @@ struct Allocator {
ReportNewDeleteSizeMismatch(p, delete_size, stack);
}
- QuarantineChunk(m, ptr, stack, alloc_type);
+ QuarantineChunk(m, ptr, stack);
}
void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) {
diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc
index 3283fb394..a64fe583b 100644
--- a/lib/asan/asan_new_delete.cc
+++ b/lib/asan/asan_new_delete.cc
@@ -63,12 +63,17 @@ struct nothrow_t {};
enum class align_val_t: size_t {};
} // namespace std
-#define OPERATOR_NEW_BODY(type) \
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(type, nothrow) \
GET_STACK_TRACE_MALLOC;\
- return asan_memalign(0, size, &stack, type);
-#define OPERATOR_NEW_BODY_ALIGN(type) \
+ void *res = asan_memalign(0, size, &stack, type);\
+ if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\
+ return res;
+#define OPERATOR_NEW_BODY_ALIGN(type, nothrow) \
GET_STACK_TRACE_MALLOC;\
- return asan_memalign((uptr)align, size, &stack, type);
+ void *res = asan_memalign((uptr)align, size, &stack, type);\
+ if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\
+ return res;
// On OS X it's not enough to just provide our own 'operator new' and
// 'operator delete' implementations, because they're going to be in the
@@ -79,40 +84,42 @@ enum class align_val_t: size_t {};
// OS X we need to intercept them using their mangled names.
#if !SANITIZER_MAC
CXX_OPERATOR_ATTRIBUTE
-void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
+void *operator new(size_t size)
+{ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); }
CXX_OPERATOR_ATTRIBUTE
-void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
+void *operator new[](size_t size)
+{ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); }
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(FROM_NEW); }
+{ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); }
CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
+{ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); }
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::align_val_t align)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); }
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, false /*nothrow*/); }
CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::align_val_t align)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); }
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, false /*nothrow*/); }
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); }
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, true /*nothrow*/); }
CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); }
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/); }
#else // SANITIZER_MAC
INTERCEPTOR(void *, _Znwm, size_t size) {
- OPERATOR_NEW_BODY(FROM_NEW);
+ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/);
}
INTERCEPTOR(void *, _Znam, size_t size) {
- OPERATOR_NEW_BODY(FROM_NEW_BR);
+ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/);
}
INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(FROM_NEW);
+ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/);
}
INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(FROM_NEW_BR);
+ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/);
}
#endif
diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc
index 9e39a7d19..b6ac7b77c 100644
--- a/lib/lsan/lsan_interceptors.cc
+++ b/lib/lsan/lsan_interceptors.cc
@@ -199,19 +199,26 @@ INTERCEPTOR(int, mprobe, void *ptr) {
}
#endif // SANITIZER_INTERCEPT_MCHECK_MPROBE
-#define OPERATOR_NEW_BODY \
- ENSURE_LSAN_INITED; \
- GET_STACK_TRACE_MALLOC; \
- return Allocate(stack, size, 1, kAlwaysClearMemory);
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(nothrow) \
+ ENSURE_LSAN_INITED; \
+ GET_STACK_TRACE_MALLOC; \
+ void *res = Allocate(stack, size, 1, kAlwaysClearMemory);\
+ if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\
+ return res;
INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
+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; }
+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; }
+void *operator new[](size_t size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(true /*nothrow*/);
+}
#define OPERATOR_DELETE_BODY \
ENSURE_LSAN_INITED; \
diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc
index 540100316..c7295feeb 100644
--- a/lib/msan/msan_new_delete.cc
+++ b/lib/msan/msan_new_delete.cc
@@ -14,6 +14,7 @@
#include "msan.h"
#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator.h"
#if MSAN_REPLACE_OPERATORS_NEW_AND_DELETE
@@ -27,18 +28,25 @@ namespace std {
} // namespace std
-#define OPERATOR_NEW_BODY \
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(nothrow) \
GET_MALLOC_STACK_TRACE; \
- return MsanReallocate(&stack, 0, size, sizeof(u64), false)
+ void *res = MsanReallocate(&stack, 0, size, sizeof(u64), false);\
+ if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\
+ return res
INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
+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; }
+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; }
+void *operator new[](size_t size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(true /*nothrow*/);
+}
#define OPERATOR_DELETE_BODY \
GET_MALLOC_STACK_TRACE; \
diff --git a/lib/sanitizer_common/sanitizer_allocator.cc b/lib/sanitizer_common/sanitizer_allocator.cc
index db3ebb033..e081cc576 100644
--- a/lib/sanitizer_common/sanitizer_allocator.cc
+++ b/lib/sanitizer_common/sanitizer_allocator.cc
@@ -246,11 +246,11 @@ void *ReturnNullOrDieOnFailure::OnOOM() {
ReportAllocatorCannotReturnNull();
}
-void *DieOnFailure::OnBadRequest() {
+void NORETURN *DieOnFailure::OnBadRequest() {
ReportAllocatorCannotReturnNull();
}
-void *DieOnFailure::OnOOM() {
+void NORETURN *DieOnFailure::OnOOM() {
atomic_store_relaxed(&allocator_out_of_memory, 1);
ReportAllocatorCannotReturnNull();
}
diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h
index f59c13d1c..06f82d3a2 100644
--- a/lib/sanitizer_common/sanitizer_allocator.h
+++ b/lib/sanitizer_common/sanitizer_allocator.h
@@ -39,8 +39,8 @@ struct ReturnNullOrDieOnFailure {
};
// Always dies on the failure.
struct DieOnFailure {
- static void *OnBadRequest();
- static void *OnOOM();
+ static void NORETURN *OnBadRequest();
+ static void NORETURN *OnOOM();
};
// Returns true if allocator detected OOM condition. Can be used to avoid memory
diff --git a/lib/scudo/scudo_new_delete.cpp b/lib/scudo/scudo_new_delete.cpp
index c022bd0ac..cdefb127b 100644
--- a/lib/scudo/scudo_new_delete.cpp
+++ b/lib/scudo/scudo_new_delete.cpp
@@ -26,13 +26,18 @@ namespace std {
struct nothrow_t {};
} // namespace std
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size) {
- return scudoMalloc(size, FromNew);
+ void *res = scudoMalloc(size, FromNew);
+ if (UNLIKELY(!res)) DieOnFailure::OnOOM();
+ return res;
}
CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size) {
- return scudoMalloc(size, FromNewArray);
+ void *res = scudoMalloc(size, FromNewArray);
+ if (UNLIKELY(!res)) DieOnFailure::OnOOM();
+ return res;
}
CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::nothrow_t const&) {
diff --git a/lib/tsan/rtl/tsan_new_delete.cc b/lib/tsan/rtl/tsan_new_delete.cc
index b6478bb08..4d03145c1 100644
--- a/lib/tsan/rtl/tsan_new_delete.cc
+++ b/lib/tsan/rtl/tsan_new_delete.cc
@@ -12,6 +12,7 @@
// Interceptors for operators new and delete.
//===----------------------------------------------------------------------===//
#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "tsan_interceptors.h"
@@ -24,13 +25,15 @@ struct nothrow_t {};
DECLARE_REAL(void *, malloc, uptr size)
DECLARE_REAL(void, free, void *ptr)
-#define OPERATOR_NEW_BODY(mangled_name) \
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(mangled_name, nothrow) \
if (cur_thread()->in_symbolizer) \
return InternalAlloc(size); \
void *p = 0; \
{ \
SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
p = user_alloc(thr, pc, size); \
+ if (!nothrow && UNLIKELY(!p)) DieOnFailure::OnOOM(); \
} \
invoke_malloc_hook(p, size); \
return p;
@@ -38,25 +41,25 @@ DECLARE_REAL(void, free, void *ptr)
SANITIZER_INTERFACE_ATTRIBUTE
void *operator new(__sanitizer::uptr size);
void *operator new(__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znwm);
+ OPERATOR_NEW_BODY(_Znwm, false /*nothrow*/);
}
SANITIZER_INTERFACE_ATTRIBUTE
void *operator new[](__sanitizer::uptr size);
void *operator new[](__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znam);
+ OPERATOR_NEW_BODY(_Znam, false /*nothrow*/);
}
SANITIZER_INTERFACE_ATTRIBUTE
void *operator new(__sanitizer::uptr size, std::nothrow_t const&);
void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t);
+ OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t, true /*nothrow*/);
}
SANITIZER_INTERFACE_ATTRIBUTE
void *operator new[](__sanitizer::uptr size, std::nothrow_t const&);
void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t);
+ OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t, true /*nothrow*/);
}
#define OPERATOR_DELETE_BODY(mangled_name) \
diff --git a/test/asan/TestCases/allocator_returns_null.cc b/test/asan/TestCases/allocator_returns_null.cc
index cdfcd90c9..90e25b55e 100644
--- a/test/asan/TestCases/allocator_returns_null.cc
+++ b/test/asan/TestCases/allocator_returns_null.cc
@@ -1,68 +1,97 @@
-// Test the behavior of malloc/calloc/realloc when the allocation size is huge.
+// Test the behavior of malloc/calloc/realloc/new when the allocation size is
+// more than ASan allocator's max allowed one.
// By default (allocator_may_return_null=0) the process should crash.
-// With allocator_may_return_null=1 the allocator should return 0.
+// With allocator_may_return_null=1 the allocator should return 0, except the
+// operator new(), which should crash anyway (operator new(std::nothrow) should
+// return nullptr, indeed).
//
// RUN: %clangxx_asan -O0 %s -o %t
// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL
-// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
-// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL
-// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH
-// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL
-// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
-// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL
-// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH
-// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mNULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-cNULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-coNULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-rNULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t new 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1 not %run %t new 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH
+// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL
-#include <limits.h>
-#include <stdlib.h>
+#include <assert.h>
#include <string.h>
#include <stdio.h>
-#include <assert.h>
+#include <stdlib.h>
#include <limits>
+#include <new>
+
int main(int argc, char **argv) {
// Disable stderr buffering. Needed on Windows.
setvbuf(stderr, NULL, _IONBF, 0);
- volatile size_t size = std::numeric_limits<size_t>::max() - 10000;
assert(argc == 2);
- void *x = 0;
- if (!strcmp(argv[1], "malloc")) {
- fprintf(stderr, "malloc:\n");
- x = malloc(size);
- }
- if (!strcmp(argv[1], "calloc")) {
- fprintf(stderr, "calloc:\n");
- x = calloc(size / 4, 4);
- }
+ const char *action = argv[1];
+ fprintf(stderr, "%s:\n", action);
- if (!strcmp(argv[1], "calloc-overflow")) {
- fprintf(stderr, "calloc-overflow:\n");
+ static const size_t kMaxAllowedMallocSizePlusOne =
+#if __LP64__ || defined(_WIN64)
+ (1ULL << 40) + 1;
+#else
+ (3UL << 30) + 1;
+#endif
+
+ void *x = 0;
+ if (!strcmp(action, "malloc")) {
+ x = malloc(kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "calloc")) {
+ x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4);
+ } else if (!strcmp(action, "calloc-overflow")) {
volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
size_t kArraySize = 4096;
volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
x = calloc(kArraySize, kArraySize2);
- }
-
- if (!strcmp(argv[1], "realloc")) {
- fprintf(stderr, "realloc:\n");
- x = realloc(0, size);
- }
- if (!strcmp(argv[1], "realloc-after-malloc")) {
- fprintf(stderr, "realloc-after-malloc:\n");
+ } else if (!strcmp(action, "realloc")) {
+ x = realloc(0, kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "realloc-after-malloc")) {
char *t = (char*)malloc(100);
*t = 42;
- x = realloc(t, size);
+ x = realloc(t, kMaxAllowedMallocSizePlusOne);
assert(*t == 42);
free(t);
+ } else if (!strcmp(action, "new")) {
+ x = operator new(kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "new-nothrow")) {
+ x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow);
+ } 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-mCRASH: malloc:
// CHECK-mCRASH: AddressSanitizer's allocator is terminating the process
// CHECK-cCRASH: calloc:
@@ -73,6 +102,10 @@ int main(int argc, char **argv) {
// CHECK-rCRASH: AddressSanitizer's allocator is terminating the process
// CHECK-mrCRASH: realloc-after-malloc:
// CHECK-mrCRASH: AddressSanitizer's allocator is terminating the process
+// CHECK-nCRASH: new:
+// CHECK-nCRASH: AddressSanitizer's allocator is terminating the process
+// CHECK-nnCRASH: new-nothrow:
+// CHECK-nnCRASH: AddressSanitizer's allocator is terminating the process
// CHECK-mNULL: malloc:
// CHECK-mNULL: x: 0
@@ -84,3 +117,5 @@ int main(int argc, char **argv) {
// CHECK-rNULL: x: 0
// CHECK-mrNULL: realloc-after-malloc:
// CHECK-mrNULL: x: 0
+// CHECK-nnNULL: new-nothrow:
+// CHECK-nnNULL: x: 0
diff --git a/test/msan/allocator_returns_null.cc b/test/msan/allocator_returns_null.cc
index f4ea51d58..2c7c32d40 100644
--- a/test/msan/allocator_returns_null.cc
+++ b/test/msan/allocator_returns_null.cc
@@ -1,63 +1,98 @@
-// Test the behavior of malloc/calloc/realloc when the allocation size is huge.
+// Test the behavior of malloc/calloc/realloc/new when the allocation size is
+// more than MSan allocator's max allowed one.
// By default (allocator_may_return_null=0) the process should crash.
-// With allocator_may_return_null=1 the allocator should return 0.
+// With allocator_may_return_null=1 the allocator should return 0, except the
+// operator new(), which should crash anyway (operator new(std::nothrow) should
+// return nullptr, indeed).
//
// RUN: %clangxx_msan -O0 %s -o %t
// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL
-// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
-// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL
-// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH
-// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL
-// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
-// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL
-// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH
-// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL
+// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mNULL
+// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-cNULL
+// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-coNULL
+// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-rNULL
+// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL
+// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t new 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=1 not %run %t new 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH
+// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL
-#include <limits.h>
-#include <stdlib.h>
+
+#include <assert.h>
#include <string.h>
#include <stdio.h>
-#include <assert.h>
+#include <stdlib.h>
#include <limits>
+#include <new>
+
int main(int argc, char **argv) {
- volatile size_t size = std::numeric_limits<size_t>::max() - 10000;
+ // Disable stderr buffering. Needed on Windows.
+ setvbuf(stderr, NULL, _IONBF, 0);
+
assert(argc == 2);
- char *x = 0;
- if (!strcmp(argv[1], "malloc")) {
- fprintf(stderr, "malloc:\n");
- x = (char*)malloc(size);
- }
- if (!strcmp(argv[1], "calloc")) {
- fprintf(stderr, "calloc:\n");
- x = (char*)calloc(size / 4, 4);
- }
+ const char *action = argv[1];
+ fprintf(stderr, "%s:\n", action);
+
+ static const size_t kMaxAllowedMallocSizePlusOne =
+#if __LP64__ || defined(_WIN64)
+ (8UL << 30) + 1;
+#else
+ (2UL << 30) + 1;
+#endif
- if (!strcmp(argv[1], "calloc-overflow")) {
- fprintf(stderr, "calloc-overflow:\n");
+ void *x = 0;
+ if (!strcmp(action, "malloc")) {
+ x = malloc(kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "calloc")) {
+ x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4);
+ } else if (!strcmp(action, "calloc-overflow")) {
volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
size_t kArraySize = 4096;
volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
- x = (char*)calloc(kArraySize, kArraySize2);
- }
-
- if (!strcmp(argv[1], "realloc")) {
- fprintf(stderr, "realloc:\n");
- x = (char*)realloc(0, size);
- }
- if (!strcmp(argv[1], "realloc-after-malloc")) {
- fprintf(stderr, "realloc-after-malloc:\n");
+ x = calloc(kArraySize, kArraySize2);
+ } else if (!strcmp(action, "realloc")) {
+ x = realloc(0, kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "realloc-after-malloc")) {
char *t = (char*)malloc(100);
*t = 42;
- x = (char*)realloc(t, size);
+ x = realloc(t, kMaxAllowedMallocSizePlusOne);
assert(*t == 42);
+ free(t);
+ } else if (!strcmp(action, "new")) {
+ x = operator new(kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "new-nothrow")) {
+ x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow);
+ } 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-mCRASH: malloc:
// CHECK-mCRASH: MemorySanitizer's allocator is terminating the process
// CHECK-cCRASH: calloc:
@@ -68,6 +103,10 @@ int main(int argc, char **argv) {
// CHECK-rCRASH: MemorySanitizer's allocator is terminating the process
// CHECK-mrCRASH: realloc-after-malloc:
// CHECK-mrCRASH: MemorySanitizer's allocator is terminating the process
+// CHECK-nCRASH: new:
+// CHECK-nCRASH: MemorySanitizer's allocator is terminating the process
+// CHECK-nnCRASH: new-nothrow:
+// CHECK-nnCRASH: MemorySanitizer's allocator is terminating the process
// CHECK-mNULL: malloc:
// CHECK-mNULL: x: 0
@@ -79,3 +118,5 @@ int main(int argc, char **argv) {
// CHECK-rNULL: x: 0
// CHECK-mrNULL: realloc-after-malloc:
// CHECK-mrNULL: x: 0
+// CHECK-nnNULL: new-nothrow:
+// CHECK-nnNULL: x: 0
diff --git a/test/scudo/sizes.cpp b/test/scudo/sizes.cpp
index 981b859a8..a0994c251 100644
--- a/test/scudo/sizes.cpp
+++ b/test/scudo/sizes.cpp
@@ -1,8 +1,12 @@
-// RUN: %clang_scudo %s -o %t
+// RUN: %clang_scudo %s -lstdc++ -o %t
// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s
// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1
// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s
// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1
+// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s
+// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s
+// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s
+// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t new-nothrow 2>&1
// RUN: %run %t usable 2>&1
// Tests for various edge cases related to sizes, notably the maximum size the
@@ -15,26 +19,38 @@
#include <string.h>
#include <limits>
+#include <new>
-int main(int argc, char **argv)
-{
+int main(int argc, char **argv) {
assert(argc == 2);
- if (!strcmp(argv[1], "malloc")) {
- // Currently the maximum size the allocator can allocate is 1ULL<<40 bytes.
- size_t size = std::numeric_limits<size_t>::max();
- void *p = malloc(size);
+ const char *action = argv[1];
+ fprintf(stderr, "%s:\n", action);
+
+#if __LP64__ || defined(_WIN64)
+ static const size_t kMaxAllowedMallocSize = 1ULL << 40;
+ static const size_t kChunkHeaderSize = 16;
+#else
+ static const size_t kMaxAllowedMallocSize = 2UL << 30;
+ static const size_t kChunkHeaderSize = 8;
+#endif
+
+ if (!strcmp(action, "malloc")) {
+ void *p = malloc(kMaxAllowedMallocSize);
assert(!p);
- size = (1ULL << 40) - 16;
- p = malloc(size);
+ p = malloc(kMaxAllowedMallocSize - kChunkHeaderSize);
assert(!p);
- }
- if (!strcmp(argv[1], "calloc")) {
+ } else if (!strcmp(action, "calloc")) {
// Trigger an overflow in calloc.
size_t size = std::numeric_limits<size_t>::max();
void *p = calloc((size / 0x1000) + 1, 0x1000);
assert(!p);
- }
- if (!strcmp(argv[1], "usable")) {
+ } else if (!strcmp(action, "new")) {
+ void *p = operator new(kMaxAllowedMallocSize);
+ assert(!p);
+ } else if (!strcmp(action, "new-nothrow")) {
+ void *p = operator new(kMaxAllowedMallocSize, std::nothrow);
+ assert(!p);
+ } else if (!strcmp(action, "usable")) {
// Playing with the actual usable size of a chunk.
void *p = malloc(1007);
assert(p);
@@ -47,7 +63,10 @@ int main(int argc, char **argv)
assert(size >= 2014);
memset(p, 'B', size);
free(p);
+ } else {
+ assert(0);
}
+
return 0;
}
diff --git a/test/tsan/allocator_returns_null.cc b/test/tsan/allocator_returns_null.cc
index 66930076a..852dda035 100644
--- a/test/tsan/allocator_returns_null.cc
+++ b/test/tsan/allocator_returns_null.cc
@@ -1,56 +1,90 @@
-// Test the behavior of malloc/calloc/realloc when the allocation size is huge.
+// Test the behavior of malloc/calloc/realloc/new when the allocation size is
+// more than TSan allocator's max allowed one.
// By default (allocator_may_return_null=0) the process should crash.
-// With allocator_may_return_null=1 the allocator should return 0.
+// With allocator_may_return_null=1 the allocator should return 0, except the
+// operator new(), which should crash anyway (operator new(std::nothrow) should
+// return nullptr, indeed).
//
// RUN: %clangxx_tsan -O0 %s -o %t
// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
-// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH
-// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
-// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mNULL
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-cNULL
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-coNULL
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-rNULL
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t new 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=1 not %run %t new 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL
-#include <limits.h>
-#include <stdlib.h>
+#include <assert.h>
#include <string.h>
#include <stdio.h>
-#include <assert.h>
+#include <stdlib.h>
#include <limits>
+#include <new>
+
int main(int argc, char **argv) {
- volatile size_t size = std::numeric_limits<size_t>::max() - 10000;
+ // Disable stderr buffering. Needed on Windows.
+ setvbuf(stderr, NULL, _IONBF, 0);
+
assert(argc == 2);
- char *x = 0;
- if (!strcmp(argv[1], "malloc")) {
- fprintf(stderr, "malloc:\n");
- x = (char*)malloc(size);
- }
- if (!strcmp(argv[1], "calloc")) {
- fprintf(stderr, "calloc:\n");
- x = (char*)calloc(size / 4, 4);
- }
+ const char *action = argv[1];
+ fprintf(stderr, "%s:\n", action);
+
+ static const size_t kMaxAllowedMallocSizePlusOne = (1ULL << 40) + 1;
- if (!strcmp(argv[1], "calloc-overflow")) {
- fprintf(stderr, "calloc-overflow:\n");
+ void *x = 0;
+ if (!strcmp(action, "malloc")) {
+ x = malloc(kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "calloc")) {
+ x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4);
+ } else if (!strcmp(action, "calloc-overflow")) {
volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
size_t kArraySize = 4096;
volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
- x = (char*)calloc(kArraySize, kArraySize2);
- }
-
- if (!strcmp(argv[1], "realloc")) {
- fprintf(stderr, "realloc:\n");
- x = (char*)realloc(0, size);
- }
- if (!strcmp(argv[1], "realloc-after-malloc")) {
- fprintf(stderr, "realloc-after-malloc:\n");
+ x = calloc(kArraySize, kArraySize2);
+ } else if (!strcmp(action, "realloc")) {
+ x = realloc(0, kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "realloc-after-malloc")) {
char *t = (char*)malloc(100);
*t = 42;
- x = (char*)realloc(t, size);
+ x = realloc(t, kMaxAllowedMallocSizePlusOne);
assert(*t == 42);
+ } else if (!strcmp(action, "new")) {
+ x = operator new(kMaxAllowedMallocSizePlusOne);
+ } else if (!strcmp(action, "new-nothrow")) {
+ x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow);
+ } else {
+ assert(0);
}
- fprintf(stderr, "x: %p\n", x);
+
+ // 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-mCRASH: malloc:
// CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process
// CHECK-cCRASH: calloc:
@@ -61,4 +95,20 @@ int main(int argc, char **argv) {
// CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process
// CHECK-mrCRASH: realloc-after-malloc:
// CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process
+// CHECK-nCRASH: new:
+// CHECK-nCRASH: ThreadSanitizer's allocator is terminating the process
+// CHECK-nnCRASH: new-nothrow:
+// CHECK-nnCRASH: ThreadSanitizer's allocator is terminating the process
+// CHECK-mNULL: malloc:
+// CHECK-mNULL: x: 0
+// CHECK-cNULL: calloc:
+// CHECK-cNULL: x: 0
+// CHECK-coNULL: calloc-overflow:
+// CHECK-coNULL: x: 0
+// CHECK-rNULL: realloc:
+// CHECK-rNULL: x: 0
+// CHECK-mrNULL: realloc-after-malloc:
+// CHECK-mrNULL: x: 0
+// CHECK-nnNULL: new-nothrow:
+// CHECK-nnNULL: x: 0