Index: lib/hwasan/hwasan.h =================================================================== --- lib/hwasan/hwasan.h +++ lib/hwasan/hwasan.h @@ -1,4 +1,4 @@ -//===-- hwasan.h --------------------------------------------------*- C++ -*-===// +//===-- hwasan.h ------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -97,15 +97,6 @@ void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp, void *context, bool request_fast_unwind); -void ReportInvalidAccess(StackTrace *stack, u32 origin); -void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size, - bool is_store); -void ReportStats(); -void ReportAtExitStatistics(); -void DescribeMemoryRange(const void *x, uptr size); -void ReportInvalidAccessInsideAddressRange(const char *what, const void *start, uptr size, - uptr offset); - // Returns a "chained" origin id, pointing to the given stack trace followed by // the previous origin id. u32 ChainOrigin(u32 id, StackTrace *stack); Index: lib/hwasan/hwasan.cc =================================================================== --- lib/hwasan/hwasan.cc +++ lib/hwasan/hwasan.cc @@ -1,4 +1,4 @@ -//===-- hwasan.cc -----------------------------------------------------------===// +//===-- hwasan.cc ---------------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -14,8 +14,9 @@ #include "hwasan.h" #include "hwasan_mapping.h" -#include "hwasan_thread.h" #include "hwasan_poisoning.h" +#include "hwasan_report.h" +#include "hwasan_thread.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" Index: lib/hwasan/hwasan_allocator.cc =================================================================== --- lib/hwasan/hwasan_allocator.cc +++ lib/hwasan/hwasan_allocator.cc @@ -1,4 +1,4 @@ -//===-- hwasan_allocator.cc --------------------------- ---------------------===// +//===-- hwasan_allocator.cc ------------------------- ---------------------===// // // The LLVM Compiler Infrastructure // @@ -15,6 +15,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_atomic.h" #include "sanitizer_common/sanitizer_errno.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -127,9 +128,12 @@ size = RoundUpTo(size, kShadowAlignment); if (size > kMaxAllowedMallocSize) { - Report("WARNING: HWAddressSanitizer failed to allocate %p bytes\n", - (void *)size); - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (AllocatorMayReturnNull()) { + Report("WARNING: HWAddressSanitizer failed to allocate 0x%zx bytes\n", + size); + return nullptr; + } + ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, stack); } HwasanThread *t = GetCurrentThread(); void *allocated; @@ -141,8 +145,12 @@ AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, size, alignment); } - if (UNLIKELY(!allocated)) - return ReturnNullOrDieOnFailure::OnOOM(); + if (UNLIKELY(!allocated)) { + SetAllocatorOutOfMemory(); + if (AllocatorMayReturnNull()) + return nullptr; + ReportOutOfMemory(size, stack); + } Metadata *meta = reinterpret_cast(allocator.GetMetaData(allocated)); meta->state = CHUNK_ALLOCATED; @@ -224,6 +232,15 @@ return new_p; } +void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) { + if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { + if (AllocatorMayReturnNull()) + return nullptr; + ReportCallocOverflow(nmemb, size, stack); + } + return HwasanAllocate(stack, nmemb * size, sizeof(u64), true); +} + HwasanChunkView FindHeapChunkByAddress(uptr address) { void *block = allocator.GetBlockBegin(reinterpret_cast(address)); if (!block) @@ -247,9 +264,7 @@ } void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) - return SetErrnoOnNull(ReturnNullOrDieOnFailure::OnBadRequest()); - return SetErrnoOnNull(HwasanAllocate(stack, nmemb * size, sizeof(u64), true)); + return SetErrnoOnNull(HwasanCalloc(stack, nmemb, size)); } void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) { @@ -263,14 +278,17 @@ } void *hwasan_valloc(uptr size, StackTrace *stack) { - return SetErrnoOnNull(HwasanAllocate(stack, size, GetPageSizeCached(), false)); + return SetErrnoOnNull( + HwasanAllocate(stack, size, GetPageSizeCached(), false)); } void *hwasan_pvalloc(uptr size, StackTrace *stack) { uptr PageSize = GetPageSizeCached(); if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) { errno = errno_ENOMEM; - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (AllocatorMayReturnNull()) + return nullptr; + ReportPvallocOverflow(size, stack); } // pvalloc(0) should allocate one page. size = size ? RoundUpTo(size, PageSize) : PageSize; @@ -280,7 +298,9 @@ void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) { if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) { errno = errno_EINVAL; - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (AllocatorMayReturnNull()) + return nullptr; + ReportInvalidAlignedAllocAlignment(size, alignment, stack); } return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false)); } @@ -288,7 +308,9 @@ void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack) { if (UNLIKELY(!IsPowerOfTwo(alignment))) { errno = errno_EINVAL; - return ReturnNullOrDieOnFailure::OnBadRequest(); + if (AllocatorMayReturnNull()) + return nullptr; + ReportInvalidAllocationAlignment(alignment, stack); } return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false)); } @@ -296,18 +318,20 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size, StackTrace *stack) { if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) { - ReturnNullOrDieOnFailure::OnBadRequest(); - return errno_EINVAL; + if (AllocatorMayReturnNull()) + return errno_EINVAL; + ReportInvalidPosixMemalignAlignment(alignment, stack); } void *ptr = HwasanAllocate(stack, size, alignment, false); if (UNLIKELY(!ptr)) + // OOM error is already taken care of by HwasanAllocate. return errno_ENOMEM; CHECK(IsAligned((uptr)ptr, alignment)); *memptr = ptr; return 0; } -} // namespace __hwasan +} // namespace __hwasan using namespace __hwasan; Index: lib/hwasan/hwasan_interceptors.cc =================================================================== --- lib/hwasan/hwasan_interceptors.cc +++ lib/hwasan/hwasan_interceptors.cc @@ -20,6 +20,7 @@ #include "hwasan_mapping.h" #include "hwasan_thread.h" #include "hwasan_poisoning.h" +#include "hwasan_report.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" Index: lib/hwasan/hwasan_linux.cc =================================================================== --- lib/hwasan/hwasan_linux.cc +++ lib/hwasan/hwasan_linux.cc @@ -20,6 +20,7 @@ #include "hwasan_dynamic_shadow.h" #include "hwasan_interface_internal.h" #include "hwasan_mapping.h" +#include "hwasan_report.h" #include "hwasan_thread.h" #include Index: lib/hwasan/hwasan_new_delete.cc =================================================================== --- lib/hwasan/hwasan_new_delete.cc +++ lib/hwasan/hwasan_new_delete.cc @@ -1,4 +1,4 @@ -//===-- hwasan_new_delete.cc ------------------------------------------------===// +//===-- hwasan_new_delete.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -15,6 +15,7 @@ #include "hwasan.h" #include "interception/interception.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_report.h" #if HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE @@ -32,7 +33,7 @@ #define OPERATOR_NEW_BODY(nothrow) \ GET_MALLOC_STACK_TRACE; \ void *res = hwasan_malloc(size, &stack);\ - if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\ + if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\ return res INTERCEPTOR_ATTRIBUTE Index: lib/hwasan/hwasan_report.h =================================================================== --- lib/hwasan/hwasan_report.h +++ lib/hwasan/hwasan_report.h @@ -0,0 +1,36 @@ +//===-- hwasan_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 HWAddressSanitizer. HWASan-private header for error +/// reporting functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef HWASAN_REPORT_H +#define HWASAN_REPORT_H + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __hwasan { + +void ReportInvalidAccess(StackTrace *stack, u32 origin); +void ReportStats(); +void ReportInvalidAccessInsideAddressRange(const char *what, const void *start, + uptr size, uptr offset); +void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size, + bool is_store); + +void ReportAtExitStatistics(); + + +} // namespace __hwasan + +#endif // HWASAN_REPORT_H Index: lib/hwasan/hwasan_report.cc =================================================================== --- lib/hwasan/hwasan_report.cc +++ lib/hwasan/hwasan_report.cc @@ -1,4 +1,4 @@ -//===-- hwasan_report.cc ----------------------------------------------------===// +//===-- hwasan_report.cc --------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -37,9 +37,9 @@ class Decorator: public __sanitizer::SanitizerCommonDecorator { public: Decorator() : SanitizerCommonDecorator() { } - const char *Allocation() { return Magenta(); } - const char *Origin() { return Magenta(); } - const char *Name() { return Green(); } + const char *Allocation() const { return Magenta(); } + const char *Origin() const { return Magenta(); } + const char *Name() const { return Green(); } }; struct HeapAddressDescription { @@ -130,5 +130,4 @@ ReportErrorSummary("tag-mismatch", stack); } - } // namespace __hwasan Index: test/hwasan/TestCases/Linux/aligned_alloc-alignment.cc =================================================================== --- test/hwasan/TestCases/Linux/aligned_alloc-alignment.cc +++ test/hwasan/TestCases/Linux/aligned_alloc-alignment.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_hwasan -O0 %s -o %t +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t 2>&1 | FileCheck %s +// RUN: %env_hwasan_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: HWAddressSanitizer: invalid alignment requested in aligned_alloc: 17 + // CHECK: {{#0 0x.* in .*}}{{aligned_alloc|memalign}} + // CHECK: {{#1 0x.* in main .*aligned_alloc-alignment.cc:}}[[@LINE-3]] + // CHECK: SUMMARY: HWAddressSanitizer: invalid-aligned-alloc-alignment + + printf("pointer after failed aligned_alloc: %zd\n", (size_t)p); + // CHECK-NULL: pointer after failed aligned_alloc: 0 + + return 0; +} Index: test/hwasan/TestCases/Linux/lit.local.cfg =================================================================== --- test/hwasan/TestCases/Linux/lit.local.cfg +++ test/hwasan/TestCases/Linux/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 not in ['Linux']: + config.unsupported = True Index: test/hwasan/TestCases/Linux/pvalloc-overflow.cc =================================================================== --- test/hwasan/TestCases/Linux/pvalloc-overflow.cc +++ test/hwasan/TestCases/Linux/pvalloc-overflow.cc @@ -0,0 +1,46 @@ +// RUN: %clangxx_hwasan -O0 %s -o %t +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t m1 2>&1 | FileCheck %s +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-NULL +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t psm1 2>&1 | FileCheck %s +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t psm1 2>&1 | FileCheck %s --check-prefix=CHECK-NULL + +// UNSUPPORTED: android + +// REQUIRES: stable-runtime + +// Checks that pvalloc overflows are caught. If the allocator is allowed to +// return null, the errno should be set to ENOMEM. + +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + assert(argc == 2); + const char *action = argv[1]; + + const size_t page_size = sysconf(_SC_PAGESIZE); + + void *p = nullptr; + if (!strcmp(action, "m1")) { + p = pvalloc((uintptr_t)-1); + } else if (!strcmp(action, "psm1")) { + p = pvalloc((uintptr_t)-(page_size - 1)); + } else { + assert(0); + } + + fprintf(stderr, "errno: %d\n", errno); + + return p != nullptr; +} + +// CHECK: {{ERROR: HWAddressSanitizer: 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: HWAddressSanitizer: pvalloc-overflow + +// CHECK-NULL: errno: 12 Index: test/hwasan/TestCases/Posix/lit.local.cfg =================================================================== --- test/hwasan/TestCases/Posix/lit.local.cfg +++ test/hwasan/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/hwasan/TestCases/Posix/posix_memalign-alignment.cc =================================================================== --- test/hwasan/TestCases/Posix/posix_memalign-alignment.cc +++ test/hwasan/TestCases/Posix/posix_memalign-alignment.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_hwasan -O0 %s -o %t +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t 2>&1 | FileCheck %s +// RUN: %env_hwasan_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: HWAddressSanitizer: 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: HWAddressSanitizer: 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/hwasan/TestCases/allocator_returns_null.cc =================================================================== --- test/hwasan/TestCases/allocator_returns_null.cc +++ test/hwasan/TestCases/allocator_returns_null.cc @@ -0,0 +1,115 @@ +// Test the behavior of malloc/calloc/realloc/new when the allocation size +// exceeds the HWASan 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 and set errno to +// the appropriate error code. +// +// RUN: %clangxx_hwasan -O0 %s -o %t +// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=1 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH-OOM +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL + +// REQUIRES: stable-runtime + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + assert(argc == 2); + const char *action = argv[1]; + fprintf(stderr, "%s:\n", action); + + static const size_t kMaxAllowedMallocSizePlusOne = (2UL << 30) + 1; + + void *x = nullptr; + 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::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); + free(t); + } 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); + + free(x); + + return x != nullptr; +} + +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: SUMMARY: HWAddressSanitizer: allocation-size-too-big +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: SUMMARY: HWAddressSanitizer: allocation-size-too-big +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: SUMMARY: HWAddressSanitizer: calloc-overflow +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: SUMMARY: HWAddressSanitizer: allocation-size-too-big +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: SUMMARY: HWAddressSanitizer: allocation-size-too-big +// CHECK-nCRASH: new: +// CHECK-nCRASH: SUMMARY: HWAddressSanitizer: allocation-size-too-big +// CHECK-nCRASH-OOM: new: +// CHECK-nCRASH-OOM: SUMMARY: HWAddressSanitizer: out-of-memory +// CHECK-nnCRASH: new-nothrow: +// CHECK-nnCRASH: SUMMARY: HWAddressSanitizer: allocation-size-too-big + +// CHECK-mNULL: malloc: +// CHECK-mNULL: errno: 12 +// CHECK-cNULL: calloc: +// CHECK-cNULL: errno: 12 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: errno: 12 +// CHECK-rNULL: realloc: +// CHECK-rNULL: errno: 12 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: errno: 12 +// CHECK-nnNULL: new-nothrow: