diff options
| author | Alex Shlyapnikov <alekseys@google.com> | 2018-06-18 20:03:31 +0000 |
|---|---|---|
| committer | Alex Shlyapnikov <alekseys@google.com> | 2018-06-18 20:03:31 +0000 |
| commit | c75d47b52d10e68a55594802ca4fd58dad1c363e (patch) | |
| tree | 08ac84c30d32869c7b885bc2ddf0da1478d511bb /compiler-rt | |
| parent | 80274b1ce74d3c415798e7bba81439ba33693f1a (diff) | |
| download | bcm5719-llvm-c75d47b52d10e68a55594802ca4fd58dad1c363e.tar.gz bcm5719-llvm-c75d47b52d10e68a55594802ca4fd58dad1c363e.zip | |
[TSan] Report proper error on allocator failures instead of CHECK(0)-ing
Summary:
Following up on and complementing D44404 and other sanitizer allocators.
Currently many allocator specific errors (OOM, for example) are reported as
a text message and CHECK(0) termination, no stack, no details, not too
helpful nor informative. To improve the situation, detailed and structured
common errors were defined and reported under the appropriate conditions.
Common tests were generalized a bit to cover a slightly different TSan
stack reporting format, extended to verify errno value and returned
pointer value check is now explicit to facilitate debugging.
Reviewers: dvyukov
Subscribers: srhines, kubamracek, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D48087
llvm-svn: 334975
Diffstat (limited to 'compiler-rt')
11 files changed, 126 insertions, 212 deletions
diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cc b/compiler-rt/lib/tsan/rtl/tsan_mman.cc index 39c0d860704..b160a9736d5 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_mman.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cc @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator_checks.h" #include "sanitizer_common/sanitizer_allocator_interface.h" +#include "sanitizer_common/sanitizer_allocator_report.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_errno.h" #include "sanitizer_common/sanitizer_placement_new.h" @@ -150,13 +151,24 @@ 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 >= (1ull << 40)) || (align >= (1ull << 40))) - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (sz >= kMaxAllowedMallocSize || align >= kMaxAllowedMallocSize) { + if (AllocatorMayReturnNull()) + return nullptr; + GET_STACK_TRACE_FATAL(thr, pc); + ReportAllocationSizeTooBig(sz, kMaxAllowedMallocSize, &stack); + } void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align); - if (UNLIKELY(p == 0)) - return ReturnNullOrDieOnFailure::OnOOM(); + if (UNLIKELY(!p)) { + SetAllocatorOutOfMemory(); + if (AllocatorMayReturnNull()) + return nullptr; + GET_STACK_TRACE_FATAL(thr, pc); + ReportOutOfMemory(sz, &stack); + } if (ctx && ctx->initialized) OnUserAlloc(thr, pc, (uptr)p, sz, true); if (signal) @@ -178,8 +190,12 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz) { } void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { - if (UNLIKELY(CheckForCallocOverflow(size, n))) - return SetErrnoOnNull(ReturnNullOrDieOnFailure::OnBadRequest()); + if (UNLIKELY(CheckForCallocOverflow(size, n))) { + if (AllocatorMayReturnNull()) + return SetErrnoOnNull(nullptr); + GET_STACK_TRACE_FATAL(thr, pc); + ReportCallocOverflow(n, size, &stack); + } void *p = user_alloc_internal(thr, pc, n * size); if (p) internal_memset(p, 0, n * size); @@ -224,7 +240,10 @@ void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { void *user_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz) { if (UNLIKELY(!IsPowerOfTwo(align))) { errno = errno_EINVAL; - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (AllocatorMayReturnNull()) + return nullptr; + GET_STACK_TRACE_FATAL(thr, pc); + ReportInvalidAllocationAlignment(align, &stack); } return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align)); } @@ -232,11 +251,14 @@ void *user_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz) { int user_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align, uptr sz) { if (UNLIKELY(!CheckPosixMemalignAlignment(align))) { - ReturnNullOrDieOnFailure::OnBadRequest(); - return errno_EINVAL; + if (AllocatorMayReturnNull()) + return errno_EINVAL; + GET_STACK_TRACE_FATAL(thr, pc); + ReportInvalidPosixMemalignAlignment(align, &stack); } void *ptr = user_alloc_internal(thr, pc, sz, align); if (UNLIKELY(!ptr)) + // OOM error is already taken care of by user_alloc_internal. return errno_ENOMEM; CHECK(IsAligned((uptr)ptr, align)); *memptr = ptr; @@ -246,7 +268,10 @@ int user_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align, void *user_aligned_alloc(ThreadState *thr, uptr pc, uptr align, uptr sz) { if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(align, sz))) { errno = errno_EINVAL; - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (AllocatorMayReturnNull()) + return nullptr; + GET_STACK_TRACE_FATAL(thr, pc); + ReportInvalidAlignedAllocAlignment(sz, align, &stack); } return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align)); } @@ -259,7 +284,10 @@ void *user_pvalloc(ThreadState *thr, uptr pc, uptr sz) { uptr PageSize = GetPageSizeCached(); if (UNLIKELY(CheckForPvallocOverflow(sz, PageSize))) { errno = errno_ENOMEM; - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (AllocatorMayReturnNull()) + return nullptr; + GET_STACK_TRACE_FATAL(thr, pc); + ReportPvallocOverflow(sz, &stack); } // pvalloc(0) should allocate one page. sz = sz ? RoundUpTo(sz, PageSize) : PageSize; diff --git a/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc b/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc index a1bb22690e0..4f52d3d71eb 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc @@ -13,8 +13,10 @@ //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_report.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "tsan_interceptors.h" +#include "tsan_rtl.h" using namespace __tsan; // NOLINT @@ -34,7 +36,10 @@ DECLARE_REAL(void, free, void *ptr) { \ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ p = user_alloc(thr, pc, size); \ - if (!nothrow && UNLIKELY(!p)) DieOnFailure::OnOOM(); \ + if (!nothrow && UNLIKELY(!p)) { \ + GET_STACK_TRACE_FATAL(thr, pc); \ + ReportOutOfMemory(size, &stack); \ + } \ } \ invoke_malloc_hook(p, size); \ return p; @@ -46,7 +51,10 @@ DECLARE_REAL(void, free, void *ptr) { \ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ p = user_memalign(thr, pc, (uptr)align, size); \ - if (!nothrow && UNLIKELY(!p)) DieOnFailure::OnOOM(); \ + if (!nothrow && UNLIKELY(!p)) { \ + GET_STACK_TRACE_FATAL(thr, pc); \ + ReportOutOfMemory(size, &stack); \ + } \ } \ invoke_malloc_hook(p, size); \ return p; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h index 5e2a745c946..523b69aaa6b 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h @@ -650,6 +650,10 @@ void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack, ExtractTagFromStack(stack, tag); } +#define GET_STACK_TRACE_FATAL(thr, pc) \ + VarSizeStackTrace stack; \ + ObtainCurrentStack(thr, pc, &stack); \ + stack.ReverseOrder(); #if TSAN_COLLECT_STATS void StatAggregate(u64 *dst, u64 *src); diff --git a/compiler-rt/lib/tsan/rtl/tsan_stack_trace.cc b/compiler-rt/lib/tsan/rtl/tsan_stack_trace.cc index ceca3f8e873..a0dee19e246 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_stack_trace.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_stack_trace.cc @@ -43,4 +43,9 @@ void VarSizeStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) { trace_buffer[cnt] = extra_top_pc; } +void VarSizeStackTrace::ReverseOrder() { + for (u32 i = 0; i < (size >> 1); i++) + Swap(trace_buffer[i], trace_buffer[size - 1 - i]); +} + } // namespace __tsan diff --git a/compiler-rt/lib/tsan/rtl/tsan_stack_trace.h b/compiler-rt/lib/tsan/rtl/tsan_stack_trace.h index 5bf89bb7584..f69b57464bc 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_stack_trace.h +++ b/compiler-rt/lib/tsan/rtl/tsan_stack_trace.h @@ -27,6 +27,10 @@ struct VarSizeStackTrace : public StackTrace { ~VarSizeStackTrace(); void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0); + // Reverses the current stack trace order, the top frame goes to the bottom, + // the last frame goes to the top. + void ReverseOrder(); + private: void ResizeBuffer(uptr new_size); diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc index 05ae4286704..26e13a55ce1 100644 --- a/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc +++ b/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc @@ -153,29 +153,12 @@ TEST(Mman, Valloc) { EXPECT_NE(p, (void*)0); EXPECT_EQ(page_size, __sanitizer_get_allocated_size(p)); user_free(thr, 0, p); - - EXPECT_DEATH(p = user_pvalloc(thr, 0, (uptr)-(page_size - 1)), - "allocator is terminating the process instead of returning 0"); - EXPECT_DEATH(p = user_pvalloc(thr, 0, (uptr)-1), - "allocator is terminating the process instead of returning 0"); } #if !SANITIZER_DEBUG // EXPECT_DEATH clones a thread with 4K stack, // which is overflown by tsan memory accesses functions in debug mode. -TEST(Mman, CallocOverflow) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - size_t kArraySize = 4096; - volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); - volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - volatile void *p = NULL; - EXPECT_DEATH(p = user_calloc(thr, pc, kArraySize, kArraySize2), - "allocator is terminating the process instead of returning 0"); - EXPECT_EQ(0L, p); -} - TEST(Mman, Memalign) { ThreadState *thr = cur_thread(); @@ -183,12 +166,16 @@ TEST(Mman, Memalign) { EXPECT_NE(p, (void*)0); user_free(thr, 0, p); + // TODO(alekseyshl): Remove this death test when memalign is verified by + // tests in sanitizer_common. p = NULL; EXPECT_DEATH(p = user_memalign(thr, 0, 7, 100), - "allocator is terminating the process instead of returning 0"); + "invalid-allocation-alignment"); EXPECT_EQ(0L, p); } +#endif + TEST(Mman, PosixMemalign) { ThreadState *thr = cur_thread(); @@ -197,16 +184,6 @@ TEST(Mman, PosixMemalign) { EXPECT_NE(p, (void*)0); EXPECT_EQ(res, 0); user_free(thr, 0, p); - - p = NULL; - // Alignment is not a power of two, although is a multiple of sizeof(void*). - EXPECT_DEATH(res = user_posix_memalign(thr, 0, &p, 3 * sizeof(p), 100), - "allocator is terminating the process instead of returning 0"); - EXPECT_EQ(0L, p); - // Alignment is not a multiple of sizeof(void*), although is a power of 2. - EXPECT_DEATH(res = user_posix_memalign(thr, 0, &p, 2, 100), - "allocator is terminating the process instead of returning 0"); - EXPECT_EQ(0L, p); } TEST(Mman, AlignedAlloc) { @@ -215,18 +192,6 @@ TEST(Mman, AlignedAlloc) { void *p = user_aligned_alloc(thr, 0, 8, 64); EXPECT_NE(p, (void*)0); user_free(thr, 0, p); - - p = NULL; - // Alignement is not a power of 2. - EXPECT_DEATH(p = user_aligned_alloc(thr, 0, 7, 100), - "allocator is terminating the process instead of returning 0"); - EXPECT_EQ(0L, p); - // Size is not a multiple of alignment. - EXPECT_DEATH(p = user_aligned_alloc(thr, 0, 8, 100), - "allocator is terminating the process instead of returning 0"); - EXPECT_EQ(0L, p); } -#endif - } // namespace __tsan diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/aligned_alloc-alignment.cc b/compiler-rt/test/sanitizer_common/TestCases/Linux/aligned_alloc-alignment.cc index 035b5ca7f76..1a340edecdb 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/aligned_alloc-alignment.cc +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/aligned_alloc-alignment.cc @@ -1,14 +1,23 @@ // RUN: %clangxx %collect_stack_traces -O0 %s -o %t + +// Alignment is not a power of 2: // RUN: %env_tool_opts=allocator_may_return_null=0 not %run %t 17 2>&1 | FileCheck %s +// Size is not a multiple of alignment: +// RUN: %env_tool_opts=allocator_may_return_null=0 not %run %t 8 2>&1 | FileCheck %s +// Alignment is 0: // RUN: %env_tool_opts=allocator_may_return_null=0 not %run %t 0 2>&1 | FileCheck %s + +// The same for allocator_may_return_null=1: // RUN: %env_tool_opts=allocator_may_return_null=1 %run %t 17 2>&1 | FileCheck %s --check-prefix=CHECK-NULL +// RUN: %env_tool_opts=allocator_may_return_null=1 %run %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-NULL // RUN: %env_tool_opts=allocator_may_return_null=1 %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL // REQUIRES: stable-runtime -// UNSUPPORTED: android, tsan, ubsan +// UNSUPPORTED: android, ubsan #include <assert.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -21,12 +30,14 @@ int main(int argc, char **argv) { void *p = aligned_alloc(alignment, 100); // CHECK: {{ERROR: .*Sanitizer: invalid alignment requested in aligned_alloc}} // Handle a case when aligned_alloc is aliased by memalign. - // CHECK: {{#0 0x.* in .*}}{{aligned_alloc|memalign}} - // CHECK: {{#1 0x.* in main .*aligned_alloc-alignment.cc:}}[[@LINE-4]] + // CHECK: {{#0 .*}}{{aligned_alloc|memalign}} + // CHECK: {{#1 .*main .*aligned_alloc-alignment.cc:}}[[@LINE-4]] // CHECK: {{SUMMARY: .*Sanitizer: invalid-aligned-alloc-alignment}} - printf("pointer after failed aligned_alloc: %zd\n", (size_t)p); - // CHECK-NULL: pointer after failed aligned_alloc: 0 + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "errno: %d, p: %lx\n", errno, (long)p); + // CHECK-NULL: errno: 22, p: 0 return 0; } diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/pvalloc-overflow.cc b/compiler-rt/test/sanitizer_common/TestCases/Linux/pvalloc-overflow.cc index 93421af19da..537c57e1a69 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/pvalloc-overflow.cc +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/pvalloc-overflow.cc @@ -6,7 +6,7 @@ // REQUIRES: stable-runtime -// UNSUPPORTED: android, freebsd, netbsd, tsan, ubsan +// UNSUPPORTED: android, freebsd, netbsd, ubsan // Checks that pvalloc overflows are caught. If the allocator is allowed to // return null, the errno should be set to ENOMEM. @@ -14,6 +14,7 @@ #include <assert.h> #include <errno.h> #include <malloc.h> +#include <stdio.h> #include <stdint.h> #include <string.h> #include <unistd.h> @@ -32,15 +33,15 @@ int main(int argc, char *argv[]) { } else { assert(0); } + // CHECK: {{ERROR: .*Sanitizer: pvalloc parameters overflow: size .* rounded up to system page size .* cannot be represented in type size_t}} + // CHECK: {{#0 .*pvalloc}} + // CHECK: {{#1 .*main .*pvalloc-overflow.cc:}} + // CHECK: {{SUMMARY: .*Sanitizer: pvalloc-overflow}} - fprintf(stderr, "errno: %d\n", errno); + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "errno: %d, p: %lx\n", errno, (long)p); + // CHECK-NULL: errno: 12, p: 0 - return p != nullptr; + return 0; } - -// CHECK: {{ERROR: .*Sanitizer: pvalloc parameters overflow: size .* rounded up to system page size .* cannot be represented in type size_t}} -// CHECK: {{#0 0x.* in .*pvalloc}} -// CHECK: {{#1 0x.* in main .*pvalloc-overflow.cc:}} -// CHECK: {{SUMMARY: .*Sanitizer: pvalloc-overflow}} - -// CHECK-NULL: errno: 12 diff --git a/compiler-rt/test/sanitizer_common/TestCases/Posix/posix_memalign-alignment.cc b/compiler-rt/test/sanitizer_common/TestCases/Posix/posix_memalign-alignment.cc index c46540b6bca..7729057d2de 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Posix/posix_memalign-alignment.cc +++ b/compiler-rt/test/sanitizer_common/TestCases/Posix/posix_memalign-alignment.cc @@ -1,14 +1,26 @@ // RUN: %clangxx %collect_stack_traces -O0 %s -o %t + +// Alignment is not a power of two: // RUN: %env_tool_opts=allocator_may_return_null=0 not %run %t 17 2>&1 | FileCheck %s +// Alignment is not a power of two, although is a multiple of sizeof(void*): +// RUN: %env_tool_opts=allocator_may_return_null=0 not %run %t 24 2>&1 | FileCheck %s +// Alignment is not a multiple of sizeof(void*), although is a power of 2: +// RUN: %env_tool_opts=allocator_may_return_null=0 not %run %t 2 2>&1 | FileCheck %s +// Alignment is 0: // RUN: %env_tool_opts=allocator_may_return_null=0 not %run %t 0 2>&1 | FileCheck %s + +// The same for allocator_may_return_null=1: // RUN: %env_tool_opts=allocator_may_return_null=1 %run %t 17 2>&1 | FileCheck %s --check-prefix=CHECK-NULL +// RUN: %env_tool_opts=allocator_may_return_null=1 %run %t 24 2>&1 | FileCheck %s --check-prefix=CHECK-NULL +// RUN: %env_tool_opts=allocator_may_return_null=1 %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-NULL // RUN: %env_tool_opts=allocator_may_return_null=1 %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL // REQUIRES: stable-runtime -// UNSUPPORTED: tsan, ubsan +// UNSUPPORTED: ubsan #include <assert.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -16,16 +28,20 @@ int main(int argc, char **argv) { assert(argc == 2); const int alignment = atoi(argv[1]); - void *p = reinterpret_cast<void*>(42); + void* const kInitialPtrValue = reinterpret_cast<void*>(0x2a); + void *p = kInitialPtrValue; + errno = 0; int res = posix_memalign(&p, alignment, 100); // CHECK: {{ERROR: .*Sanitizer: invalid alignment requested in posix_memalign}} - // CHECK: {{#0 0x.* in .*posix_memalign}} - // CHECK: {{#1 0x.* in main .*posix_memalign-alignment.cc:}}[[@LINE-3]] + // CHECK: {{#0 .*posix_memalign}} + // CHECK: {{#1 .*main .*posix_memalign-alignment.cc:}}[[@LINE-3]] // CHECK: {{SUMMARY: .*Sanitizer: invalid-posix-memalign-alignment}} - printf("pointer after failed posix_memalign: %zd\n", (size_t)p); - // CHECK-NULL: pointer after failed posix_memalign: 42 + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "errno: %d, res: %d, p: %lx\n", errno, res, (long)p); + // CHECK-NULL: errno: 0, res: 22, p: 2a return 0; } diff --git a/compiler-rt/test/sanitizer_common/TestCases/allocator_returns_null.cc b/compiler-rt/test/sanitizer_common/TestCases/allocator_returns_null.cc index 425e7f3165f..9ecdfef9ffc 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/allocator_returns_null.cc +++ b/compiler-rt/test/sanitizer_common/TestCases/allocator_returns_null.cc @@ -1,8 +1,8 @@ // Test the behavior of malloc/calloc/realloc/new when the allocation size // exceeds the sanitizer's allocator 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 and set errno to -// the appropriate error code. +// 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: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH @@ -36,7 +36,7 @@ // RUN: | FileCheck %s --check-prefix=CHECK-NULL // TODO(alekseyshl): win32 is disabled due to failing errno tests, fix it there. -// UNSUPPORTED: tsan, ubsan, win32 +// UNSUPPORTED: ubsan, win32 #include <assert.h> #include <errno.h> @@ -51,12 +51,8 @@ int main(int argc, char **argv) { const char *action = argv[1]; fprintf(stderr, "%s:\n", action); - // The maximum value of all supported sanitizers: - // ASan: asan_allocator.cc, search for kMaxAllowedMallocSize. - // LSan: lsan_allocator.cc, search for kMaxAllowedMallocSize. - // ASan + LSan: ASan limit is used. - // MSan: msan_allocator.cc, search for kMaxAllowedMallocSize. - // TSan: tsan_mman.cc, user_alloc_internal function. + // The maximum value of all supported sanitizers (search for + // kMaxAllowedMallocSize). For ASan + LSan, ASan limit is used. static const size_t kMaxAllowedMallocSizePlusOne = #if __LP64__ || defined(_WIN64) (1ULL << 40) + 1; @@ -90,11 +86,11 @@ int main(int argc, char **argv) { assert(0); } - fprintf(stderr, "errno: %d\n", errno); + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "errno: %d, x: %lx\n", errno, (long)x); - free(x); - - return x != nullptr; + return 0; } // CHECK-mCRASH: malloc: @@ -115,4 +111,4 @@ int main(int argc, char **argv) { // CHECK-nnCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}} // CHECK-NULL: {{malloc|calloc|calloc-overflow|realloc|realloc-after-malloc|new-nothrow}} -// CHECK-NULL: errno: 12 +// CHECK-NULL: errno: 12, x: 0 diff --git a/compiler-rt/test/tsan/allocator_returns_null.cc b/compiler-rt/test/tsan/allocator_returns_null.cc deleted file mode 100644 index 5e2e2e9a53c..00000000000 --- a/compiler-rt/test/tsan/allocator_returns_null.cc +++ /dev/null @@ -1,124 +0,0 @@ -// 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, 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 \ -// 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 <assert.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits> -#include <new> - -int main(int argc, char **argv) { - // Disable stderr buffering. Needed on Windows. - setvbuf(stderr, NULL, _IONBF, 0); - - assert(argc == 2); - const char *action = argv[1]; - fprintf(stderr, "%s:\n", action); - - // The limit enforced in tsan_mman.cc, user_alloc_internal function. - static const size_t kMaxAllowedMallocSizePlusOne = (1ULL << 40) + 1; - - 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); - } 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, 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, "errno: %d\n", errno); - - // 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: -// CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process -// CHECK-coCRASH: calloc-overflow: -// CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process -// CHECK-rCRASH: realloc: -// 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: errno: 12 -// CHECK-mNULL: x: 0 -// CHECK-cNULL: calloc: -// CHECK-cNULL: errno: 12 -// CHECK-cNULL: x: 0 -// CHECK-coNULL: calloc-overflow: -// CHECK-coNULL: errno: 12 -// CHECK-coNULL: x: 0 -// CHECK-rNULL: realloc: -// CHECK-rNULL: errno: 12 -// CHECK-rNULL: x: 0 -// CHECK-mrNULL: realloc-after-malloc: -// CHECK-mrNULL: errno: 12 -// CHECK-mrNULL: x: 0 -// CHECK-nnNULL: new-nothrow: -// CHECK-nnNULL: x: 0 |

