Index: lib/scudo/CMakeLists.txt =================================================================== --- lib/scudo/CMakeLists.txt +++ lib/scudo/CMakeLists.txt @@ -36,6 +36,7 @@ set(SCUDO_SOURCES scudo_allocator.cpp scudo_crc32.cpp + scudo_errors.cpp scudo_flags.cpp scudo_malloc.cpp scudo_termination.cpp Index: lib/scudo/scudo_allocator.cpp =================================================================== --- lib/scudo/scudo_allocator.cpp +++ lib/scudo/scudo_allocator.cpp @@ -16,6 +16,7 @@ #include "scudo_allocator.h" #include "scudo_crc32.h" +#include "scudo_errors.h" #include "scudo_flags.h" #include "scudo_interface_internal.h" #include "scudo_tsd.h" @@ -224,8 +225,6 @@ static const uptr MaxAllowedMallocSize = FIRST_32_SECOND_64(2UL << 30, 1ULL << 40); - typedef ReturnNullOrDieOnFailure FailureHandler; - ScudoBackendAllocator BackendAllocator; ScudoQuarantine AllocatorQuarantine; @@ -360,24 +359,30 @@ void *allocate(uptr Size, uptr Alignment, AllocType Type, bool ForceZeroContents = false) { initThreadMaybe(); - if (UNLIKELY(Alignment > MaxAlignment)) - return FailureHandler::OnBadRequest(); + if (UNLIKELY(Alignment > MaxAlignment)) { + if (AllocatorMayReturnNull()) + return nullptr; + reportAllocationAlignmentTooBig(Alignment, MaxAlignment); + } if (UNLIKELY(Alignment < MinAlignment)) Alignment = MinAlignment; - if (UNLIKELY(Size >= MaxAllowedMallocSize)) - return FailureHandler::OnBadRequest(); - if (UNLIKELY(Size == 0)) - Size = 1; - const uptr NeededSize = RoundUpTo(Size, MinAlignment) + + const uptr NeededSize = RoundUpTo(Size ? Size : 1, MinAlignment) + Chunk::getHeaderSize(); const uptr AlignedSize = (Alignment > MinAlignment) ? NeededSize + (Alignment - Chunk::getHeaderSize()) : NeededSize; - if (UNLIKELY(AlignedSize >= MaxAllowedMallocSize)) - return FailureHandler::OnBadRequest(); + if (UNLIKELY(Size >= MaxAllowedMallocSize) || + UNLIKELY(AlignedSize >= MaxAllowedMallocSize)) { + if (AllocatorMayReturnNull()) + return nullptr; + reportAllocationSizeTooBig(Size, AlignedSize, MaxAllowedMallocSize); + } - if (CheckRssLimit && UNLIKELY(isRssLimitExceeded())) - return FailureHandler::OnOOM(); + if (CheckRssLimit && UNLIKELY(isRssLimitExceeded())) { + if (AllocatorMayReturnNull()) + return nullptr; + reportRssLimitExceeded(); + } // Primary and Secondary backed allocations have a different treatment. We // deal with alignment requirements of Primary serviced allocations here, @@ -398,8 +403,12 @@ ClassId = 0; BackendPtr = BackendAllocator.allocateSecondary(BackendSize, Alignment); } - if (UNLIKELY(!BackendPtr)) - return FailureHandler::OnOOM(); + if (UNLIKELY(!BackendPtr)) { + SetAllocatorOutOfMemory(); + if (AllocatorMayReturnNull()) + return nullptr; + reportOutOfMemory(Size); + } // If requested, we will zero out the entire contents of the returned chunk. if ((ForceZeroContents || ZeroContents) && ClassId) @@ -573,8 +582,11 @@ void *calloc(uptr NMemB, uptr Size) { initThreadMaybe(); - if (UNLIKELY(CheckForCallocOverflow(NMemB, Size))) - return FailureHandler::OnBadRequest(); + if (UNLIKELY(CheckForCallocOverflow(NMemB, Size))) { + if (AllocatorMayReturnNull()) + return nullptr; + reportCallocOverflow(NMemB, Size); + } return allocate(NMemB * Size, MinAlignment, FromMalloc, true); } @@ -591,9 +603,9 @@ return stats[StatType]; } - void *handleBadRequest() { + bool canReturnNull() { initThreadMaybe(); - return FailureHandler::OnBadRequest(); + return AllocatorMayReturnNull(); } void setRssLimit(uptr LimitMb, bool HardLimit) { @@ -632,7 +644,9 @@ void *scudoAllocate(uptr Size, uptr Alignment, AllocType Type) { if (Alignment && UNLIKELY(!IsPowerOfTwo(Alignment))) { errno = EINVAL; - return Instance.handleBadRequest(); + if (Instance.canReturnNull()) + return nullptr; + reportAllocationAlignmentNotPowerOfTwo(Alignment); } return SetErrnoOnNull(Instance.allocate(Size, Alignment, Type)); } @@ -664,7 +678,9 @@ uptr PageSize = GetPageSizeCached(); if (UNLIKELY(CheckForPvallocOverflow(Size, PageSize))) { errno = ENOMEM; - return Instance.handleBadRequest(); + if (Instance.canReturnNull()) + return nullptr; + reportPvallocOverflow(Size); } // pvalloc(0) should allocate one page. Size = Size ? RoundUpTo(Size, PageSize) : PageSize; @@ -673,7 +689,8 @@ int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { if (UNLIKELY(!CheckPosixMemalignAlignment(Alignment))) { - Instance.handleBadRequest(); + if (!Instance.canReturnNull()) + reportInvalidPosixMemalignAlignment(Alignment); return EINVAL; } void *Ptr = Instance.allocate(Size, Alignment, FromMemalign); @@ -686,7 +703,9 @@ void *scudoAlignedAlloc(uptr Alignment, uptr Size) { if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(Alignment, Size))) { errno = EINVAL; - return Instance.handleBadRequest(); + if (Instance.canReturnNull()) + return nullptr; + reportInvalidAlignedAllocAlignment(Size, Alignment); } return SetErrnoOnNull(Instance.allocate(Size, Alignment, FromMalloc)); } Index: lib/scudo/scudo_allocator_secondary.h =================================================================== --- lib/scudo/scudo_allocator_secondary.h +++ lib/scudo/scudo_allocator_secondary.h @@ -87,7 +87,7 @@ ReservedAddressRange AddressRange; uptr ReservedBeg = AddressRange.Init(ReservedSize, SecondaryAllocatorName); if (UNLIKELY(ReservedBeg == ~static_cast(0))) - return ReturnNullOrDieOnFailure::OnOOM(); + return nullptr; // A page-aligned pointer is assumed after that, so check it now. DCHECK(IsAligned(ReservedBeg, PageSize)); uptr ReservedEnd = ReservedBeg + ReservedSize; Index: lib/scudo/scudo_errors.h =================================================================== --- /dev/null +++ lib/scudo/scudo_errors.h @@ -0,0 +1,35 @@ +//===-- scudo_errors.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// Header for scudo_errors.cpp. +/// +//===----------------------------------------------------------------------===// + +#ifndef SCUDO_ERRORS_H_ +#define SCUDO_ERRORS_H_ + +#include "sanitizer_common/sanitizer_internal_defs.h" + +namespace __scudo { + +void NORETURN reportCallocOverflow(uptr Count, uptr Size); +void NORETURN reportPvallocOverflow(uptr Size); +void NORETURN reportAllocationAlignmentTooBig(uptr Alignment, + uptr MaxAlignment); +void NORETURN reportAllocationAlignmentNotPowerOfTwo(uptr Alignment); +void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment); +void NORETURN reportInvalidAlignedAllocAlignment(uptr Size, uptr Alignment); +void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize, + uptr MaxSize); +void NORETURN reportRssLimitExceeded(); +void NORETURN reportOutOfMemory(uptr RequestedSize); + +} // namespace __scudo + +#endif // SCUDO_ERRORS_H_ Index: lib/scudo/scudo_errors.cpp =================================================================== --- /dev/null +++ lib/scudo/scudo_errors.cpp @@ -0,0 +1,77 @@ +//===-- scudo_errors.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// Verbose termination functions. +/// +//===----------------------------------------------------------------------===// + +#include "scudo_utils.h" + +#include "sanitizer_common/sanitizer_flags.h" + +namespace __scudo { + +void NORETURN reportCallocOverflow(uptr Count, uptr Size) { + dieWithMessage("calloc parameters overflow: count * size (%zd * %zd) cannot " + "be represented with type size_t\n", Count, Size); +} + +void NORETURN reportPvallocOverflow(uptr Size) { + dieWithMessage("pvalloc parameters overflow: size 0x%zx rounded up to system " + "page size 0x%zx cannot be represented in type size_t\n", Size, + GetPageSizeCached()); +} + +void NORETURN reportAllocationAlignmentTooBig(uptr Alignment, + uptr MaxAlignment) { + dieWithMessage("invalid allocation alignment: %zd exceeds maximum supported " + "allocation of %zd\n", Alignment, MaxAlignment); +} + +void NORETURN reportAllocationAlignmentNotPowerOfTwo(uptr Alignment) { + dieWithMessage("invalid allocation alignment: %zd, alignment must be a power " + "of two\n", Alignment); +} + +void NORETURN reportInvalidPosixMemalignAlignment(uptr Alignment) { + dieWithMessage("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 +} + +void NORETURN reportInvalidAlignedAllocAlignment(uptr Size, uptr Alignment) { +#if SANITIZER_POSIX + dieWithMessage("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", Alignment, Size); +#else + dieWithMessage("invalid alignment requested in aligned_alloc: %zd, the " + "requested size 0x%zx must be a multiple of alignment\n", Alignment, + Size); +#endif +} + +void NORETURN reportAllocationSizeTooBig(uptr UserSize, uptr TotalSize, + uptr MaxSize) { + dieWithMessage("requested allocation size 0x%zx (0x%zx after adjustments) " + "exceeds maximum supported size of 0x%zx\n", UserSize, TotalSize, + MaxSize); +} + +void NORETURN reportRssLimitExceeded() { + dieWithMessage("specified RSS limit exceeded, currently set to " + "soft_rss_limit_mb=%zd\n", common_flags()->soft_rss_limit_mb); +} + +void NORETURN reportOutOfMemory(uptr RequestedSize) { + dieWithMessage("allocator is out of memory trying to allocate 0x%zx bytes\n", + RequestedSize); +} + +} // namespace __scudo Index: lib/scudo/scudo_new_delete.cpp =================================================================== --- lib/scudo/scudo_new_delete.cpp +++ lib/scudo/scudo_new_delete.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "scudo_allocator.h" +#include "scudo_errors.h" #include "interception/interception.h" @@ -30,7 +31,7 @@ // TODO(alekseys): throw std::bad_alloc instead of dying on OOM. #define OPERATOR_NEW_BODY_ALIGN(Type, Align, NoThrow) \ void *Ptr = scudoAllocate(size, static_cast(Align), Type); \ - if (!NoThrow && UNLIKELY(!Ptr)) DieOnFailure::OnOOM(); \ + if (!NoThrow && UNLIKELY(!Ptr)) reportOutOfMemory(size); \ return Ptr; #define OPERATOR_NEW_BODY(Type, NoThrow) \ OPERATOR_NEW_BODY_ALIGN(Type, 0, NoThrow) Index: test/scudo/aligned-new.cpp =================================================================== --- test/scudo/aligned-new.cpp +++ test/scudo/aligned-new.cpp @@ -1,6 +1,7 @@ // RUN: %clangxx_scudo -std=c++1z -faligned-allocation %s -o %t -// RUN: %run %t valid 2>&1 -// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 +// RUN: %run %t valid 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t invalid 2>&1 | FileCheck %s // Tests that the C++17 aligned new/delete operators are working as expected. // Currently we do not check the consistency of the alignment on deallocation, @@ -77,6 +78,7 @@ const size_t alignment = (1U << 8) - 1; void *p = operator new(1, static_cast(alignment), std::nothrow); + // CHECK: Scudo ERROR: invalid allocation alignment assert(!p); } Index: test/scudo/memalign.c =================================================================== --- test/scudo/memalign.c +++ test/scudo/memalign.c @@ -1,6 +1,6 @@ // RUN: %clang_scudo %s -o %t // RUN: %run %t valid 2>&1 -// RUN: not %run %t invalid 2>&1 +// RUN: not %run %t invalid 2>&1 | FileCheck --check-prefix=CHECK-align %s // RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 // RUN: not %run %t double-free 2>&1 | FileCheck --check-prefix=CHECK-double-free %s // RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t realloc 2>&1 | FileCheck --check-prefix=CHECK-realloc %s @@ -66,6 +66,7 @@ if (!strcmp(argv[1], "invalid")) { // Alignment is not a power of 2. p = memalign(alignment - 1, size); + // CHECK-align: Scudo ERROR: invalid allocation alignment assert(!p); // Size is not a multiple of alignment. p = aligned_alloc(alignment, size >> 1); Index: test/scudo/sizes.cpp =================================================================== --- test/scudo/sizes.cpp +++ test/scudo/sizes.cpp @@ -1,11 +1,11 @@ // RUN: %clangxx_scudo %s -lstdc++ -o %t -// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-max // RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t malloc 2>&1 -// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-calloc // RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t calloc 2>&1 -// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s -// RUN: %env_scudo_opts=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s -// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_scudo_opts=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-oom +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s --check-prefix=CHECK-max // RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 // RUN: %run %t usable 2>&1 @@ -70,4 +70,6 @@ return 0; } -// CHECK: allocator is terminating the process +// CHECK-max: {{Scudo ERROR: requested allocation size .* exceeds maximum supported size}} +// CHECK-oom: Scudo ERROR: allocator is out of memory +// CHECK-calloc: Scudo ERROR: calloc parameters overflow Index: test/scudo/valloc.c =================================================================== --- test/scudo/valloc.c +++ test/scudo/valloc.c @@ -1,6 +1,6 @@ // RUN: %clang_scudo %s -o %t // RUN: %run %t valid 2>&1 -// RUN: not %run %t invalid 2>&1 +// RUN: not %run %t invalid 2>&1 | FileCheck %s // RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 // UNSUPPORTED: android @@ -54,6 +54,7 @@ if (!strcmp(argv[1], "invalid")) { // Size passed to pvalloc overflows when rounded up. p = pvalloc((size_t)-1); + // CHECK: Scudo ERROR: pvalloc parameters overflow assert(!p); assert(errno == ENOMEM); errno = 0;