Index: lib/lsan/CMakeLists.txt =================================================================== --- lib/lsan/CMakeLists.txt +++ lib/lsan/CMakeLists.txt @@ -16,6 +16,7 @@ lsan_mac.cc lsan_malloc_mac.cc lsan_preinit.cc + lsan_report.cc lsan_thread.cc) set(LSAN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) Index: lib/lsan/lsan_allocator.cc =================================================================== --- lib/lsan/lsan_allocator.cc +++ lib/lsan/lsan_allocator.cc @@ -22,6 +22,7 @@ #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_common.h" +#include "lsan_report.h" extern "C" void *memset(void *ptr, int value, uptr num); @@ -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_common.h =================================================================== --- lib/lsan/lsan_common.h +++ lib/lsan/lsan_common.h @@ -19,6 +19,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_platform.h" +#include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_symbolizer.h" @@ -249,6 +250,13 @@ void *metadata_; }; +class Decorator: public __sanitizer::SanitizerCommonDecorator { + public: + Decorator() : SanitizerCommonDecorator() { } + const char *Error() const { return Red(); } + const char *Leak() const { return Blue(); } +}; + } // namespace __lsan extern "C" { Index: lib/lsan/lsan_common.cc =================================================================== --- lib/lsan/lsan_common.cc +++ lib/lsan/lsan_common.cc @@ -19,7 +19,6 @@ #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_procmaps.h" -#include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_suppressions.h" @@ -122,13 +121,6 @@ } } -class Decorator: public __sanitizer::SanitizerCommonDecorator { - public: - Decorator() : SanitizerCommonDecorator() { } - const char *Error() { return Red(); } - const char *Leak() { return Blue(); } -}; - static inline bool CanBeAHeapPointer(uptr p) { // Since our heap is located in mmap-ed memory, we can assume a sensible lower // bound on heap addresses. Index: lib/lsan/lsan_interceptors.cc =================================================================== --- lib/lsan/lsan_interceptors.cc +++ lib/lsan/lsan_interceptors.cc @@ -27,6 +27,7 @@ #include "lsan.h" #include "lsan_allocator.h" #include "lsan_common.h" +#include "lsan_report.h" #include "lsan_thread.h" #include @@ -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/lsan/lsan_report.h =================================================================== --- /dev/null +++ lib/lsan/lsan_report.h @@ -0,0 +1,36 @@ +//===-- lsan_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 +/// This file is a part of LeakSanitizer. LSan-private header for error +/// reporting functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LSAN_REPORT_H +#define LSAN_REPORT_H + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __lsan { + +void NORETURN ReportCallocOverflow(uptr count, uptr size, + const StackTrace &stack); +void NORETURN ReportInvalidAllocationAlignment(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 __lsan + +#endif // LSAN_REPORT_H Index: lib/lsan/lsan_report.cc =================================================================== --- /dev/null +++ lib/lsan/lsan_report.cc @@ -0,0 +1,92 @@ +//===-- lsan_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 +/// This file is a part of LeakSanitizer. Contains error reporting code. +/// +//===----------------------------------------------------------------------===// + +#include "lsan_common.h" +#include "lsan_report.h" +#include "sanitizer_common/sanitizer_allocator.h" + +namespace __lsan { + +class ScopedErrorReport { + public: + ScopedErrorReport(const char *error_summary_, const StackTrace &stack_) + : error_summary(error_summary_), + stack(stack_) { + Printf("%s", d.Error()); + } + ~ScopedErrorReport() { + Printf("%s", d.Default()); + stack.Print(); + PrintHintAllocatorCannotReturnNull("LSAN_OPTIONS"); + ReportErrorSummary(error_summary, &stack); + } + + private: + const char *error_summary; + const StackTrace &stack; + const Decorator d; +}; + +void NORETURN ReportCallocOverflow(uptr count, uptr size, + const StackTrace &stack) { + { + ScopedErrorReport report("calloc-overflow", stack); + Report("ERROR: LeakSanitizer: calloc parameters overflow: count * size " + "(%zd * %zd) cannot be represented in type size_t\n", count, size); + } + Die(); +} + +void NORETURN ReportInvalidAllocationAlignment(uptr alignment, + const StackTrace &stack) { + { + ScopedErrorReport report("invalid-allocation-alignment", stack); + Report("ERROR: LeakSanitizer: invalid allocation alignment: %zd, " + "alignment must be a power of two\n", alignment); + } + Die(); +} + +void NORETURN ReportInvalidPosixMemalignAlignment(uptr alignment, + const StackTrace &stack) { + { + ScopedErrorReport report("invalid-posix-memalign-alignment", stack); + Report("ERROR: LeakSanitizer: invalid alignment requested in " + "posix_memalign: %zd, alignment must be a power of two and a " + "multiple of sizeof(void*) == %zd\n", alignment, + sizeof(void*)); // NOLINT + } + Die(); +} + +void NORETURN ReportAllocationSizeTooBig(uptr user_size, uptr max_size, + const StackTrace &stack) { + { + ScopedErrorReport report("allocation-size-too-big", stack); + Report("ERROR: LeakSanitizer: requested allocation size 0x%zx exceeds " + "maximum supported size of 0x%zx\n", user_size, max_size); + } + Die(); +} + +void NORETURN ReportOutOfMemory(uptr requested_size, const StackTrace &stack) { + { + ScopedErrorReport report("out-of-memory", stack); + Report("ERROR: LeakSanitizer: allocator is out of memory trying to " + "allocate 0x%zx bytes\n", requested_size); + } + Die(); +} + +} // namespace __lsan Index: test/lsan/TestCases/Linux/aligned_alloc-alignment.cc =================================================================== --- /dev/null +++ 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 =================================================================== --- /dev/null +++ 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 =================================================================== --- /dev/null +++ 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