https://github.com/ainozaki created 
https://github.com/llvm/llvm-project/pull/237683

None

>From def4515d7bc697567f144536d01daa3372a4b87b Mon Sep 17 00:00:00 2001
From: ainozaki <[email protected]>
Date: Sun, 1 Nov 2026 05:25:32 +0000
Subject: [PATCH] Asan reports AllocToken

---
 clang/docs/AllocToken.md                      |   8 +
 clang/docs/ReleaseNotes.md                    |   4 +
 clang/lib/Driver/SanitizerArgs.cpp            |  22 ++-
 clang/test/Driver/fsanitize-alloc-token.c     |  15 +-
 compiler-rt/lib/asan/CMakeLists.txt           |   1 +
 compiler-rt/lib/asan/asan.syms.extra          |   1 +
 compiler-rt/lib/asan/asan_alloc_token.cpp     | 171 ++++++++++++++++++
 compiler-rt/lib/asan/asan_allocator.cpp       | 120 ++++++++----
 compiler-rt/lib/asan/asan_allocator.h         |  43 +++--
 compiler-rt/lib/asan/asan_descriptions.cpp    |   3 +
 compiler-rt/lib/asan/asan_descriptions.h      |   2 +
 .../TestCases/Linux/alloc-token-runtime.cpp   |  52 ++++++
 .../test/asan/TestCases/Linux/alloc-token.cpp |  71 ++++++++
 13 files changed, 454 insertions(+), 59 deletions(-)
 create mode 100644 compiler-rt/lib/asan/asan_alloc_token.cpp
 create mode 100644 
compiler-rt/test/asan/TestCases/Linux/alloc-token-runtime.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/Linux/alloc-token.cpp

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 (AllocatorMayReturnNull())
       return nullptr;
     ReportReallocArrayOverflow(nmemb, size, stack);
   }
-  return asan_realloc(p, nmemb * size, stack);
+  return asan_realloc(p, nmemb * size, stack, alloc_token);
 }
 
-void* asan_realloc(void* p, uptr size, BufferedStackTrace* stack) {
+void* asan_realloc(void* p, uptr size, BufferedStackTrace* stack,
+                   const uptr* alloc_token) {
   if (!p)
     return SetErrnoOnNull(instance.Allocate(size, /*alignment=*/8, stack,
-                                            FROM_MALLOC, /*can_fill=*/true));
+                                            FROM_MALLOC, /*can_fill=*/true,
+                                            alloc_token));
   if (size == 0) {
     if (flags()->allocator_frees_and_returns_null_on_realloc_zero) {
       instance.Deallocate(p, 0, 0, stack, FROM_MALLOC);
@@ -1121,15 +1154,18 @@ void* asan_realloc(void* p, uptr size, 
BufferedStackTrace* stack) {
     // Allocate a size of 1 if we shouldn't free() on Realloc to 0
     size = 1;
   }
-  return SetErrnoOnNull(instance.Reallocate(p, size, stack));
+  return SetErrnoOnNull(instance.Reallocate(p, size, stack, alloc_token));
 }
 
-void* asan_valloc(uptr size, BufferedStackTrace* stack) {
+void* asan_valloc(uptr size, BufferedStackTrace* stack,
+                  const uptr* alloc_token) {
   return SetErrnoOnNull(instance.Allocate(size, GetPageSizeCached(), stack,
-                                          FROM_MALLOC, /*can_fill=*/true));
+                                          FROM_MALLOC, /*can_fill=*/true,
+                                          alloc_token));
 }
 
-void* asan_pvalloc(uptr size, BufferedStackTrace* stack) {
+void* asan_pvalloc(uptr size, BufferedStackTrace* stack,
+                   const uptr* alloc_token) {
   uptr PageSize = GetPageSizeCached();
   if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
     errno = errno_ENOMEM;
@@ -1139,11 +1175,12 @@ void* asan_pvalloc(uptr size, BufferedStackTrace* 
stack) {
   }
   // pvalloc(0) should allocate one page.
   size = size ? RoundUpTo(size, PageSize) : PageSize;
-  return SetErrnoOnNull(
-      instance.Allocate(size, PageSize, stack, FROM_MALLOC, 
/*can_fill=*/true));
+  return SetErrnoOnNull(instance.Allocate(size, PageSize, stack, FROM_MALLOC,
+                                          /*can_fill=*/true, alloc_token));
 }
 
-void* asan_memalign(uptr alignment, uptr size, BufferedStackTrace* stack) {
+void* asan_memalign(uptr alignment, uptr size, BufferedStackTrace* stack,
+                    const uptr* alloc_token) {
   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
@@ -1151,10 +1188,11 @@ void* asan_memalign(uptr alignment, uptr size, 
BufferedStackTrace* stack) {
     ReportInvalidAllocationAlignment(alignment, stack);
   }
   return SetErrnoOnNull(instance.Allocate(size, alignment, stack, FROM_MALLOC,
-                                          /*can_fill=*/true));
+                                          /*can_fill=*/true, alloc_token));
 }
 
-void* asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace* stack) 
{
+void* asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace* stack,
+                         const uptr* alloc_token) {
   if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
@@ -1162,18 +1200,18 @@ void* asan_aligned_alloc(uptr alignment, uptr size, 
BufferedStackTrace* stack) {
     ReportInvalidAlignedAllocAlignment(size, alignment, stack);
   }
   return SetErrnoOnNull(instance.Allocate(size, alignment, stack, FROM_MALLOC,
-                                          /*can_fill=*/true));
+                                          /*can_fill=*/true, alloc_token));
 }
 
 int asan_posix_memalign(void** memptr, uptr alignment, uptr size,
-                        BufferedStackTrace* stack) {
+                        BufferedStackTrace* stack, const uptr* alloc_token) {
   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
     if (AllocatorMayReturnNull())
       return errno_EINVAL;
     ReportInvalidPosixMemalignAlignment(alignment, stack);
   }
   void* ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC,
-                                /*can_fill=*/true);
+                                /*can_fill=*/true, alloc_token);
   if (UNLIKELY(!ptr))
     // OOM error is already taken care of by Allocate.
     return errno_ENOMEM;
@@ -1206,19 +1244,20 @@ uptr asan_malloc_usable_size(const void* ptr, uptr pc, 
uptr bp) {
 
 namespace {
 
-void* asan_new(uptr size, BufferedStackTrace* stack, bool array) {
+void* asan_new(uptr size, BufferedStackTrace* stack, bool array,
+               const uptr* alloc_token) {
   return SetErrnoOnNull(instance.Allocate(size, /*alignment=*/0, stack,
                                           array ? FROM_NEW_BR : FROM_NEW,
-                                          /*can_fill=*/true));
+                                          /*can_fill=*/true, alloc_token));
 }
 
 void* asan_new_aligned(uptr size, uptr alignment, BufferedStackTrace* stack,
-                       bool array) {
+                       bool array, const uptr* alloc_token) {
   if (UNLIKELY(!CheckAlignedNewAlignment(alignment)))
     ReportInvalidAllocationAlignment(alignment, stack);
   return SetErrnoOnNull(instance.Allocate(size, alignment, stack,
                                           array ? FROM_NEW_BR : FROM_NEW,
-                                          /*can_fill=*/true));
+                                          /*can_fill=*/true, alloc_token));
 }
 
 void asan_delete(void* ptr, BufferedStackTrace* stack, bool array) {
@@ -1243,21 +1282,24 @@ void asan_delete_sized_aligned(void* ptr, uptr size, 
uptr alignment,
 
 }  // namespace
 
-void* asan_new(uptr size, BufferedStackTrace* stack) {
-  return asan_new(size, stack, /*array=*/false);
+void* asan_new(uptr size, BufferedStackTrace* stack, const uptr* alloc_token) {
+  return asan_new(size, stack, /*array=*/false, alloc_token);
 }
 
-void* asan_new_aligned(uptr size, uptr alignment, BufferedStackTrace* stack) {
-  return asan_new_aligned(size, alignment, stack, /*array=*/false);
+void* asan_new_aligned(uptr size, uptr alignment, BufferedStackTrace* stack,
+                       const uptr* alloc_token) {
+  return asan_new_aligned(size, alignment, stack, /*array=*/false, 
alloc_token);
 }
 
-void* asan_new_array(uptr size, BufferedStackTrace* stack) {
-  return asan_new(size, stack, /*array=*/true);
+void* asan_new_array(uptr size, BufferedStackTrace* stack,
+                     const uptr* alloc_token) {
+  return asan_new(size, stack, /*array=*/true, alloc_token);
 }
 
 void* asan_new_array_aligned(uptr size, uptr alignment,
-                             BufferedStackTrace* stack) {
-  return asan_new_aligned(size, alignment, stack, /*array=*/true);
+                             BufferedStackTrace* stack,
+                             const uptr* alloc_token) {
+  return asan_new_aligned(size, alignment, stack, /*array=*/true, alloc_token);
 }
 
 void asan_delete(void* ptr, BufferedStackTrace* stack) {
diff --git a/compiler-rt/lib/asan/asan_allocator.h 
b/compiler-rt/lib/asan/asan_allocator.h
index f3b215b7b05ee8..975c20afc020e1 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -66,6 +66,7 @@ class AsanChunkView {
   u32 GetAllocStackId() const;
   u32 GetFreeStackId() const;
   AllocType GetAllocType() const;
+  bool GetAllocToken(uptr* token) const;
   bool AddrIsInside(uptr addr, uptr access_size, sptr* offset) const {
     if (addr >= Beg() && (addr + access_size) <= End()) {
       *offset = addr - Beg();
@@ -283,34 +284,48 @@ struct AsanThreadLocalMallocStorage {
   AsanThreadLocalMallocStorage() {}
 };
 
-void* asan_memalign(uptr alignment, uptr size, BufferedStackTrace* stack);
+// A null alloc_token means no token was supplied; zero is a valid token value.
+void* asan_memalign(uptr alignment, uptr size, BufferedStackTrace* stack,
+                    const uptr* alloc_token = nullptr);
 void asan_free(void* ptr, BufferedStackTrace* stack);
 void asan_free_sized(void* ptr, uptr size, BufferedStackTrace* stack);
 void asan_free_aligned_sized(void* ptr, uptr alignment, uptr size,
                              BufferedStackTrace* stack);
 
-void* asan_malloc(uptr size, BufferedStackTrace* stack);
-void* asan_calloc(uptr nmemb, uptr size, BufferedStackTrace* stack);
+void* asan_malloc(uptr size, BufferedStackTrace* stack,
+                  const uptr* alloc_token = nullptr);
+void* asan_calloc(uptr nmemb, uptr size, BufferedStackTrace* stack,
+                  const uptr* alloc_token = nullptr);
 #if SANITIZER_AIX
 void* asan_vec_malloc(uptr size, BufferedStackTrace* stack);
 void* asan_vec_calloc(uptr nmemb, uptr size, BufferedStackTrace* stack);
 #endif
-void* asan_realloc(void* p, uptr size, BufferedStackTrace* stack);
+void* asan_realloc(void* p, uptr size, BufferedStackTrace* stack,
+                   const uptr* alloc_token = nullptr);
 void* asan_reallocarray(void* p, uptr nmemb, uptr size,
-                        BufferedStackTrace* stack);
-void* asan_valloc(uptr size, BufferedStackTrace* stack);
-void* asan_pvalloc(uptr size, BufferedStackTrace* stack);
-
-void* asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace* stack);
+                        BufferedStackTrace* stack,
+                        const uptr* alloc_token = nullptr);
+void* asan_valloc(uptr size, BufferedStackTrace* stack,
+                  const uptr* alloc_token = nullptr);
+void* asan_pvalloc(uptr size, BufferedStackTrace* stack,
+                   const uptr* alloc_token = nullptr);
+
+void* asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace* stack,
+                         const uptr* alloc_token = nullptr);
 int asan_posix_memalign(void** memptr, uptr alignment, uptr size,
-                        BufferedStackTrace* stack);
+                        BufferedStackTrace* stack,
+                        const uptr* alloc_token = nullptr);
 uptr asan_malloc_usable_size(const void* ptr, uptr pc, uptr bp);
 
-void* asan_new(uptr size, BufferedStackTrace* stack);
-void* asan_new_aligned(uptr size, uptr alignment, BufferedStackTrace* stack);
-void* asan_new_array(uptr size, BufferedStackTrace* stack);
+void* asan_new(uptr size, BufferedStackTrace* stack,
+               const uptr* alloc_token = nullptr);
+void* asan_new_aligned(uptr size, uptr alignment, BufferedStackTrace* stack,
+                       const uptr* alloc_token = nullptr);
+void* asan_new_array(uptr size, BufferedStackTrace* stack,
+                     const uptr* alloc_token = nullptr);
 void* asan_new_array_aligned(uptr size, uptr alignment,
-                             BufferedStackTrace* stack);
+                             BufferedStackTrace* stack,
+                             const uptr* alloc_token = nullptr);
 void asan_delete(void* ptr, BufferedStackTrace* stack);
 void asan_delete_aligned(void* ptr, uptr alignment, BufferedStackTrace* stack);
 void asan_delete_sized(void* ptr, uptr size, BufferedStackTrace* stack);
diff --git a/compiler-rt/lib/asan/asan_descriptions.cpp 
b/compiler-rt/lib/asan/asan_descriptions.cpp
index 551b819a4c436d..415121601925ec 100644
--- a/compiler-rt/lib/asan/asan_descriptions.cpp
+++ b/compiler-rt/lib/asan/asan_descriptions.cpp
@@ -172,6 +172,7 @@ bool GetHeapAddressInformation(uptr addr, uptr access_size,
   CHECK_NE(chunk.AllocTid(), kInvalidTid);
   descr->alloc_tid = chunk.AllocTid();
   descr->alloc_stack_id = chunk.GetAllocStackId();
+  descr->has_alloc_token = chunk.GetAllocToken(&descr->alloc_token);
   descr->free_tid = chunk.FreeTid();
   if (descr->free_tid != kInvalidTid)
     descr->free_stack_id = chunk.GetFreeStackId();
@@ -419,6 +420,8 @@ void StackAddressDescription::Print() const {
 
 void HeapAddressDescription::Print() const {
   PrintHeapChunkAccess(addr, chunk_access);
+  if (has_alloc_token)
+    Printf("Allocation token: 0x%zx\n", alloc_token);
 
   asanThreadRegistry().CheckLocked();
   AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
diff --git a/compiler-rt/lib/asan/asan_descriptions.h 
b/compiler-rt/lib/asan/asan_descriptions.h
index a614f47d461bbd..a4ece0f6957a94 100644
--- a/compiler-rt/lib/asan/asan_descriptions.h
+++ b/compiler-rt/lib/asan/asan_descriptions.h
@@ -121,6 +121,8 @@ struct HeapAddressDescription {
   uptr free_tid;
   u32 alloc_stack_id;
   u32 free_stack_id;
+  uptr alloc_token;
+  bool has_alloc_token;
   ChunkAccess chunk_access;
 
   void Print() const;
diff --git a/compiler-rt/test/asan/TestCases/Linux/alloc-token-runtime.cpp 
b/compiler-rt/test/asan/TestCases/Linux/alloc-token-runtime.cpp
new file mode 100644
index 00000000000000..f544cc18d38a27
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Linux/alloc-token-runtime.cpp
@@ -0,0 +1,52 @@
+// RUN: %clangxx_asan -O0 %s -o %t
+// RUN: %env_asan_opts=detect_leaks=0:allocator_may_return_null=1 %run %t 2>&1 
| FileCheck %s
+// RUN: 
%env_asan_opts=detect_leaks=0:allocator_may_return_null=1:malloc_context_size=0 
%run %t 2>&1 | FileCheck %s
+// REQUIRES: linux, (x86_64-target-arch || aarch64-target-arch)
+// UNSUPPORTED: android
+
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sanitizer/asan_interface.h>
+
+extern "C" {
+void *__alloc_token_calloc(size_t, size_t, size_t);
+void *__alloc_token_realloc(void *, size_t, size_t);
+void *__alloc_token_aligned_alloc(size_t, size_t, size_t);
+}
+
+int main() {
+  void *p = __alloc_token_calloc(1, 1, 0);
+  assert(*static_cast<char *>(p) == 0);
+  __asan_describe_address(p);
+  void *q = __alloc_token_realloc(p, 64, SIZE_MAX);
+  __asan_describe_address(p);
+  // CHECK: Allocation token: 0x0
+  // CHECK: Allocation token: 0x0
+  // CHECK: freed by thread
+
+  memset(q, 1, 64);
+  assert(!__alloc_token_realloc(q, SIZE_MAX, 1));
+  __asan_describe_address(q);
+  free(q);
+  __asan_describe_address(q);
+  // CHECK: Allocation token: 0xffffffffffffffff
+  // CHECK: Allocation token: 0xffffffffffffffff
+  // CHECK: freed by thread
+
+  p = __alloc_token_aligned_alloc(4096, 4 << 20, 0x123);
+  assert(reinterpret_cast<uintptr_t>(p) % 4096 == 0);
+  memset(p, 1, 4 << 20);
+  free(p);
+  __asan_describe_address(p);
+  // CHECK: Allocation token: 0x123
+
+  fprintf(stderr, "ordinary allocation\n");
+  p = malloc(64);
+  __asan_describe_address(p);
+  free(p);
+  // CHECK: ordinary allocation
+  // CHECK-NOT: Allocation token:
+}
diff --git a/compiler-rt/test/asan/TestCases/Linux/alloc-token.cpp 
b/compiler-rt/test/asan/TestCases/Linux/alloc-token.cpp
new file mode 100644
index 00000000000000..28ea7ef1505b28
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Linux/alloc-token.cpp
@@ -0,0 +1,71 @@
+// RUN: %clangxx_asan -std=c++17 -O0 -fsanitize=alloc-token 
-falloc-token-max=1 %s -o %t
+// RUN: not %run %t 0 2>&1 | FileCheck %s --check-prefixes=CHECK,UAF
+// RUN: not %run %t 1 2>&1 | FileCheck %s --check-prefixes=CHECK,UAF
+// RUN: not %run %t 2 2>&1 | FileCheck %s --check-prefixes=CHECK,OVERFLOW
+// RUN: not %run %t 3 2>&1 | FileCheck %s --check-prefix=PLAIN
+// RUN: %if x86_64-target-arch %{ 
%env_asan_opts=detect_leaks=0:quarantine_size_mb=0:thread_local_quarantine_size_kb=0
 %run %t 4 2>&1 | FileCheck %s --check-prefix=REUSE %}
+// RUN: %clangxx_asan -std=c++17 -O2 -fsanitize=alloc-token 
-falloc-token-max=1 %s -o %t
+// RUN: not %run %t 0 2>&1 | FileCheck %s --check-prefixes=CHECK,UAF
+// RUN: not %run %t 1 2>&1 | FileCheck %s --check-prefixes=CHECK,UAF
+// REQUIRES: linux, (x86_64-target-arch || aarch64-target-arch)
+// UNSUPPORTED: android
+
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <new>
+#include <sanitizer/asan_interface.h>
+
+struct alignas(64) Aligned {
+  int value;
+};
+
+static void report_callback(const char *report) {
+  assert(strstr(report, "Allocation token: 0x0"));
+  fprintf(stderr, "Token in report buffer\n");
+}
+
+__attribute__((noinline, no_sanitize("alloc-token"))) void *
+ordinary_allocation() {
+  return malloc(40);
+}
+
+int main(int argc, char **argv) {
+  int kind = atoi(argv[1]);
+  if (kind == 4) {
+    // Both requests use the same x86-64 size class.
+    void *p = malloc(32);
+    __asan_describe_address(p);
+    free(p);
+    void *q = ordinary_allocation();
+    assert(p == q);
+    memset(q, 1, 40);
+    __asan_describe_address(q);
+    free(q);
+    return 0;
+  }
+  if (kind != 3)
+    __asan_set_error_report_callback(report_callback);
+  if (kind == 1) {
+    volatile Aligned *p = new (std::nothrow) Aligned[2];
+    delete[] p;
+    p->value = 1;
+  } else {
+    volatile int *p =
+        static_cast<int *>(kind == 3 ? ordinary_allocation() : malloc(64));
+    if (kind != 2)
+      free(const_cast<int *>(p));
+    p[kind == 2 ? 16 : 0] = 1;
+  }
+}
+
+// UAF: ERROR: AddressSanitizer: heap-use-after-free
+// OVERFLOW: ERROR: AddressSanitizer: heap-buffer-overflow
+// CHECK: Allocation token: 0x0
+// CHECK: Token in report buffer
+// PLAIN: ERROR: AddressSanitizer: heap-use-after-free
+// PLAIN-NOT: Allocation token:
+// REUSE: Allocation token: 0x0
+// REUSE: 40-byte region
+// REUSE-NOT: Allocation token:

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to