summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Shlyapnikov <alekseys@google.com>2018-06-05 18:02:09 +0000
committerAlex Shlyapnikov <alekseys@google.com>2018-06-05 18:02:09 +0000
commit1c2424a6bb396f18962689aed6b87f129e666176 (patch)
tree68466aedb509e4a27b1bc4733541bad9e94a4302
parenta99ae33c7f9965e19ecb4494cfd865b5495562b9 (diff)
[LSan] Report proper error on allocator failures instead of CHECK(0)-ing
Summary: Following up on and complementing D44404. Currently many allocator specific errors (OOM, for example) are reported as a text message and CHECK(0) termination, not stack, no details, not too helpful nor informative. To improve the situation, detailed and structured errors were defined and reported under the appropriate conditions. Reviewers: eugenis Subscribers: srhines, mgorny, delcypher, llvm-commits, #sanitizers Differential Revision: https://reviews.llvm.org/D47645 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@334034 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/lsan/lsan_allocator.cc42
-rw-r--r--lib/lsan/lsan_interceptors.cc25
-rw-r--r--lib/sanitizer_common/CMakeLists.txt2
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_report.cc126
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_report.h38
-rw-r--r--lib/sanitizer_common/sanitizer_report_decorator.h5
-rw-r--r--test/lsan/TestCases/Linux/aligned_alloc-alignment.cc25
-rw-r--r--test/lsan/TestCases/Posix/lit.local.cfg9
-rw-r--r--test/lsan/TestCases/Posix/posix_memalign-alignment.cc22
-rw-r--r--test/lsan/TestCases/allocator_returns_null.cc20
10 files changed, 275 insertions, 39 deletions
diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc
index 85721c431..0aa6db63f 100644
--- a/lib/lsan/lsan_allocator.cc
+++ b/lib/lsan/lsan_allocator.cc
@@ -17,6 +17,7 @@
#include "sanitizer_common/sanitizer_allocator.h"
#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_errno.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
@@ -70,17 +71,27 @@ static void RegisterDeallocation(void *p) {
atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed);
}
+static void *ReportAllocationSizeTooBig(uptr size, const StackTrace &stack) {
+ if (AllocatorMayReturnNull()) {
+ Report("WARNING: LeakSanitizer failed to allocate 0x%zx bytes\n", size);
+ return nullptr;
+ }
+ ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, &stack);
+}
+
void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
bool cleared) {
if (size == 0)
size = 1;
- if (size > kMaxAllowedMallocSize) {
- Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size);
- return ReturnNullOrDieOnFailure::OnBadRequest();
- }
+ if (size > kMaxAllowedMallocSize)
+ return ReportAllocationSizeTooBig(size, stack);
void *p = allocator.Allocate(GetAllocatorCache(), size, alignment);
- if (UNLIKELY(!p))
- return ReturnNullOrDieOnFailure::OnOOM();
+ if (UNLIKELY(!p)) {
+ SetAllocatorOutOfMemory();
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportOutOfMemory(size, &stack);
+ }
// Do not rely on the allocator to clear the memory (it's slow).
if (cleared && allocator.FromPrimary(p))
memset(p, 0, size);
@@ -91,8 +102,11 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
}
static void *Calloc(uptr nmemb, uptr size, const StackTrace &stack) {
- if (UNLIKELY(CheckForCallocOverflow(size, nmemb)))
- return ReturnNullOrDieOnFailure::OnBadRequest();
+ if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportCallocOverflow(nmemb, size, &stack);
+ }
size *= nmemb;
return Allocate(stack, size, 1, true);
}
@@ -108,9 +122,8 @@ void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
uptr alignment) {
RegisterDeallocation(p);
if (new_size > kMaxAllowedMallocSize) {
- Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size);
allocator.Deallocate(GetAllocatorCache(), p);
- return ReturnNullOrDieOnFailure::OnBadRequest();
+ return ReportAllocationSizeTooBig(new_size, stack);
}
p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment);
RegisterAllocation(stack, p, new_size);
@@ -131,8 +144,9 @@ uptr GetMallocUsableSize(const void *p) {
int lsan_posix_memalign(void **memptr, uptr alignment, uptr size,
const StackTrace &stack) {
if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
- ReturnNullOrDieOnFailure::OnBadRequest();
- return errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return errno_EINVAL;
+ ReportInvalidPosixMemalignAlignment(alignment, &stack);
}
void *ptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
if (UNLIKELY(!ptr))
@@ -146,7 +160,9 @@ int lsan_posix_memalign(void **memptr, uptr alignment, uptr size,
void *lsan_memalign(uptr alignment, uptr size, const StackTrace &stack) {
if (UNLIKELY(!IsPowerOfTwo(alignment))) {
errno = errno_EINVAL;
- return ReturnNullOrDieOnFailure::OnBadRequest();
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportInvalidAllocationAlignment(alignment, &stack);
}
return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory));
}
diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc
index 41a9ff861..b076d11de 100644
--- a/lib/lsan/lsan_interceptors.cc
+++ b/lib/lsan/lsan_interceptors.cc
@@ -14,6 +14,7 @@
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
@@ -200,21 +201,21 @@ INTERCEPTOR(int, mprobe, void *ptr) {
// 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 = lsan_malloc(size, stack); \
- if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM(); \
+#define OPERATOR_NEW_BODY(nothrow)\
+ ENSURE_LSAN_INITED;\
+ GET_STACK_TRACE_MALLOC;\
+ void *res = lsan_malloc(size, stack);\
+ if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
return res;
-#define OPERATOR_NEW_BODY_ALIGN(nothrow) \
- ENSURE_LSAN_INITED; \
- GET_STACK_TRACE_MALLOC; \
- void *res = lsan_memalign((uptr)align, size, stack); \
- if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM(); \
+#define OPERATOR_NEW_BODY_ALIGN(nothrow)\
+ ENSURE_LSAN_INITED;\
+ GET_STACK_TRACE_MALLOC;\
+ void *res = lsan_memalign((uptr)align, size, stack);\
+ if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
return res;
-#define OPERATOR_DELETE_BODY \
- ENSURE_LSAN_INITED; \
+#define OPERATOR_DELETE_BODY\
+ ENSURE_LSAN_INITED;\
lsan_free(ptr);
// On OS X it's not enough to just provide our own 'operator new' and
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 47a7c4509..558fa6343 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SANITIZER_COVERAGE_SOURCES
sanitizer_coverage_win_sections.cc)
set(SANITIZER_SYMBOLIZER_SOURCES
+ sanitizer_allocator_report.cc
sanitizer_stackdepot.cc
sanitizer_stacktrace.cc
sanitizer_stacktrace_libcdep.cc
@@ -98,6 +99,7 @@ set(SANITIZER_HEADERS
sanitizer_allocator_local_cache.h
sanitizer_allocator_primary32.h
sanitizer_allocator_primary64.h
+ sanitizer_allocator_report.h
sanitizer_allocator_secondary.h
sanitizer_allocator_size_class_map.h
sanitizer_allocator_stats.h
diff --git a/lib/sanitizer_common/sanitizer_allocator_report.cc b/lib/sanitizer_common/sanitizer_allocator_report.cc
new file mode 100644
index 000000000..4760ba719
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_allocator_report.cc
@@ -0,0 +1,126 @@
+//===-- sanitizer_allocator_report.cc ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared allocator error reporting for ThreadSanitizer, MemorySanitizer, etc.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator.h"
+#include "sanitizer_allocator_report.h"
+#include "sanitizer_common.h"
+#include "sanitizer_report_decorator.h"
+
+namespace __sanitizer {
+
+class ScopedAllocatorErrorReport {
+ public:
+ ScopedAllocatorErrorReport(const char *error_summary_,
+ const StackTrace *stack_)
+ : error_summary(error_summary_),
+ stack(stack_) {
+ Printf("%s", d.Error());
+ }
+ ~ScopedAllocatorErrorReport() {
+ Printf("%s", d.Default());
+ stack->Print();
+ // TODO(alekseyshl): Define SanitizerToolOptionsEnvVarName and use it there.
+ PrintHintAllocatorCannotReturnNull("");
+ ReportErrorSummary(error_summary, stack);
+ }
+
+ private:
+ ScopedErrorReportLock lock;
+ const char *error_summary;
+ const StackTrace* const stack;
+ const SanitizerCommonDecorator d;
+};
+
+void NORETURN ReportCallocOverflow(uptr count, uptr size,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("calloc-overflow", stack);
+ Report("ERROR: %s: calloc parameters overflow: count * size (%zd * %zd) "
+ "cannot be represented in type size_t\n", SanitizerToolName, count,
+ size);
+ }
+ Die();
+}
+
+void NORETURN ReportPvallocOverflow(uptr size, const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("pvalloc-overflow", stack);
+ Report("ERROR: %s: pvalloc parameters overflow: size 0x%zx rounded up to "
+ "system page size 0x%zx cannot be represented in type size_t\n",
+ SanitizerToolName, size, GetPageSizeCached());
+ }
+ Die();
+}
+
+void NORETURN ReportInvalidAllocationAlignment(uptr alignment,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("invalid-allocation-alignment", stack);
+ Report("ERROR: %s: invalid allocation alignment: %zd, alignment must be a "
+ "power of two\n", SanitizerToolName, alignment);
+ }
+ Die();
+}
+
+void NORETURN ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("invalid-aligned-alloc-alignment", stack);
+#if SANITIZER_POSIX
+ Report("ERROR: %s: invalid alignment requested in "
+ "aligned_alloc: %zd, alignment must be a power of two and the "
+ "requested size 0x%zx must be a multiple of alignment\n",
+ SanitizerToolName, alignment, size);
+#else
+ Report("ERROR: %s: invalid alignment requested in aligned_alloc: %zd, "
+ "the requested size 0x%zx must be a multiple of alignment\n",
+ SanitizerToolName, alignment, size);
+#endif
+ }
+ Die();
+}
+
+void NORETURN ReportInvalidPosixMemalignAlignment(uptr alignment,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("invalid-posix-memalign-alignment",
+ stack);
+ Report("ERROR: %s: invalid alignment requested in "
+ "posix_memalign: %zd, alignment must be a power of two and a "
+ "multiple of sizeof(void*) == %zd\n", SanitizerToolName, alignment,
+ sizeof(void*)); // NOLINT
+ }
+ Die();
+}
+
+void NORETURN ReportAllocationSizeTooBig(uptr user_size, uptr max_size,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("allocation-size-too-big", stack);
+ Report("ERROR: %s: requested allocation size 0x%zx exceeds maximum "
+ "supported size of 0x%zx\n", SanitizerToolName, user_size, max_size);
+ }
+ Die();
+}
+
+void NORETURN ReportOutOfMemory(uptr requested_size, const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("out-of-memory", stack);
+ Report("ERROR: %s: allocator is out of memory trying to allocate 0x%zx "
+ "bytes\n", SanitizerToolName, requested_size);
+ }
+ Die();
+}
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_allocator_report.h b/lib/sanitizer_common/sanitizer_allocator_report.h
new file mode 100644
index 000000000..b19b22fa5
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_allocator_report.h
@@ -0,0 +1,38 @@
+//===-- sanitizer_allocator_report.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared allocator error reporting for ThreadSanitizer, MemorySanitizer, etc.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_REPORT_H
+#define SANITIZER_ALLOCATOR_REPORT_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_stacktrace.h"
+
+namespace __sanitizer {
+
+void NORETURN ReportCallocOverflow(uptr count, uptr size,
+ const StackTrace *stack);
+void NORETURN ReportPvallocOverflow(uptr size, const StackTrace *stack);
+void NORETURN ReportInvalidAllocationAlignment(uptr alignment,
+ const StackTrace *stack);
+void NORETURN ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment,
+ const StackTrace *stack);
+void NORETURN ReportInvalidPosixMemalignAlignment(uptr alignment,
+ const StackTrace *stack);
+void NORETURN ReportAllocationSizeTooBig(uptr user_size, uptr max_size,
+ const StackTrace *stack);
+void NORETURN ReportOutOfMemory(uptr requested_size, const StackTrace *stack);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_ALLOCATOR_REPORT_H
diff --git a/lib/sanitizer_common/sanitizer_report_decorator.h b/lib/sanitizer_common/sanitizer_report_decorator.h
index 060b58d3f..a7878684a 100644
--- a/lib/sanitizer_common/sanitizer_report_decorator.h
+++ b/lib/sanitizer_common/sanitizer_report_decorator.h
@@ -25,10 +25,11 @@ class SanitizerCommonDecorator {
// stdout, which is not the case on Windows (see SetConsoleTextAttribute()).
public:
SanitizerCommonDecorator() : ansi_(ColorizeReports()) {}
- const char *Bold() const { return ansi_ ? "\033[1m" : ""; }
+ const char *Bold() const { return ansi_ ? "\033[1m" : ""; }
const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
const char *Warning() const { return Red(); }
- const char *MemoryByte() { return Magenta(); }
+ const char *Error() const { return Red(); }
+ const char *MemoryByte() const { return Magenta(); }
protected:
const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; }
diff --git a/test/lsan/TestCases/Linux/aligned_alloc-alignment.cc b/test/lsan/TestCases/Linux/aligned_alloc-alignment.cc
new file mode 100644
index 000000000..370691d58
--- /dev/null
+++ b/test/lsan/TestCases/Linux/aligned_alloc-alignment.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx_lsan -O0 %s -o %t
+// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
+
+// UNSUPPORTED: android
+
+// REQUIRES: stable-runtime
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void *aligned_alloc(size_t alignment, size_t size);
+
+int main() {
+ void *p = aligned_alloc(17, 100);
+ // CHECK: {{ERROR: .*Sanitizer: invalid allocation alignment: 17}}
+ // CHECK: {{#0 0x.* in .*}}{{aligned_alloc|memalign}}
+ // CHECK: {{#1 0x.* in main .*aligned_alloc-alignment.cc:}}[[@LINE-3]]
+ // CHECK: {{SUMMARY: .*Sanitizer: invalid-allocation-alignment}}
+
+ printf("pointer after failed aligned_alloc: %zd\n", (size_t)p);
+ // CHECK-NULL: pointer after failed aligned_alloc: 0
+
+ return 0;
+}
diff --git a/test/lsan/TestCases/Posix/lit.local.cfg b/test/lsan/TestCases/Posix/lit.local.cfg
new file mode 100644
index 000000000..60a946082
--- /dev/null
+++ b/test/lsan/TestCases/Posix/lit.local.cfg
@@ -0,0 +1,9 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os in ['Windows']:
+ config.unsupported = True
diff --git a/test/lsan/TestCases/Posix/posix_memalign-alignment.cc b/test/lsan/TestCases/Posix/posix_memalign-alignment.cc
new file mode 100644
index 000000000..1b9b14ebe
--- /dev/null
+++ b/test/lsan/TestCases/Posix/posix_memalign-alignment.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_lsan -O0 %s -o %t
+// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
+
+// REQUIRES: stable-runtime
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ void *p = reinterpret_cast<void*>(42);
+ int res = posix_memalign(&p, 17, 100);
+ // CHECK: {{ERROR: .*Sanitizer: invalid alignment requested in posix_memalign: 17}}
+ // CHECK: {{#0 0x.* in .*posix_memalign}}
+ // CHECK: {{#1 0x.* in 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
+
+ return 0;
+}
diff --git a/test/lsan/TestCases/allocator_returns_null.cc b/test/lsan/TestCases/allocator_returns_null.cc
index 722cc5c4e..fee90f724 100644
--- a/test/lsan/TestCases/allocator_returns_null.cc
+++ b/test/lsan/TestCases/allocator_returns_null.cc
@@ -36,10 +36,6 @@
// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL
-// TODO(alekseyshl): Enable it back for standalone LSan mode when CHECK(0) in
-// LSan allocator are converted to proper errors (see D44404 for the reference).
-// REQUIRES: asan
-
#include <assert.h>
#include <errno.h>
#include <stdio.h>
@@ -102,21 +98,21 @@ int main(int argc, char **argv) {
}
// CHECK-mCRASH: malloc:
-// CHECK-mCRASH: SUMMARY: AddressSanitizer: allocation-size-too-big
+// CHECK-mCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
// CHECK-cCRASH: calloc:
-// CHECK-cCRASH: SUMMARY: AddressSanitizer: allocation-size-too-big
+// CHECK-cCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
// CHECK-coCRASH: calloc-overflow:
-// CHECK-coCRASH: SUMMARY: AddressSanitizer: calloc-overflow
+// CHECK-coCRASH: {{SUMMARY: .*Sanitizer: calloc-overflow}}
// CHECK-rCRASH: realloc:
-// CHECK-rCRASH: SUMMARY: AddressSanitizer: allocation-size-too-big
+// CHECK-rCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
// CHECK-mrCRASH: realloc-after-malloc:
-// CHECK-mrCRASH: SUMMARY: AddressSanitizer: allocation-size-too-big
+// CHECK-mrCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
// CHECK-nCRASH: new:
-// CHECK-nCRASH: SUMMARY: AddressSanitizer: allocation-size-too-big
+// CHECK-nCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
// CHECK-nCRASH-OOM: new:
-// CHECK-nCRASH-OOM: SUMMARY: AddressSanitizer: out-of-memory
+// CHECK-nCRASH-OOM: {{SUMMARY: .*Sanitizer: out-of-memory}}
// CHECK-nnCRASH: new-nothrow:
-// CHECK-nnCRASH: SUMMARY: AddressSanitizer: allocation-size-too-big
+// CHECK-nnCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
// CHECK-mNULL: malloc:
// CHECK-mNULL: errno: 12