Index: lib/lsan/lsan_allocator.cc =================================================================== --- lib/lsan/lsan_allocator.cc +++ 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 @@ atomic_store(reinterpret_cast(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 @@ } 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 @@ 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 @@ 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 @@ 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)); } Index: lib/lsan/lsan_interceptors.cc =================================================================== --- lib/lsan/lsan_interceptors.cc +++ 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 @@ // 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 Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -69,6 +69,7 @@ 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 @@ 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 Index: lib/sanitizer_common/sanitizer_allocator_report.h =================================================================== --- lib/sanitizer_common/sanitizer_allocator_report.h +++ 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 Index: lib/sanitizer_common/sanitizer_allocator_report.cc =================================================================== --- lib/sanitizer_common/sanitizer_allocator_report.cc +++ 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 Index: lib/sanitizer_common/sanitizer_report_decorator.h =================================================================== --- lib/sanitizer_common/sanitizer_report_decorator.h +++ lib/sanitizer_common/sanitizer_report_decorator.h @@ -25,10 +25,11 @@ // 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" : ""; } Index: test/lsan/TestCases/Linux/aligned_alloc-alignment.cc =================================================================== --- test/lsan/TestCases/Linux/aligned_alloc-alignment.cc +++ 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 +#include + +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; +} Index: test/lsan/TestCases/Posix/lit.local.cfg =================================================================== --- test/lsan/TestCases/Posix/lit.local.cfg +++ 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 Index: test/lsan/TestCases/Posix/posix_memalign-alignment.cc =================================================================== --- test/lsan/TestCases/Posix/posix_memalign-alignment.cc +++ 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 +#include + +int main() { + void *p = reinterpret_cast(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; +} Index: test/lsan/TestCases/allocator_returns_null.cc =================================================================== --- test/lsan/TestCases/allocator_returns_null.cc +++ 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 #include #include @@ -102,21 +98,21 @@ } // 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