llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-driver Author: Ai Nozaki (ainozaki) <details> <summary>Changes</summary> --- Patch is 36.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/237683.diff 13 Files Affected: - (modified) clang/docs/AllocToken.md (+8) - (modified) clang/docs/ReleaseNotes.md (+4) - (modified) clang/lib/Driver/SanitizerArgs.cpp (+18-4) - (modified) clang/test/Driver/fsanitize-alloc-token.c (+13-2) - (modified) compiler-rt/lib/asan/CMakeLists.txt (+1) - (modified) compiler-rt/lib/asan/asan.syms.extra (+1) - (added) compiler-rt/lib/asan/asan_alloc_token.cpp (+171) - (modified) compiler-rt/lib/asan/asan_allocator.cpp (+81-39) - (modified) compiler-rt/lib/asan/asan_allocator.h (+29-14) - (modified) compiler-rt/lib/asan/asan_descriptions.cpp (+3) - (modified) compiler-rt/lib/asan/asan_descriptions.h (+2) - (added) compiler-rt/test/asan/TestCases/Linux/alloc-token-runtime.cpp (+52) - (added) compiler-rt/test/asan/TestCases/Linux/alloc-token.cpp (+71) ``````````diff diff --git a/clang/docs/AllocToken.md b/clang/docs/AllocToken.md index cca7136b652abd..f86be324f4dc0d 100644 --- a/clang/docs/AllocToken.md +++ b/clang/docs/AllocToken.md @@ -144,6 +144,14 @@ void *__alloc_token_2_Znwm(size_t size); This ABI provides a more efficient alternative where `-falloc-token-max` is small. +### AddressSanitizer Integration + +`-fsanitize=address,alloc-token` includes allocation tokens in heap reports, +including freed allocations in quarantine. It supports non-Android Linux +x86-64 and AArch64 with 64-bit pointers and the default ABI. The fast ABI and +extended instrumentation are unsupported with AddressSanitizer. +Token-enabled allocations reserve one additional shadow granule for metadata. + ### Instrumenting Non-Standard Allocation Functions By default, AllocToken only instruments standard library allocation functions. diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index a2ad6d06815c7c..7de3b520a01b40 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -1453,6 +1453,10 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use ### Sanitizers +- AddressSanitizer supports `-fsanitize=address,alloc-token` to report heap + allocation tokens on non-Android Linux x86-64 and AArch64 with 64-bit pointers + using the default AllocToken ABI. + - Added the `typefunchash` and `typefunchashpointersplit` token assignment modes for `-fsanitize=alloc-token`, selected with `-Xclang -falloc-token-mode=<mode>`. These modes derive token IDs from both diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index a1bfa90e5dceb2..70c60bfeba5a72 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -777,10 +777,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, SanitizerKind::Undefined | SanitizerKind::Memory | SanitizerKind::Type), std::make_pair(SanitizerKind::AllocToken, - SanitizerKind::Address | SanitizerKind::HWAddress | - SanitizerKind::KernelAddress | - SanitizerKind::KernelHWAddress | - SanitizerKind::Memory)}; + SanitizerKind::HWAddress | SanitizerKind::KernelAddress | + SanitizerKind::KernelHWAddress | SanitizerKind::Memory)}; // Enable toolchain specific default sanitizers if not explicitly disabled. SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove; @@ -1322,6 +1320,22 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, AllocTokenExtended = Args.hasFlag( options::OPT_fsanitize_alloc_token_extended, options::OPT_fno_sanitize_alloc_token_extended, AllocTokenExtended); + if ((Kinds & SanitizerKind::Address) && + (Kinds & SanitizerKind::AllocToken) && DiagnoseErrors) { + const llvm::Triple &Triple = TC.getTriple(); + bool SupportedArch = + (Triple.getArch() == llvm::Triple::x86_64 && !Triple.isX32()) || + (Triple.getArch() == llvm::Triple::aarch64 && Triple.isAArch64(64)); + if (!Triple.isOSLinux() || Triple.isAndroid() || !SupportedArch) + D.Diag(diag::err_drv_unsupported_opt_for_target) + << "-fsanitize=address,alloc-token" << Triple.str(); + if (AllocTokenFastABI) + D.Diag(diag::err_drv_argument_not_allowed_with) + << "-fsanitize-alloc-token-fast-abi" << "-fsanitize=address"; + if (AllocTokenExtended) + D.Diag(diag::err_drv_argument_not_allowed_with) + << "-fsanitize-alloc-token-extended" << "-fsanitize=address"; + } } if (AllAddedKinds & SanitizerKind::Type) { diff --git a/clang/test/Driver/fsanitize-alloc-token.c b/clang/test/Driver/fsanitize-alloc-token.c index 0ffe9abad8053b..586624cc8ae94a 100644 --- a/clang/test/Driver/fsanitize-alloc-token.c +++ b/clang/test/Driver/fsanitize-alloc-token.c @@ -16,8 +16,19 @@ // RUN: %clang --target=aarch64-none-elf -fsanitize=alloc-token %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-BAREMETAL // CHECK-BAREMETAL: "-fsanitize=alloc-token" -// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-ADDRESS -// CHECK-INCOMPATIBLE-ADDRESS: error: invalid argument '-fsanitize=alloc-token' not allowed with '-fsanitize=address' +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS +// RUN: %clang --target=aarch64-linux-gnu -fsanitize=address,alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS +// CHECK-ADDRESS: "-fsanitize=address,alloc-token" + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=address,alloc-token -fsanitize-alloc-token-fast-abi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS-FAST +// CHECK-ADDRESS-FAST: error: invalid argument '-fsanitize-alloc-token-fast-abi' not allowed with '-fsanitize=address' +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=address,alloc-token -fsanitize-alloc-token-extended %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS-EXTENDED +// CHECK-ADDRESS-EXTENDED: error: invalid argument '-fsanitize-alloc-token-extended' not allowed with '-fsanitize=address' +// RUN: not %clang --target=x86_64-apple-darwin -fsanitize=address,alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS-TARGET +// RUN: not %clang --target=aarch64-linux-android -fsanitize=address,alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS-TARGET +// RUN: not %clang --target=i386-linux-gnu -fsanitize=address,alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS-TARGET +// RUN: not %clang --target=x86_64-linux-gnux32 -fsanitize=address,alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ADDRESS-TARGET +// CHECK-ADDRESS-TARGET: error: unsupported option '-fsanitize=address,alloc-token' for target // RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token,memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-MEMORY // CHECK-INCOMPATIBLE-MEMORY: error: invalid argument '-fsanitize=alloc-token' not allowed with '-fsanitize=memory' diff --git a/compiler-rt/lib/asan/CMakeLists.txt b/compiler-rt/lib/asan/CMakeLists.txt index 580418fa64e312..14fd39bc42d1a2 100644 --- a/compiler-rt/lib/asan/CMakeLists.txt +++ b/compiler-rt/lib/asan/CMakeLists.txt @@ -3,6 +3,7 @@ set(ASAN_SOURCES asan_aix.cpp asan_allocator.cpp + asan_alloc_token.cpp asan_activation.cpp asan_debugging.cpp asan_descriptions.cpp diff --git a/compiler-rt/lib/asan/asan.syms.extra b/compiler-rt/lib/asan/asan.syms.extra index f8e9b3aedcc3b7..2fb970b5f3ec55 100644 --- a/compiler-rt/lib/asan/asan.syms.extra +++ b/compiler-rt/lib/asan/asan.syms.extra @@ -1,4 +1,5 @@ __asan_* +__alloc_token_* __lsan_* __ubsan_* __sancov_* diff --git a/compiler-rt/lib/asan/asan_alloc_token.cpp b/compiler-rt/lib/asan/asan_alloc_token.cpp new file mode 100644 index 00000000000000..75f44c6c857b24 --- /dev/null +++ b/compiler-rt/lib/asan/asan_alloc_token.cpp @@ -0,0 +1,171 @@ +//===-- asan_alloc_token.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Allocation entry points for the default AllocToken ABI. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +#if SANITIZER_LINUX && !SANITIZER_ANDROID && SANITIZER_WORDSIZE == 64 && \ + (SANITIZER_X64 || defined(__aarch64__)) + +# include "asan_allocator.h" +# include "asan_report.h" +# include "asan_stack.h" + +using namespace __asan; + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_malloc(uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_malloc(size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_calloc(uptr count, uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_calloc(count, size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_realloc(void* ptr, uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_reallocarray(void* ptr, uptr count, uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_reallocarray(ptr, count, size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_memalign(uptr alignment, uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_memalign(alignment, size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_aligned_alloc(uptr alignment, uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_aligned_alloc(alignment, size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __alloc_token_posix_memalign(void** ptr, uptr alignment, uptr size, + uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_posix_memalign(ptr, alignment, size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_valloc(uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_valloc(size, &stack, &token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* __alloc_token_pvalloc(uptr size, uptr token) { + GET_STACK_TRACE_MALLOC; + return asan_pvalloc(size, &stack, &token); +} +} // extern "C" + +// Use ABI-level types for the mangled C++ allocation entry points. +# define ALLOC_TOKEN_NEW(Name, Args, Allocate, CanReturnNull) \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE void* Name Args { \ + GET_STACK_TRACE_MALLOC; \ + void* ptr = Allocate; \ + if (!CanReturnNull && UNLIKELY(!ptr)) \ + ReportOutOfMemory(size, &stack); \ + return ptr; \ + } + +# define ALLOC_TOKEN_NEW_VARIANTS(Name, New, AlignedNew) \ + ALLOC_TOKEN_NEW(Name, (uptr size, uptr token), New(size, &stack, &token), \ + false) \ + ALLOC_TOKEN_NEW(Name##RKSt9nothrow_t, \ + (uptr size, const void*, uptr token), \ + New(size, &stack, &token), true) \ + ALLOC_TOKEN_NEW(Name##St11align_val_t, \ + (uptr size, uptr alignment, uptr token), \ + AlignedNew(size, alignment, &stack, &token), false) \ + ALLOC_TOKEN_NEW(Name##St11align_val_tRKSt9nothrow_t, \ + (uptr size, uptr alignment, const void*, uptr token), \ + AlignedNew(size, alignment, &stack, &token), true) + +ALLOC_TOKEN_NEW_VARIANTS(__alloc_token__Znwm, asan_new, asan_new_aligned) +ALLOC_TOKEN_NEW_VARIANTS(__alloc_token__Znam, asan_new_array, + asan_new_array_aligned) + +// Hot/cold hints do not affect ASan's allocation policy. +# define ALLOC_TOKEN_HOT_COLD_NEW_VARIANTS(Name, New, AlignedNew) \ + ALLOC_TOKEN_NEW(Name##12__hot_cold_t, (uptr size, u8, uptr token), \ + New(size, &stack, &token), false) \ + ALLOC_TOKEN_NEW(Name##RKSt9nothrow_t12__hot_cold_t, \ + (uptr size, const void*, u8, uptr token), \ + New(size, &stack, &token), true) \ + ALLOC_TOKEN_NEW(Name##St11align_val_t12__hot_cold_t, \ + (uptr size, uptr alignment, u8, uptr token), \ + AlignedNew(size, alignment, &stack, &token), false) \ + ALLOC_TOKEN_NEW(Name##St11align_val_tRKSt9nothrow_t12__hot_cold_t, \ + (uptr size, uptr alignment, const void*, u8, uptr token), \ + AlignedNew(size, alignment, &stack, &token), true) + +ALLOC_TOKEN_HOT_COLD_NEW_VARIANTS(__alloc_token__Znwm, asan_new, + asan_new_aligned) +ALLOC_TOKEN_HOT_COLD_NEW_VARIANTS(__alloc_token__Znam, asan_new_array, + asan_new_array_aligned) +# undef ALLOC_TOKEN_HOT_COLD_NEW_VARIANTS + +# undef ALLOC_TOKEN_NEW_VARIANTS +# undef ALLOC_TOKEN_NEW + +struct SizeReturningNewResult { + void* ptr; + uptr size; +}; + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +SizeReturningNewResult __alloc_token___size_returning_new(uptr size, + uptr token) { + GET_STACK_TRACE_MALLOC; + void* ptr = asan_new(size, &stack, &token); + if (UNLIKELY(!ptr)) + ReportOutOfMemory(size, &stack); + return {ptr, size}; +} + +SANITIZER_INTERFACE_ATTRIBUTE +SizeReturningNewResult __alloc_token___size_returning_new_aligned( + uptr size, uptr alignment, uptr token) { + GET_STACK_TRACE_MALLOC; + void* ptr = asan_new_aligned(size, alignment, &stack, &token); + if (UNLIKELY(!ptr)) + ReportOutOfMemory(size, &stack); + return {ptr, size}; +} + +SANITIZER_INTERFACE_ATTRIBUTE +SizeReturningNewResult __alloc_token___size_returning_new_hot_cold(uptr size, + u8, + uptr token) { + return __alloc_token___size_returning_new(size, token); +} + +SANITIZER_INTERFACE_ATTRIBUTE +SizeReturningNewResult __alloc_token___size_returning_new_aligned_hot_cold( + uptr size, uptr alignment, u8, uptr token) { + return __alloc_token___size_returning_new_aligned(size, alignment, token); +} +} // extern "C" + +#endif // Supported Linux targets. diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp index b75a8ea09e34af..ec76d2a2811ef6 100644 --- a/compiler-rt/lib/asan/asan_allocator.cpp +++ b/compiler-rt/lib/asan/asan_allocator.cpp @@ -93,6 +93,9 @@ class ChunkHeader { atomic_uint8_t chunk_state; u8 alloc_type : 2; u8 lsan_tag : 2; +#if SANITIZER_LINUX + u8 has_alloc_token : 1; +#endif #if SANITIZER_WINDOWS // True if this was a zero-size allocation upgraded to size 1. // Used to report the original size (0) to the user via HeapSize/RtlSizeHeap. @@ -165,6 +168,11 @@ enum { class AsanChunk : public ChunkBase { public: uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; } + // The token follows both the user region and ChunkBase. + uptr* AllocTokenAddress() { + return reinterpret_cast<uptr*>(RoundUpTo( + Beg() + Max(UsedSize(), kChunkHeader2Size), ASAN_SHADOW_GRANULARITY)); + } bool AddrIsInside(uptr addr) { return (addr >= Beg()) && (addr < Beg() + UsedSize()); } @@ -540,7 +548,7 @@ struct Allocator { // (true) or a fatal Report*+Die() (false). void* AllocateImpl(uptr size, uptr alignment, BufferedStackTrace* stack, AllocType alloc_type, bool can_fill, - bool may_return_null) { + bool may_return_null, const uptr* alloc_token = nullptr) { if (UNLIKELY(!AsanInited())) AsanInitFromRtl(); if (UNLIKELY(IsRssLimitExceeded())) { @@ -573,6 +581,8 @@ struct Allocator { uptr needed_size = rounded_size + rz_size; if (alignment > min_alignment) needed_size += alignment; + if (alloc_token) + needed_size += ASAN_SHADOW_GRANULARITY; bool from_primary = PrimaryAllocator::CanAllocate(needed_size, alignment); // If we are allocating from the secondary allocator, there will be no // automatic right redzone, so add the right redzone manually. @@ -624,6 +634,11 @@ struct Allocator { CHECK(size); m->SetUsedSize(size); m->user_requested_alignment_log = user_requested_alignment_log; +#if SANITIZER_LINUX + m->has_alloc_token = alloc_token != nullptr; + if (alloc_token) + *m->AllocTokenAddress() = *alloc_token; +#endif m->SetAllocContext(t ? t->tid() : kMainTid, StackDepotPut(*stack)); @@ -684,9 +699,10 @@ struct Allocator { // Defer to the global, flag controlled, OOM policy. void* Allocate(uptr size, uptr alignment, BufferedStackTrace* stack, - AllocType alloc_type, bool can_fill) { + AllocType alloc_type, bool can_fill, + const uptr* alloc_token = nullptr) { return AllocateImpl(size, alignment, stack, alloc_type, can_fill, - AllocatorMayReturnNull()); + AllocatorMayReturnNull(), alloc_token); } // Set quarantine flag if chunk is allocated, issue ASan error report on @@ -799,7 +815,8 @@ struct Allocator { QuarantineChunk(m, ptr, stack); } - void* Reallocate(void* old_ptr, uptr new_size, BufferedStackTrace* stack) { + void* Reallocate(void* old_ptr, uptr new_size, BufferedStackTrace* stack, + const uptr* alloc_token = nullptr) { CHECK(old_ptr && new_size); uptr p = reinterpret_cast<uptr>(old_ptr); uptr chunk_beg = p - kChunkHeaderSize; @@ -810,7 +827,7 @@ struct Allocator { thread_stats.realloced += new_size; void* new_ptr = Allocate(new_size, /*alignment=*/8, stack, FROM_MALLOC, - /*can_fill=*/true); + /*can_fill=*/true, alloc_token); if (new_ptr) { u8 chunk_state = atomic_load(&m->chunk_state, memory_order_acquire); if (chunk_state != CHUNK_ALLOCATED) @@ -826,14 +843,14 @@ struct Allocator { } void* Calloc(uptr nmemb, uptr size, BufferedStackTrace* stack, - uptr align = 8) { + uptr align = 8, const uptr* alloc_token = nullptr) { if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { if (AllocatorMayReturnNull()) return nullptr; ReportCallocOverflow(nmemb, size, stack); } void* ptr = Allocate(nmemb * size, align, stack, FROM_MALLOC, - /*can_fill=*/false); + /*can_fill=*/false, alloc_token); // If the memory comes from the secondary allocator no need to clear it // as it comes directly from mmap. if (ptr && allocator.FromPrimary(ptr)) @@ -1018,6 +1035,16 @@ AllocType AsanChunkView::GetAllocType() const { return (AllocType)chunk_->alloc_type; } +bool AsanChunkView::GetAllocToken(uptr* token) const { +#if SANITIZER_LINUX + if (chunk_->has_alloc_token) { + *token = *chunk_->AllocTokenAddress(); + return true; + } +#endif + return false; +} + u32 AsanChunkView::GetAllocStackId() const { u32 tid = 0; u32 stack = 0; @@ -1078,13 +1105,17 @@ void asan_free_aligned_sized(void* ptr, uptr alignment, uptr size, instance.Deallocate(ptr, size, alignment, stack, FROM_MALLOC); } -void* asan_malloc(uptr size, BufferedStackTrace* stack) { +void* asan_malloc(uptr size, BufferedStackTrace* stack, + const uptr* alloc_token) { return SetErrnoOnNull(instance.Allocate(size, /*alignment=*/8, stack, - FROM_MALLOC, /*can_fill=*/true)); + FROM_MALLOC, /*can_fill=*/true, + alloc_token)); } -void* asan_calloc(uptr nmemb, uptr size, BufferedStackTrace* stack) { - return SetErrnoOnNull(instance.Calloc(nmemb, size, stack)); +void* asan_calloc(uptr nmemb, uptr size, BufferedStackTrace* stack, + const uptr* alloc_token) { + return SetErrnoOnNull( + instance.Calloc(nmemb, size, stack, /*align=*/8, alloc_token)); } #if SANITIZER_AIX @@ -1099,20 +1130,22 @@ void* asan_vec_calloc(uptr nmemb, uptr size, BufferedStackTrace* stack) { #endif void* asan_reallocarray(void* p, uptr nmemb, uptr size, - BufferedStackTrace* stack) { + BufferedStackTrace* stack, const uptr* alloc_token) { if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { errno = errno_ENOMEM; if (AllocatorMayRetur... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/237683 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
