[compiler-rt] [compiler-rt][asan] Add AMDGPU ASan support via HSA API interceptors. (PR #192240)

Amit Kumar Pandey via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 17 01:55:57 PDT 2026


https://github.com/ampandey-1995 updated https://github.com/llvm/llvm-project/pull/192240

>From 7e4f9cf6c2a8e8d972214925f5094057f96ace70 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 15 Apr 2026 16:00:46 +0530
Subject: [PATCH 01/46] [compiler-rt][asan] Add AMDGPU ASan support via HSA API
 interceptors.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add initial AddressSanitizer support for AMDGPU by intercepting HSA APIs
used for host-side initialization and CPU↔GPU memory operations
(allocation, copies, IPC, vmem, and other related queries).

This support is guarded by `SANITIZER_AMDGPU`, which enables inclusion of
the ROCm HSA and COMgr headers required to build the interceptors.
---
 compiler-rt/CMakeLists.txt                    |  20 +++
 compiler-rt/lib/asan/asan_interceptors.cpp    | 164 ++++++++++++++++++
 .../lib/sanitizer_common/sanitizer_platform.h |   2 +-
 3 files changed, 185 insertions(+), 1 deletion(-)

diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index d720b86c6df24..50f5515422833 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -561,6 +561,26 @@ elseif(COMPILER_RT_HAS_G_FLAG)
   list(APPEND SANITIZER_COMMON_CFLAGS -g)
 endif()
 
+if(SANITIZER_AMDGPU)
+  list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDGPU=1)
+  message(STATUS "Looking 'hsa' and 'amd_comgr' header")
+  find_path(HSA_INCLUDE NAMES hsa.h HINTS ${SANITIZER_HSA_INCLUDE_PATH} /opt/rocm/include PATH_SUFFIXES hsa)
+  if(NOT HSA_INCLUDE)
+    message(FATAL_ERROR "Required header 'hsa.h' not found in path ${HSA_INCLUDE}. Aborting SANITIZER_AMDGPU build")
+  endif()
+  message(STATUS "Found 'hsa.h' in ${HSA_INCLUDE}")
+  include_directories(${HSA_INCLUDE})
+  find_path(COMgr_INCLUDE NAMES amd_comgr.h.in HINTS ${SANITIZER_COMGR_INCLUDE_PATH} PATH_SUFFIXES amd_comgr)
+  if(NOT COMgr_INCLUDE)
+    find_path(COMgr_INCLUDE NAMES amd_comgr.h HINTS /opt/rocm/include PATH_SUFFIXES amd_comgr)
+    if(NOT COMgr_INCLUDE)
+      message(FATAL_ERROR "Required header 'amd_comgr.h/amd_comgr.h.in' not found in path ${COMgr_INCLUDE}. Aborting SANITIZER_AMDGPU build")
+    endif()
+  endif()
+  message(STATUS "Found 'amd_comgr.h.in/amd_comgr.h' in ${COMgr_INCLUDE}")
+  include_directories(${COMgr_INCLUDE})
+endif()
+
 if(LLVM_ENABLE_MODULES)
   # Sanitizers cannot be built with -fmodules. The interceptors intentionally
   # don't include system headers, which is incompatible with modules.
diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp
index 5075919a47d50..16e827c635f21 100644
--- a/compiler-rt/lib/asan/asan_interceptors.cpp
+++ b/compiler-rt/lib/asan/asan_interceptors.cpp
@@ -895,6 +895,166 @@ DEFINE_REAL(int, vfork, )
 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork, )
 #  endif
 
+#  if SANITIZER_AMDGPU
+void ENSURE_HSA_INITED();
+
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_allocate,
+            hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
+            void** ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_MALLOC;
+  return asan_hsa_amd_memory_pool_allocate(memory_pool, size, flags, ptr,
+                                           &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_free, void* ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_FREE;
+  return asan_hsa_amd_memory_pool_free(ptr, &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
+            const hsa_agent_t* agents, const uint32_t* flags, const void* ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_FREE;
+  return asan_hsa_amd_agents_allow_access(num_agents, agents, flags, ptr,
+                                          &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_memory_copy, void* dst, const void* src,
+            size_t size) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  if (flags()->replace_intrin) {
+    if (dst != src) {
+      CHECK_RANGES_OVERLAP("hsa_memory_copy", dst, size, src, size);
+    }
+    ASAN_READ_RANGE(nullptr, src, size);
+    ASAN_WRITE_RANGE(nullptr, dst, size);
+  }
+  return REAL(hsa_memory_copy)(dst, src, size);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy, void* dst,
+            hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
+            size_t size, uint32_t num_dep_signals,
+            const hsa_signal_t* dep_signals, hsa_signal_t completion_signal) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  if (flags()->replace_intrin) {
+    if (dst != src) {
+      CHECK_RANGES_OVERLAP("hsa_amd_memory_async_copy", dst, size, src, size);
+    }
+    ASAN_READ_RANGE(nullptr, src, size);
+    ASAN_WRITE_RANGE(nullptr, dst, size);
+  }
+  return REAL(hsa_amd_memory_async_copy)(dst, dst_agent, src, src_agent, size,
+                                         num_dep_signals, dep_signals,
+                                         completion_signal);
+}
+
+#    if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void* dst,
+            hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
+            size_t size, uint32_t num_dep_signals,
+            const hsa_signal_t* dep_signals, hsa_signal_t completion_signal,
+            hsa_amd_sdma_engine_id_t engine_id, bool force_copy_on_sdma) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  if (flags()->replace_intrin) {
+    if (dst != src) {
+      CHECK_RANGES_OVERLAP("hsa_amd_memory_async_copy_on_engine", dst, size,
+                           src, size);
+    }
+    ASAN_READ_RANGE(nullptr, src, size);
+    ASAN_WRITE_RANGE(nullptr, dst, size);
+  }
+  return REAL(hsa_amd_memory_async_copy_on_engine)(
+      dst, dst_agent, src, src_agent, size, num_dep_signals, dep_signals,
+      completion_signal, engine_id, force_copy_on_sdma);
+}
+#    endif
+
+INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
+            hsa_amd_ipc_memory_t* handle) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_ipc_memory_create(ptr, len, handle);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_attach,
+            const hsa_amd_ipc_memory_t* handle, size_t len, uint32_t num_agents,
+            const hsa_agent_t* mapping_agents, void** mapped_ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_ipc_memory_attach(handle, len, num_agents, mapping_agents,
+                                        mapped_ptr);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_detach, void* mapped_ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_ipc_memory_detach(mapped_ptr);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_vmem_address_reserve_align, void** ptr,
+            size_t size, uint64_t address, uint64_t alignment, uint64_t flags) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_MALLOC;
+  return asan_hsa_amd_vmem_address_reserve_align(ptr, size, address, alignment,
+                                                 flags, &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_vmem_address_free, void* ptr, size_t size) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_FREE;
+  return asan_hsa_amd_vmem_address_free(ptr, size, &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
+            hsa_amd_pointer_info_t* info, void* (*alloc)(size_t),
+            uint32_t* num_agents_accessible, hsa_agent_t** accessible) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_pointer_info(ptr, info, alloc, num_agents_accessible,
+                                   accessible);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_init) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_init();
+}
+
+void InitializeAmdgpuInterceptors() {
+  ASAN_INTERCEPT_FUNC(hsa_init);
+  ASAN_INTERCEPT_FUNC(hsa_memory_copy);
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_allocate);
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_free);
+  ASAN_INTERCEPT_FUNC(hsa_amd_agents_allow_access);
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy);
+#    if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy_on_engine);
+#    endif
+  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_create);
+  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_attach);
+  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_detach);
+  ASAN_INTERCEPT_FUNC(hsa_amd_vmem_address_reserve_align);
+  ASAN_INTERCEPT_FUNC(hsa_amd_vmem_address_free);
+  ASAN_INTERCEPT_FUNC(hsa_amd_pointer_info);
+}
+
+void ENSURE_HSA_INITED() {
+  if (!REAL(hsa_init))
+    InitializeAmdgpuInterceptors();
+}
+#  endif
+
 // ---------------------- InitializeAsanInterceptors ---------------- {{{1
 namespace __asan {
 void InitializeAsanInterceptors() {
@@ -1011,6 +1171,10 @@ void InitializeAsanInterceptors() {
   ASAN_INTERCEPT_FUNC(vfork);
 #  endif
 
+#  if SANITIZER_AMDGPU
+  InitializeAmdgpuInterceptors();
+#  endif
+
   VReport(1, "AddressSanitizer: libc interceptors initialized\n");
 }
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
index 3d85959ac94a6..6b5ffd0af9595 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
@@ -315,7 +315,7 @@
 #  define SANITIZER_ALPHA 0
 #endif
 
-#if defined(__AMDGPU__)
+#if SANITIZER_AMDGPU || defined(__AMDGPU__)
 #  define SANITIZER_AMDGPU 1
 #else
 #  define SANITIZER_AMDGPU 0

>From f7b428e2ae30126cc1c795b9c7dd62ab3e4c9c19 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 22 Apr 2026 15:10:01 +0530
Subject: [PATCH 02/46] [compiler-rt][HSA] DeviceAllocator route through
 CombinedAllocator and HSA Allocator wrappers.

For SANITIZER_AMDGPU, the sanitizer `CombinedAllocator` gains an
optional `DeviceAllocator` (DeviceAllocatorT): init can turn it on,
Allocate can take a DeviceAllocationInfo* and send those requests to the
device path, and free / pointer queries / stats / locks also consider
device-owned chunks.

`asan_allocator` passes `da_info` into the underlying combined
allocator, enables the device side at InitLinkerInitialized on AMDGPU
builds, and adds HSA-related wrappers (hsa_init, pool alloc/free, agents
allow access, IPC, vmem, pointer_info) plus declarations/includes in
asan_allocator.h.

sanitizer_allocator.h pulls in HSA headers (when AMDGPU) and
sanitizer_allocator_device.h before the combined allocator.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 260 +++++++++++++++++-
 compiler-rt/lib/asan/asan_allocator.h         |  50 ++++
 .../sanitizer_common/sanitizer_allocator.h    |  27 +-
 .../sanitizer_allocator_combined.h            |  96 ++++++-
 4 files changed, 412 insertions(+), 21 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 6af197ab4cb1c..7477f9e52e533 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -393,7 +393,14 @@ struct Allocator {
 
   void InitLinkerInitialized(const AllocatorOptions& options) {
     SetAllocatorMayReturnNull(options.may_return_null);
+#if SANITIZER_AMDGPU
+    // Device-backed HSA allocations (e.g. hsa_amd_memory_pool_allocate) use
+    // CombinedAllocator's device path; it must be enabled for InitMemFuncs/
+    // AmdgpuMemFuncs::Init to run at startup.
+    allocator.InitLinkerInitialized(options.release_to_os_interval_ms, 0, true);
+#else
     allocator.InitLinkerInitialized(options.release_to_os_interval_ms);
+#endif
     SharedInitCode(options);
     max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
                                        ? common_flags()->max_allocation_size_mb
@@ -540,7 +547,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,DeviceAllocationInfo *da_info = nullptr) {
     if (UNLIKELY(!AsanInited()))
       AsanInitFromRtl();
     if (UNLIKELY(IsRssLimitExceeded())) {
@@ -595,11 +602,11 @@ struct Allocator {
     void* allocated;
     if (t) {
       AllocatorCache* cache = GetAllocatorCache(&t->malloc_storage());
-      allocated = allocator.Allocate(cache, needed_size, 8);
+      allocated = allocator.Allocate(cache, needed_size, 8, da_info);
     } else {
       SpinMutexLock l(&fallback_mutex);
-      AllocatorCache* cache = &fallback_allocator_cache;
-      allocated = allocator.Allocate(cache, needed_size, 8);
+      AllocatorCache *cache = &fallback_allocator_cache;
+      allocated = allocator.Allocate(cache, needed_size, 8, da_info);
     }
     if (UNLIKELY(!allocated)) {
       SetAllocatorOutOfMemory();
@@ -1475,3 +1482,248 @@ int __asan_update_allocation_context(void* addr) {
   GET_STACK_TRACE_MALLOC;
   return instance.UpdateAllocationStack((uptr)addr, &stack);
 }
+
+#if SANITIZER_AMDGPU
+
+DECLARE_REAL(hsa_status_t, hsa_init);
+DECLARE_REAL(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
+             const hsa_agent_t* agents, const uint32_t* flags, const void* ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_memory_pool_allocate,
+             hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
+             void** ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_memory_pool_free, void* ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
+             hsa_amd_ipc_memory_t* handle)
+DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_attach,
+             const hsa_amd_ipc_memory_t* handle, size_t len,
+             uint32_t num_agents, const hsa_agent_t* mapping_agents,
+             void** mapped_ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_detach, void* mapped_ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_vmem_address_reserve_align, void** ptr,
+             size_t size, uint64_t address, uint64_t alignment, uint64_t flags)
+DECLARE_REAL(hsa_status_t, hsa_amd_vmem_address_free, void* ptr, size_t size)
+DECLARE_REAL(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
+             hsa_amd_pointer_info_t* info, void* (*alloc)(size_t),
+             uint32_t* num_agents_accessible, hsa_agent_t** accessible)
+DECLARE_REAL(hsa_status_t, hsa_amd_register_system_event_handler,
+             hsa_amd_system_event_callback_t, void*)
+
+namespace __asan {
+// Always align to page boundary to match current ROCr behavior
+static const size_t kPageSize_ = 4096;
+
+hsa_status_t asan_hsa_amd_memory_pool_allocate(
+    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
+    BufferedStackTrace* stack) {
+  AmdgpuAllocationInfo aa_info;
+  aa_info.alloc_func =
+      reinterpret_cast<void*>(asan_hsa_amd_memory_pool_allocate);
+  aa_info.memory_pool = memory_pool;
+  aa_info.size = size;
+  aa_info.flags = flags;
+  aa_info.ptr = nullptr;
+  SetErrnoOnNull(*ptr = instance.Allocate(size, kPageSize_, stack, FROM_MALLOC,
+                                          false, &aa_info));
+  return aa_info.status;
+}
+
+hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
+                                           BufferedStackTrace* stack) {
+  void* p = get_allocator().GetBlockBegin(ptr);
+  if (p) {
+    instance.Deallocate(ptr, 0, 0, stack, FROM_MALLOC);
+    return HSA_STATUS_SUCCESS;
+  }
+  return REAL(hsa_amd_memory_pool_free)(ptr);
+}
+
+hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
+                                              const hsa_agent_t* agents,
+                                              const uint32_t* flags,
+                                              const void* ptr,
+                                              BufferedStackTrace* stack) {
+  void* p = get_allocator().GetBlockBegin(ptr);
+  return REAL(hsa_amd_agents_allow_access)(num_agents, agents, flags,
+                                           p ? p : ptr);
+}
+
+// For asan allocator, kMetadataSize is 0 and maximum redzone size is 2048. This
+// implies for device allocation, the gap between user_beg and GetBlockBegin()
+// is always one kPageSize_
+// IPC calls use static_assert to make sure kMetadataSize = 0
+//
+#  if SANITIZER_CAN_USE_ALLOCATOR64
+static struct AP64<LocalAddressSpaceView> AP_;
+#  else
+static struct AP32<LocalAddressSpaceView> AP_;
+#  endif
+
+hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
+                                            hsa_amd_ipc_memory_t* handle) {
+  void* ptr_ = get_allocator().GetBlockBegin(ptr);
+  AsanChunk* m = ptr_
+                     ? instance.GetAsanChunkByAddr(reinterpret_cast<uptr>(ptr_))
+                     : nullptr;
+  if (ptr_ && m) {
+    static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
+    uptr p = reinterpret_cast<uptr>(ptr);
+    uptr p_ = reinterpret_cast<uptr>(ptr_);
+    if (p == p_ + kPageSize_ && len == m->UsedSize()) {
+      size_t len_ = get_allocator().GetActuallyAllocatedSize(ptr_);
+      return REAL(hsa_amd_ipc_memory_create)(ptr_, len_, handle);
+    }
+  }
+  return REAL(hsa_amd_ipc_memory_create)(ptr, len, handle);
+}
+
+hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
+                                            size_t len, uint32_t num_agents,
+                                            const hsa_agent_t* mapping_agents,
+                                            void** mapped_ptr) {
+  static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
+  size_t len_ = len + kPageSize_;
+  hsa_status_t status = REAL(hsa_amd_ipc_memory_attach)(
+      handle, len_, num_agents, mapping_agents, mapped_ptr);
+  if (status == HSA_STATUS_SUCCESS && mapped_ptr) {
+    uptr mapped_base = reinterpret_cast<uptr>(*mapped_ptr);
+    uptr user_beg = mapped_base + kPageSize_;
+    uptr tail_beg = RoundUpTo(user_beg + len, ASAN_SHADOW_GRANULARITY);
+    uptr mapped_end = mapped_base + kPageSize_ + RoundUpTo(len, kPageSize_);
+
+    PoisonShadow(mapped_base, kPageSize_, kAsanHeapLeftRedzoneMagic);
+
+    if (mapped_end > tail_beg)
+      PoisonShadow(tail_beg, mapped_end - tail_beg, kAsanHeapLeftRedzoneMagic);
+
+    uptr size_rounded_down = RoundDownTo(len, ASAN_SHADOW_GRANULARITY);
+    if (size_rounded_down)
+      PoisonShadow(user_beg, size_rounded_down, 0);
+
+    if (len != size_rounded_down && CanPoisonMemory()) {
+      u8* shadow = (u8*)MemToShadow(user_beg + size_rounded_down);
+      *shadow = flags()->poison_partial
+                    ? static_cast<u8>(len & (ASAN_SHADOW_GRANULARITY - 1))
+                    : 0;
+    }
+
+    *mapped_ptr = reinterpret_cast<void*>(user_beg);
+  }
+  return status;
+}
+
+hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr) {
+  static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
+  uptr mapped_base = reinterpret_cast<uptr>(mapped_ptr) - kPageSize_;
+
+  hsa_amd_pointer_info_t info;
+  info.size = sizeof(hsa_amd_pointer_info_t);
+  if (REAL(hsa_amd_pointer_info)(reinterpret_cast<void*>(mapped_base), &info,
+                                 nullptr, nullptr,
+                                 nullptr) == HSA_STATUS_SUCCESS) {
+    PoisonShadow(mapped_base, info.sizeInBytes, 0);
+    FlushUnneededASanShadowMemory(mapped_base, info.sizeInBytes);
+  }
+
+  return REAL(hsa_amd_ipc_memory_detach)(reinterpret_cast<void*>(mapped_base));
+}
+
+hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
+    void** ptr, size_t size, uint64_t address, uint64_t alignment,
+    uint64_t flags, BufferedStackTrace* stack) {
+  // Bypass the tracking for a fixed address since it cannot be supported.
+  // Reasons:
+  //  1. Address may not meet the alignment/page-size requirement.
+  //  2. Requested range overlaps an existing reserved/mapped range.
+  //  3. Insufficient VA space to honor that exact placement.
+  if (address)
+    return REAL(hsa_amd_vmem_address_reserve_align)(ptr, size, address,
+                                                    alignment, flags);
+
+  if (alignment < kPageSize_)
+    alignment = kPageSize_;
+
+  if (UNLIKELY(!IsPowerOfTwo(alignment))) {
+    errno = errno_EINVAL;
+    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+  }
+
+  AmdgpuAllocationInfo aa_info;
+  aa_info.alloc_func =
+      reinterpret_cast<void*>(asan_hsa_amd_vmem_address_reserve_align);
+  aa_info.memory_pool = {0};
+  aa_info.size = size;
+  aa_info.flags64 = flags;
+  aa_info.address = 0;
+  aa_info.alignment = alignment;
+  aa_info.ptr = nullptr;
+  SetErrnoOnNull(*ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC,
+                                          false, &aa_info));
+
+  return aa_info.status;
+}
+
+hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
+                                            BufferedStackTrace* stack) {
+  if (UNLIKELY(!IsAligned(reinterpret_cast<uptr>(ptr), kPageSize_))) {
+    errno = errno_EINVAL;
+    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+  }
+  if (size == 0) {
+    errno = errno_EINVAL;
+    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+  }
+
+  void* p = get_allocator().GetBlockBegin(ptr);
+  if (p) {
+    instance.Deallocate(ptr, 0, 0, stack, FROM_MALLOC);
+    return HSA_STATUS_SUCCESS;
+  }
+  return REAL(hsa_amd_vmem_address_free)(ptr, size);
+}
+
+hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
+                                       hsa_amd_pointer_info_t* info,
+                                       void* (*alloc)(size_t),
+                                       uint32_t* num_agents_accessible,
+                                       hsa_agent_t** accessible) {
+  void* ptr_ = get_allocator().GetBlockBegin(ptr);
+  AsanChunk* m = ptr_
+                     ? instance.GetAsanChunkByAddr(reinterpret_cast<uptr>(ptr_))
+                     : nullptr;
+  if (ptr_ && m) {
+    hsa_status_t status = REAL(hsa_amd_pointer_info)(
+        ptr_, info, alloc, num_agents_accessible, accessible);
+    if (status == HSA_STATUS_SUCCESS && info) {
+      static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
+      // Adjust base address of agent,host and sizeInBytes so as to return
+      // the actual pointer information of user allocation rather than asan
+      // allocation. Asan allocation pointer info can be acquired using internal
+      // 'GetPointerInfo'
+      info->agentBaseAddress = reinterpret_cast<void*>(
+          reinterpret_cast<uptr>(info->agentBaseAddress) + kPageSize_);
+      info->hostBaseAddress = reinterpret_cast<void*>(
+          reinterpret_cast<uptr>(info->hostBaseAddress) + kPageSize_);
+      info->sizeInBytes = m->UsedSize();
+    }
+    return status;
+  }
+  return REAL(hsa_amd_pointer_info)(ptr, info, alloc, num_agents_accessible,
+                                    accessible);
+}
+
+hsa_status_t asan_hsa_init() {
+  hsa_status_t status = REAL(hsa_init)();
+  if (status == HSA_STATUS_SUCCESS) {
+    // Only clear state when recovering from a prior shutdown (avoids clearing
+    // amdgpu_event_registered on every refcount bump and re-registering).
+    if (__sanitizer::AmdgpuMemFuncs::IsAmdgpuRuntimeShutdown())
+      __sanitizer::AmdgpuMemFuncs::ClearAmdgpuRuntimeShutdownState();
+    // Load HSA entry points once the runtime is up; device allocator may stay
+    // disabled, but interceptors and RegisterSystemEventHandlers need them.
+    if (__sanitizer::AmdgpuMemFuncs::Init())
+      __sanitizer::AmdgpuMemFuncs::RegisterSystemEventHandlers();
+  }
+  return status;
+}
+#endif
+}  // namespace __asan
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index f3b215b7b05ee..4f38e7de6a15e 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -331,4 +331,54 @@ void PrintInternalAllocatorStats();
 void AsanSoftRssLimitExceededCallback(bool exceeded);
 
 }  // namespace __asan
+
+#if SANITIZER_AMDGPU
+
+#  if defined(__has_include)
+#    if __has_include("hsa.h")
+#      include "hsa.h"
+#      include "hsa_ext_amd.h"
+#    elif __has_include("hsa/hsa.h")
+#      include "hsa/hsa.h"
+#      include "hsa/hsa_ext_amd.h"
+#    endif
+#  else
+#    include "hsa/hsa.h"
+#    include "hsa/hsa_ext_amd.h"
+#  endif
+
+namespace __asan {
+hsa_status_t asan_hsa_amd_memory_pool_allocate(
+    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
+    BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
+                                           BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
+                                              const hsa_agent_t* agents,
+                                              const uint32_t* flags,
+                                              const void* ptr,
+                                              BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
+                                            hsa_amd_ipc_memory_t* handle);
+hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
+                                            size_t len, uint32_t num_agents,
+                                            const hsa_agent_t* mapping_agents,
+                                            void** mapped_ptr);
+hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr);
+hsa_status_t asan_hsa_amd_vmem_address_reserve_align(void** ptr, size_t size,
+                                                     uint64_t address,
+                                                     uint64_t alignment,
+                                                     uint64_t flags,
+                                                     BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
+                                            BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
+                                       hsa_amd_pointer_info_t* info,
+                                       void* (*alloc)(size_t),
+                                       uint32_t* num_agents_accessible,
+                                       hsa_agent_t** accessible);
+hsa_status_t asan_hsa_init();
+}  // namespace __asan
+#endif
+
 #endif  // ASAN_ALLOCATOR_H
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index 6154f7810334b..dfaa50eabab13 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -24,6 +24,22 @@
 #include "sanitizer_procmaps.h"
 #include "sanitizer_type_traits.h"
 
+#if SANITIZER_AMDGPU
+
+#  if defined(__has_include)
+#    if __has_include("hsa.h")
+#      include "hsa.h"
+#      include "hsa_ext_amd.h"
+#    elif __has_include("hsa/hsa.h")
+#      include "hsa/hsa.h"
+#      include "hsa/hsa_ext_amd.h"
+#    endif
+#  else
+#    include "hsa/hsa.h"
+#    include "hsa/hsa_ext_amd.h"
+#  endif
+#endif
+
 namespace __sanitizer {
 
 // Allows the tools to name their allocations appropriately.
@@ -63,13 +79,14 @@ struct NoOpMapUnmapCallback {
   void OnUnmap(uptr p, uptr size) const {}
 };
 
-#include "sanitizer_allocator_size_class_map.h"
-#include "sanitizer_allocator_stats.h"
-#include "sanitizer_allocator_primary64.h"
-#include "sanitizer_allocator_primary32.h"
+#include "sanitizer_allocator_combined.h"
+#include "sanitizer_allocator_device.h"
 #include "sanitizer_allocator_local_cache.h"
+#include "sanitizer_allocator_primary32.h"
+#include "sanitizer_allocator_primary64.h"
 #include "sanitizer_allocator_secondary.h"
-#include "sanitizer_allocator_combined.h"
+#include "sanitizer_allocator_size_class_map.h"
+#include "sanitizer_allocator_stats.h"
 
 bool IsRssLimitExceeded();
 void SetRssLimitExceeded(bool limit_exceeded);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
index 49940d9b5d505..b3ec606f61cb1 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -29,19 +29,32 @@ class CombinedAllocator {
                          LargeMmapAllocatorPtrArray,
                          typename PrimaryAllocator::AddressSpaceView>;
 
-  void InitLinkerInitialized(s32 release_to_os_interval_ms,
-                             uptr heap_start = 0) {
+#if SANITIZER_AMDGPU
+  using DeviceAllocator =
+      DeviceAllocatorT<typename PrimaryAllocator::MapUnmapCallback>;
+#endif
+
+  void InitLinkerInitialized(s32 release_to_os_interval_ms, uptr heap_start = 0,
+                             bool enable_device_allocator = false) {
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.InitLinkerInitialized();
+#if SANITIZER_AMDGPU
+    device_.Init(enable_device_allocator, primary_.kMetadataSize);
+#endif
   }
 
-  void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) {
+  void Init(s32 release_to_os_interval_ms, uptr heap_start = 0,
+            bool enable_device_allocator = false) {
     stats_.Init();
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.Init();
+#if SANITIZER_AMDGPU
+    device_.Init(enable_device_allocator, primary_.kMetadataSize);
+#endif
   }
 
-  void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
+  void* Allocate(AllocatorCache* cache, uptr size, uptr alignment,
+                 DeviceAllocationInfo* da_info = nullptr) {
     // Returning 0 on malloc(0) may break a lot of code.
     if (size == 0)
       size = 1;
@@ -65,7 +78,12 @@ class CombinedAllocator {
     // alignment without such requirement, and allocating 'size' would use
     // extraneous memory, so we employ 'original_size'.
     void *res;
-    if (primary_.CanAllocate(size, alignment))
+#if SANITIZER_AMDGPU
+    if (da_info)
+      res = device_.Allocate(&stats_, original_size, alignment, da_info);
+    else
+#endif
+        if (primary_.CanAllocate(size, alignment))
       res = cache->Allocate(&primary_, primary_.ClassID(size));
     else
       res = secondary_.Allocate(&stats_, original_size, alignment);
@@ -90,8 +108,12 @@ class CombinedAllocator {
     if (!p) return;
     if (primary_.PointerIsMine(p))
       cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
-    else
+    else if (secondary_.PointerIsMine(p))
       secondary_.Deallocate(&stats_, p);
+#if SANITIZER_AMDGPU
+    else if (device_.PointerIsMine(p))
+      device_.Deallocate(&stats_, p);
+#endif
   }
 
   void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
@@ -115,7 +137,13 @@ class CombinedAllocator {
   bool PointerIsMine(const void *p) const {
     if (primary_.PointerIsMine(p))
       return true;
-    return secondary_.PointerIsMine(p);
+    if (secondary_.PointerIsMine(p))
+      return true;
+#if SANITIZER_AMDGPU
+    if (device_.PointerIsMine(p))
+      return true;
+#endif
+    return false;
   }
 
   bool FromPrimary(const void *p) const { return primary_.PointerIsMine(p); }
@@ -123,31 +151,60 @@ class CombinedAllocator {
   void *GetMetaData(const void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetMetaData(p);
-    return secondary_.GetMetaData(p);
+    if (secondary_.PointerIsMine(p))
+      return secondary_.GetMetaData(p);
+#if SANITIZER_AMDGPU
+    if (device_.PointerIsMine(p))
+      return device_.GetMetaData(p);
+#endif
+    return nullptr;
   }
 
   void *GetBlockBegin(const void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);
-    return secondary_.GetBlockBegin(p);
+    if (secondary_.PointerIsMine(p))
+      return secondary_.GetBlockBegin(p);
+#if SANITIZER_AMDGPU
+    if (device_.PointerIsMine(p))
+      return device_.GetBlockBegin(p);
+#endif
+    return nullptr;
   }
 
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
   void *GetBlockBeginFastLocked(const void *p) {
+    void* beg;
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);
-    return secondary_.GetBlockBeginFastLocked(p);
+    if ((beg = secondary_.GetBlockBeginFastLocked(p)))
+      return beg;
+#if SANITIZER_AMDGPU
+    if ((beg = device_.GetBlockBeginFastLocked(p)))
+      return beg;
+#endif
+    return nullptr;
   }
 
   uptr GetActuallyAllocatedSize(void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetActuallyAllocatedSize(p);
-    return secondary_.GetActuallyAllocatedSize(p);
+    if (secondary_.PointerIsMine(p))
+      return secondary_.GetActuallyAllocatedSize(p);
+#if SANITIZER_AMDGPU
+    if (device_.PointerIsMine(p))
+      return device_.GetActuallyAllocatedSize(p);
+#endif
+    return 0;
   }
 
   uptr TotalMemoryUsed() {
-    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
+    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed()
+#if SANITIZER_AMDGPU
+           + device_.TotalMemoryUsed()
+#endif
+        ;
   }
 
   void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
@@ -171,11 +228,17 @@ class CombinedAllocator {
   void PrintStats() {
     primary_.PrintStats();
     secondary_.PrintStats();
+#if SANITIZER_AMDGPU
+    device_.PrintStats();
+#endif
   }
 
   // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
   // introspection API.
   void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+#if SANITIZER_AMDGPU
+    device_.ForceLock();
+#endif
     primary_.ForceLock();
     secondary_.ForceLock();
   }
@@ -183,6 +246,9 @@ class CombinedAllocator {
   void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
     secondary_.ForceUnlock();
     primary_.ForceUnlock();
+#if SANITIZER_AMDGPU
+    device_.ForceUnlock();
+#endif
   }
 
   // Iterate over all existing chunks.
@@ -190,10 +256,16 @@ class CombinedAllocator {
   void ForEachChunk(ForEachChunkCallback callback, void *arg) {
     primary_.ForEachChunk(callback, arg);
     secondary_.ForEachChunk(callback, arg);
+#if SANITIZER_AMDGPU
+    device_.ForEachChunk(callback, arg);
+#endif
   }
 
  private:
   PrimaryAllocator primary_;
   SecondaryAllocator secondary_;
+#if SANITIZER_AMDGPU
+  DeviceAllocator device_;
+#endif
   AllocatorGlobalStats stats_;
 };

>From 288bbd6f33e39d6466c09c84bd3224b2177672b6 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 22 Apr 2026 15:20:46 +0530
Subject: [PATCH 03/46] [compiler-rt][sanitizer_common] AMDGPU
 `DeviceAllocatorT` and AmdgpuMemFuncs(HSA dlsym, shutdown hook)

Under `SANITIZER_AMDGPU`, this adds `DeviceAllocatorT`
(sanitizer_allocator_device.h) to track HSA-backed slabs (allocate/free,
chunk lookup, stats, shutdown fallbacks) and AmdgpuMemFuncs
(sanitizer_allocator_amdgpu.{h,cpp}) to dlsym ROCr hsa_amd_* entry
points, perform pool or vmem allocations, free by pointer type, query
hsa_amd_pointer_info, and register a one-shot shutdown callback so the
allocator can stop using HSA after runtime teardown.
---
 .../lib/sanitizer_common/CMakeLists.txt       |   1 +
 .../sanitizer_allocator_amdgpu.cpp            | 209 +++++++++++
 .../sanitizer_allocator_amdgpu.h              |  48 +++
 .../sanitizer_allocator_device.h              | 354 ++++++++++++++++++
 4 files changed, 612 insertions(+)
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h

diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
index 96c23c6d8ab82..5e13b5b18cc56 100644
--- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
@@ -3,6 +3,7 @@
 
 set(SANITIZER_SOURCES_NOTERMINATION
   sanitizer_allocator.cpp
+  sanitizer_allocator_amdgpu.cpp
   sanitizer_common.cpp
   sanitizer_deadlock_detector1.cpp
   sanitizer_deadlock_detector2.cpp
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
new file mode 100644
index 0000000000000..3597db7c9d1bf
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -0,0 +1,209 @@
+//===-- sanitizer_allocator_amdgpu.cpp --------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Part of the Sanitizer Allocator.
+//
+//===----------------------------------------------------------------------===//
+#if SANITIZER_AMDGPU
+#  include <dlfcn.h>  // For dlsym
+
+#  include "sanitizer_allocator.h"
+#  include "sanitizer_atomic.h"
+
+namespace __sanitizer {
+struct HsaFunctions {
+  // -------------- Memory Allocate/Deallocate Functions ----------------
+  hsa_status_t (*memory_pool_allocate)(hsa_amd_memory_pool_t memory_pool,
+                                       size_t size, uint32_t flags, void** ptr);
+  hsa_status_t (*memory_pool_free)(void* ptr);
+  hsa_status_t (*pointer_info)(void* ptr, hsa_amd_pointer_info_t* info,
+                               void* (*alloc)(size_t),
+                               uint32_t* num_agents_accessible,
+                               hsa_agent_t** accessible);
+  hsa_status_t (*vmem_address_reserve_align)(void** ptr, size_t size,
+                                             uint64_t address,
+                                             uint64_t alignment,
+                                             uint64_t flags);
+  hsa_status_t (*vmem_address_free)(void* ptr, size_t size);
+
+  // ----------------- System Event Register Function -------------------
+  hsa_status_t (*register_system_event_handler)(
+      hsa_amd_system_event_callback_t callback, void* data);
+};
+
+static HsaFunctions hsa_amd;
+
+// Always align to page boundary to match current ROCr behavior
+static const size_t kPageSize_ = 4096;
+
+static atomic_uint8_t amdgpu_runtime_shutdown{0};
+static atomic_uint8_t amdgpu_event_registered{0};
+
+#  define LOAD_HSA_FUNC_WITH_ERROR_CHECK(func, name, success)         \
+    func = (decltype(func))dlsym(RTLD_NEXT, name);                    \
+    if (!func) {                                                      \
+      VReport(2, "Amdgpu Init: Failed to load " #name " function\n"); \
+      success = false;                                                \
+    }
+
+// Check AMDGPU runtime shutdown state
+bool AmdgpuMemFuncs::IsAmdgpuRuntimeShutdown() {
+  return static_cast<bool>(
+      atomic_load(&amdgpu_runtime_shutdown, memory_order_acquire));
+}
+
+// Notify AMDGPU runtime shutdown to allocator
+void AmdgpuMemFuncs::NotifyAmdgpuRuntimeShutdown() {
+  uint8_t shutdown = 0;
+  if (atomic_compare_exchange_strong(&amdgpu_runtime_shutdown, &shutdown, 1,
+                                     memory_order_acq_rel)) {
+    VReport(2, "Amdgpu Allocator: AMDGPU runtime shutdown detected\n");
+  }
+}
+
+// Clear shutdown state when hsa_init() succeeds again (re-init after shutdown).
+// Resets amdgpu_runtime_shutdown so allocator operations are enabled, and
+// amdgpu_event_registered so RegisterSystemEventHandlers() will register the
+// shutdown callback for the new runtime instance.
+void AmdgpuMemFuncs::ClearAmdgpuRuntimeShutdownState() {
+  atomic_store(&amdgpu_runtime_shutdown, 0, memory_order_release);
+  atomic_store(&amdgpu_event_registered, 0, memory_order_release);
+}
+
+bool AmdgpuMemFuncs::Init() {
+  bool success = true;
+  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.memory_pool_allocate,
+                                 "hsa_amd_memory_pool_allocate", success);
+  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.memory_pool_free,
+                                 "hsa_amd_memory_pool_free", success);
+  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.pointer_info, "hsa_amd_pointer_info",
+                                 success);
+  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.vmem_address_reserve_align,
+                                 "hsa_amd_vmem_address_reserve_align", success);
+  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.vmem_address_free,
+                                 "hsa_amd_vmem_address_free", success);
+  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.register_system_event_handler,
+                                 "hsa_amd_register_system_event_handler",
+                                 success);
+  if (!success) {
+    VReport(1, "Amdgpu Init: Failed to load AMDGPU runtime functions\n");
+    return false;
+  }
+  return true;
+}
+
+void* AmdgpuMemFuncs::Allocate(uptr size, uptr alignment,
+                               DeviceAllocationInfo* da_info) {
+  // Do not allocate if AMDGPU runtime is shutdown
+  if (UNLIKELY(IsAmdgpuRuntimeShutdown())) {
+    VReport(1,
+            "Amdgpu Allocate: Runtime shutdown, skipping allocation for size "
+            "%zu alignment %zu\n",
+            size, alignment);
+    return nullptr;
+  }
+
+  AmdgpuAllocationInfo* aa_info =
+      reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
+  if (!aa_info->memory_pool.handle) {
+    aa_info->status = hsa_amd.vmem_address_reserve_align(
+        &aa_info->ptr, size, aa_info->address, aa_info->alignment,
+        aa_info->flags64);
+  } else {
+    aa_info->status = hsa_amd.memory_pool_allocate(
+        aa_info->memory_pool, size, aa_info->flags, &aa_info->ptr);
+  }
+  if (aa_info->status != HSA_STATUS_SUCCESS)
+    return nullptr;
+
+  return aa_info->ptr;
+}
+
+void AmdgpuMemFuncs::Deallocate(void* p) {
+  // Deallocate does nothing after AMDGPU runtime shutdown
+  if (UNLIKELY(IsAmdgpuRuntimeShutdown())) {
+    VReport(
+        1,
+        "Amdgpu Deallocate: Runtime shutdown, skipping deallocation for %p\n",
+        reinterpret_cast<void*>(p));
+    return;
+  }
+
+  DevicePointerInfo DevPtrInfo;
+  if (AmdgpuMemFuncs::GetPointerInfo(reinterpret_cast<uptr>(p), &DevPtrInfo)) {
+    if (DevPtrInfo.type == HSA_EXT_POINTER_TYPE_HSA) {
+      UNUSED hsa_status_t status = hsa_amd.memory_pool_free(p);
+    } else if (DevPtrInfo.type == HSA_EXT_POINTER_TYPE_RESERVED_ADDR) {
+      UNUSED hsa_status_t status =
+          hsa_amd.vmem_address_free(p, DevPtrInfo.map_size);
+    }
+  }
+}
+
+bool AmdgpuMemFuncs::GetPointerInfo(uptr ptr, DevicePointerInfo* ptr_info) {
+  // GetPointerInfo returns false after AMDGPU runtime shutdown
+  if (UNLIKELY(IsAmdgpuRuntimeShutdown())) {
+    VReport(1,
+            "Amdgpu GetPointerInfo: Runtime shutdown, skipping query for %p\n",
+            reinterpret_cast<void*>(ptr));
+    return false;
+  }
+
+  hsa_amd_pointer_info_t info;
+  info.size = sizeof(hsa_amd_pointer_info_t);
+  hsa_status_t status =
+      hsa_amd.pointer_info(reinterpret_cast<void*>(ptr), &info, 0, 0, 0);
+
+  if (status != HSA_STATUS_SUCCESS)
+    return false;
+
+  if (info.type == HSA_EXT_POINTER_TYPE_RESERVED_ADDR)
+    ptr_info->map_beg = reinterpret_cast<uptr>(info.hostBaseAddress);
+  else if (info.type == HSA_EXT_POINTER_TYPE_HSA)
+    ptr_info->map_beg = reinterpret_cast<uptr>(info.agentBaseAddress);
+  ptr_info->map_size = info.sizeInBytes;
+  ptr_info->type = reinterpret_cast<hsa_amd_pointer_type_t>(info.type);
+
+  return true;
+}
+// Register shutdown system event handler only once
+// TODO: Register multiple event handlers if needed in future
+void AmdgpuMemFuncs::RegisterSystemEventHandlers() {
+  uint8_t registered = 0;
+  // Check if shutdown event handler is already registered
+  if (atomic_compare_exchange_strong(&amdgpu_event_registered, &registered, 1,
+                                     memory_order_acq_rel)) {
+    // Callback to detect and notify AMDGPU runtime shutdown
+    hsa_amd_system_event_callback_t callback = [](const hsa_amd_event_t* event,
+                                                  void* data) {
+      if (!event)
+        return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+      if (event->event_type == HSA_AMD_SYSTEM_SHUTDOWN_EVENT)
+        AmdgpuMemFuncs::NotifyAmdgpuRuntimeShutdown();
+      return HSA_STATUS_SUCCESS;
+    };
+    // Register the event callback
+    hsa_status_t status =
+        hsa_amd.register_system_event_handler(callback, nullptr);
+    // Check as registered if successful
+    if (status == HSA_STATUS_SUCCESS)
+      VReport(
+          1,
+          "Amdgpu RegisterSystemEventHandlers: Registered shutdown event \n");
+    else {
+      VReport(1,
+              "Amdgpu RegisterSystemEventHandlers: Failed to register shutdown "
+              "event \n");
+      atomic_store(&amdgpu_event_registered, 0, memory_order_release);
+    }
+  }
+}
+
+uptr AmdgpuMemFuncs::GetPageSize() { return kPageSize_; }
+}  // namespace __sanitizer
+#endif  // SANITIZER_AMDGPU
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
new file mode 100644
index 0000000000000..ff77911efb546
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -0,0 +1,48 @@
+//===-- sanitizer_allocator_amdgpu.h ----------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Part of the Sanitizer Allocator.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_ALLOCATOR_H
+#  error This file must be included inside sanitizer_allocator_device.h
+#endif
+
+#if SANITIZER_AMDGPU
+class AmdgpuMemFuncs {
+ public:
+  static bool Init();
+  static void* Allocate(uptr size, uptr alignment,
+                        DeviceAllocationInfo* da_info);
+  static void Deallocate(void* p);
+  static bool GetPointerInfo(uptr ptr, DevicePointerInfo* ptr_info);
+  static uptr GetPageSize();
+  static void RegisterSystemEventHandlers();
+  static bool IsAmdgpuRuntimeShutdown();
+  static void ClearAmdgpuRuntimeShutdownState();
+
+ private:
+  static void NotifyAmdgpuRuntimeShutdown();
+};
+
+struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
+  AmdgpuAllocationInfo() : DeviceAllocationInfo(DAT_AMDGPU) {
+    status = HSA_STATUS_SUCCESS;
+    alloc_func = nullptr;
+  }
+  hsa_status_t status;
+  void* alloc_func;
+  hsa_amd_memory_pool_t memory_pool;
+  u64 alignment;
+  u64 address;
+  u64 flags64;
+  usize size;
+  u32 flags;
+  void* ptr;
+};
+#endif  // SANITIZER_AMDGPU
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
new file mode 100644
index 0000000000000..c6a79760e902a
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -0,0 +1,354 @@
+//===-- sanitizer_allocator_device.h ----------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Part of the Sanitizer Allocator.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_ALLOCATOR_H
+#  error This file must be included inside sanitizer_allocator.h
+#endif
+
+struct DeviceAllocationInfo;
+#if SANITIZER_AMDGPU
+// Device memory allocation usually requires additional information, we can put
+// all the additional information into a data structure DeviceAllocationInfo.
+// This is only a parent structure since different vendors may require
+// different allocation info.
+typedef enum {
+  DAT_UNKNOWN = 0,
+  DAT_AMDGPU = 1,
+} DeviceAllocationType;
+
+struct DeviceAllocationInfo {
+  DeviceAllocationInfo(DeviceAllocationType type = DAT_UNKNOWN) {
+    type_ = type;
+  }
+  DeviceAllocationType type_;
+};
+
+struct DevicePointerInfo {
+  u64 type;
+  uptr map_beg;
+  uptr map_size;
+};
+
+#  include "sanitizer_allocator_amdgpu.h"
+
+template <class MapUnmapCallback = NoOpMapUnmapCallback>
+class DeviceAllocatorT {
+ public:
+  using PtrArrayT = DefaultLargeMmapAllocatorPtrArray;
+  using DeviceMemFuncs = AmdgpuMemFuncs;
+
+  void Init(bool enable, uptr kMetadataSize) {
+    internal_memset(this, 0, sizeof(*this));
+    enabled_ = enable;
+    if (!enable)
+      return;
+    kMetadataSize_ = kMetadataSize;
+    chunks_ = reinterpret_cast<uptr*>(ptr_array_.Init());
+    InitMemFuncs();
+  }
+
+  void* Allocate(AllocatorStats* stat, uptr size, uptr alignment,
+                 DeviceAllocationInfo* da_info) {
+    if (!da_info || !InitMemFuncs())
+      return nullptr;
+
+    // Allocate an extra page for Metadata
+    if (kMetadataSize_ + (size % page_size_) > page_size_) {
+      size += page_size_;
+    }
+    CHECK(IsPowerOfTwo(alignment));
+    uptr map_size = RoundUpMapSize(size);
+    if (alignment > page_size_)
+      map_size += alignment;
+    // Overflow.
+    if (map_size < size) {
+      Report(
+          "WARNING: %s: DeviceAllocator allocation overflow: "
+          "0x%zx bytes with 0x%zx alignment requested\n",
+          SanitizerToolName, map_size, alignment);
+      return nullptr;
+    }
+    void* ptr = DeviceMemFuncs::Allocate(map_size, alignment, da_info);
+    if (!ptr)
+      return nullptr;
+    uptr map_beg = reinterpret_cast<uptr>(ptr);
+    CHECK(IsAligned(map_beg, page_size_));
+    MapUnmapCallback().OnMap(map_beg, map_size);
+    uptr map_end = map_beg + map_size;
+    uptr res = map_beg;
+    if (res & (alignment - 1))  // Align.
+      res += alignment - (res & (alignment - 1));
+    CHECK(IsAligned(res, alignment));
+    CHECK(IsAligned(res, page_size_));
+    CHECK_GE(res + size, map_beg);
+    CHECK_LE(res + size, map_end);
+    uptr size_log = MostSignificantSetBitIndex(map_size);
+    CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log));
+    {
+      SpinMutexLock l(&mutex_);
+      ptr_array_.EnsureSpace(n_chunks_);
+      uptr idx = n_chunks_++;
+      chunks_[idx] = map_beg;
+      chunks_sorted_ = false;
+      stats.n_allocs++;
+      stats.currently_allocated += map_size;
+      stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated);
+      stats.by_size_log[size_log]++;
+      stat->Add(AllocatorStatAllocated, map_size);
+      stat->Add(AllocatorStatMapped, map_size);
+    }
+    return reinterpret_cast<void*>(res);
+  }
+
+  void Deallocate(AllocatorStats* stat, void* p) {
+    Header header, *h;
+    {
+      SpinMutexLock l(&mutex_);
+      uptr idx;
+      uptr p_ = reinterpret_cast<uptr>(p);
+      EnsureSortedChunks();  // Avoid doing the sort while iterating.
+      for (idx = 0; idx < n_chunks_; idx++) {
+        if (chunks_[idx] >= p_)
+          break;
+      }
+      CHECK_EQ(chunks_[idx], p_);
+      CHECK_LT(idx, n_chunks_);
+      h = GetHeader(chunks_[idx], &header);
+      chunks_[idx] = chunks_[--n_chunks_];
+      chunks_sorted_ = false;
+      stats.n_frees++;
+      stats.currently_allocated -= h->map_size;
+      stat->Sub(AllocatorStatAllocated, h->map_size);
+      stat->Sub(AllocatorStatMapped, h->map_size);
+    }
+    MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
+    DeviceMemFuncs::Deallocate(p);
+  }
+
+  uptr TotalMemoryUsed() {
+    Header header;
+    SpinMutexLock l(&mutex_);
+    uptr res = 0;
+    for (uptr i = 0; i < n_chunks_; i++) {
+      Header* h = GetHeader(chunks_[i], &header);
+      res += RoundUpMapSize(h->map_size);
+    }
+    return res;
+  }
+
+  bool PointerIsMine(const void* p) const {
+    return GetBlockBegin(p) != nullptr;
+  }
+
+  uptr GetActuallyAllocatedSize(void* p) {
+    Header header;
+    uptr p_ = reinterpret_cast<uptr>(p);
+    Header* h = GetHeaderAnyPointer(p_, &header);
+    return h ? h->map_size : 0;
+  }
+
+  void* GetMetaData(const void* p) {
+    Header header;
+    uptr p_ = reinterpret_cast<uptr>(p);
+    Header* h = GetHeaderAnyPointer(p_, &header);
+    return h ? reinterpret_cast<void*>(h->map_beg + h->map_size -
+                                       kMetadataSize_)
+             : nullptr;
+  }
+
+  void* GetBlockBegin(const void* ptr) const {
+    Header header;
+    if (!mem_funcs_inited_)
+      return nullptr;
+    uptr p = reinterpret_cast<uptr>(ptr);
+    SpinMutexLock l(&mutex_);
+    uptr nearest_chunk = 0;
+    // Cache-friendly linear search.
+    for (uptr i = 0; i < n_chunks_; i++) {
+      uptr ch = chunks_[i];
+      if (p < ch)
+        continue;  // p is at left to this chunk, skip it.
+      if (p - ch < p - nearest_chunk)
+        nearest_chunk = ch;
+    }
+    if (!nearest_chunk)
+      return nullptr;
+    if (p != nearest_chunk) {
+      Header* h = GetHeader(nearest_chunk, &header);
+      CHECK_GE(nearest_chunk, h->map_beg);
+      CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
+      CHECK_LE(nearest_chunk, p);
+      if (h->map_beg + h->map_size <= p) {
+        return nullptr;
+      }
+    }
+    return GetUser(nearest_chunk);
+  }
+
+  void EnsureSortedChunks() {
+    if (chunks_sorted_)
+      return;
+    Sort(reinterpret_cast<uptr*>(chunks_), n_chunks_);
+    chunks_sorted_ = true;
+  }
+
+  // This function does the same as GetBlockBegin, but is much faster.
+  // Must be called with the allocator locked.
+  void* GetBlockBeginFastLocked(const void* ptr) {
+    if (!mem_funcs_inited_)
+      return nullptr;
+    mutex_.CheckLocked();
+    uptr p = reinterpret_cast<uptr>(ptr);
+    uptr n = n_chunks_;
+    if (!n)
+      return nullptr;
+    EnsureSortedChunks();
+    Header header, *h;
+    h = GetHeader(chunks_[n - 1], &header);
+    uptr min_mmap_ = chunks_[0];
+    uptr max_mmap_ = chunks_[n - 1] + h->map_size;
+    if (p < min_mmap_)
+      return nullptr;
+    if (p >= max_mmap_) {
+      // TODO (bingma): If dev_runtime_unloaded_ = true, map_size is limited
+      // to one page and we might miss a valid 'ptr'. If we hit cases where
+      // this kind of miss is unacceptable, we will need to implement a full
+      // solution with higher cost
+      return nullptr;
+    }
+    uptr beg = 0, end = n - 1;
+    // This loop is a log(n) lower_bound. It does not check for the exact match
+    // to avoid expensive cache-thrashing loads.
+    while (end - beg >= 2) {
+      uptr mid = (beg + end) / 2;  // Invariant: mid >= beg + 1
+      if (p < chunks_[mid])
+        end = mid - 1;  // We are not interested in chunks[mid].
+      else
+        beg = mid;  // chunks[mid] may still be what we want.
+    }
+
+    if (beg < end) {
+      CHECK_EQ(beg + 1, end);
+      // There are 2 chunks left, choose one.
+      if (p >= chunks_[end])
+        beg = end;
+    }
+
+    if (p != chunks_[beg]) {
+      h = GetHeader(chunks_[beg], &header);
+      CHECK_NE(h, nullptr);
+      if (p < h->map_beg)
+        return nullptr;
+      if (h->map_beg + h->map_size <= p) {
+        // TODO (bingma): See above TODO in this function
+        return nullptr;
+      }
+    }
+    return GetUser(chunks_[beg]);
+  }
+
+  void PrintStats() {
+    Printf(
+        "Stats: DeviceAllocator: allocated %zd times, "
+        "remains %zd (%zd K) max %zd M; by size logs: ",
+        stats.n_allocs, stats.n_allocs - stats.n_frees,
+        stats.currently_allocated >> 10, stats.max_allocated >> 20);
+    for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) {
+      uptr c = stats.by_size_log[i];
+      if (!c)
+        continue;
+      Printf("%zd:%zd; ", i, c);
+    }
+    Printf("\n");
+  }
+
+  // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
+  // introspection API.
+  void ForceLock() SANITIZER_ACQUIRE(mutex_) { mutex_.Lock(); }
+
+  void ForceUnlock() SANITIZER_RELEASE(mutex_) { mutex_.Unlock(); }
+
+  // Iterate over all existing chunks.
+  // The allocator must be locked when calling this function.
+  void ForEachChunk(ForEachChunkCallback callback, void* arg) {
+    EnsureSortedChunks();  // Avoid doing the sort while iterating.
+    for (uptr i = 0; i < n_chunks_; i++) {
+      const uptr t = chunks_[i];
+      callback(t, arg);
+      // Consistency check: verify that the array did not change.
+      CHECK_EQ(chunks_[i], t);
+    }
+  }
+
+ private:
+  bool InitMemFuncs() {
+    if (!enabled_ || mem_funcs_inited_ || mem_funcs_init_count_ >= 2) {
+      return mem_funcs_inited_;
+    }
+    mem_funcs_inited_ = DeviceMemFuncs::Init();
+    mem_funcs_init_count_++;
+    if (mem_funcs_inited_)
+      page_size_ = DeviceMemFuncs::GetPageSize();
+    return mem_funcs_inited_;
+  }
+
+  typedef DevicePointerInfo Header;
+
+  Header* GetHeaderAnyPointer(uptr p, Header* h) const {
+    CHECK(IsAligned(p, page_size_));
+    return DeviceMemFuncs::GetPointerInfo(p, h) ? h : nullptr;
+  }
+
+  Header* GetHeader(uptr chunk, Header* h) const {
+    // Device allocator has dependency on device runtime. If device runtime
+    // is unloaded, GetPointerInfo() will fail. For such case, we can still
+    // return a valid value for map_beg, map_size will be limited to one page
+    if (LIKELY(!dev_runtime_unloaded_)) {
+      if (DeviceMemFuncs::GetPointerInfo(chunk, h))
+        return h;
+      // If GetPointerInfo() fails, we don't assume the runtime is unloaded yet.
+      // We just return a conservative single-page header. Here mark/check the
+      // runtime shutdown state
+      dev_runtime_unloaded_ = DeviceMemFuncs::IsAmdgpuRuntimeShutdown();
+    }
+    // If we reach here, device runtime is unloaded.
+    // Fallback: conservative single-page header
+    h->map_beg = chunk;
+    h->map_size = page_size_;
+    return h;
+  }
+
+  void* GetUser(const uptr ptr) const { return reinterpret_cast<void*>(ptr); }
+
+  uptr RoundUpMapSize(uptr size) {
+    return RoundUpTo(size, page_size_) + page_size_;
+  }
+
+  bool enabled_;
+  bool mem_funcs_inited_;
+  mutable bool dev_runtime_unloaded_;
+  // Maximum of mem_funcs_init_count_ is 2:
+  //   1. The initial init called from Init(...), it could fail if
+  //      libhsa-runtime64.so is dynamically loaded with dlopen()
+  //   2. A potential deferred init called by Allocate(...)
+  u32 mem_funcs_init_count_;
+  uptr kMetadataSize_;
+  uptr page_size_;
+  uptr* chunks_;
+  PtrArrayT ptr_array_;
+  uptr n_chunks_;
+  bool chunks_sorted_;
+  struct Stats {
+    uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
+  } stats;
+  mutable StaticSpinMutex mutex_;
+};
+#endif  // SANITIZER_AMDGPU

>From e981433b2531b10e17e594c44cbc02d8d421743e Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 22 Apr 2026 15:33:28 +0530
Subject: [PATCH 04/46] [compiler-rt][HSA-Test] Add AMDGPU ASan lit feature and
 ROCm/HSA regression tests.

Wire the compiler-rt test suite for optional SANITIZER_AMDGPU builds and
add AddressSanitizer AMDGPU integration tests that link against ROCm's
libhsa-runtime64.

Lit/CMake
- pythonize_bool(SANITIZER_AMDGPU) in compiler-rt/test/CMakeLists.txt
  and pass sanitizer_amdgpu into lit via lit.common.configured.in.
- When sanitizer_amdgpu is true, register the sanitizer-amdgpu lit
  feature so tests can REQUIRES: sanitizer-amdgpu.

ASan AMDGPU tests (test/asan/TestCases/AMDGPU)
- lit.local.cfg.py: gate tests on Linux, dynamic ASan, ROCm discovery
  (ROCM_PATH or /opt/rocm), and define %rocm_include, %rocm_lib, and
  rpath substitutions for the shared ASan runtime and HSA DSO.
- hsa_memory_copy_overlap.cpp: overlapping hsa_memory_copy ranges
  trigger the ASan overlap diagnostic.
- hsa_amd_memory_pool_allocate_double_free.cpp: allocate from a
  runtime-alloc HSA memory pool then double hsa_amd_memory_pool_free;
  expect ASan double-free reporting.
---
 compiler-rt/test/CMakeLists.txt               |  2 +
 ...a_amd_memory_pool_allocate_double_free.cpp | 89 +++++++++++++++++++
 .../AMDGPU/hsa_memory_copy_overlap.cpp        | 39 ++++++++
 .../asan/TestCases/AMDGPU/lit.local.cfg.py    | 58 ++++++++++++
 compiler-rt/test/lit.common.cfg.py            |  3 +
 compiler-rt/test/lit.common.configured.in     |  1 +
 6 files changed, 192 insertions(+)
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py

diff --git a/compiler-rt/test/CMakeLists.txt b/compiler-rt/test/CMakeLists.txt
index 3fab82518e75f..0b28a532f8e22 100644
--- a/compiler-rt/test/CMakeLists.txt
+++ b/compiler-rt/test/CMakeLists.txt
@@ -16,6 +16,8 @@ pythonize_bool(COMPILER_RT_HAS_AARCH64_SME)
 
 pythonize_bool(COMPILER_RT_HAS_NO_DEFAULT_CONFIG_FLAG)
 
+pythonize_bool(SANITIZER_AMDGPU)
+
 if(LLVM_TREE_AVAILABLE OR NOT COMPILER_RT_STANDALONE_BUILD)
   set(COMPILER_RT_BUILT_WITH_LLVM TRUE)
 else()
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
new file mode 100644
index 0000000000000..76899716e2398
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -0,0 +1,89 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// Regression test for the AddressSanitizer hsa_amd_memory_pool_allocate /
+// hsa_amd_memory_pool_free interceptors: freeing the same pool allocation
+// twice is diagnosed (same family of checks as double-free on malloc).
+//
+// Link against ROCm's HSA runtime. Tests under TestCases/AMDGPU run only when
+// lit finds a ROCm install (see lit.local.cfg.py): $ROCM_PATH or /opt/rocm,
+// with include/hsa/hsa.h and libhsa-runtime64. Compiler-rt must be built with
+// SANITIZER_AMDGPU enabled. The suite uses the dynamic ASan runtime only.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+struct PoolSearch {
+  hsa_amd_memory_pool_t pool;
+  bool found;
+};
+
+static hsa_status_t find_alloc_pool(hsa_amd_memory_pool_t pool, void *data) {
+  auto *ps = static_cast<PoolSearch *>(data);
+  bool allow = false;
+  if (hsa_amd_memory_pool_get_info(
+          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
+          HSA_STATUS_SUCCESS ||
+      !allow)
+    return HSA_STATUS_SUCCESS;
+  ps->pool = pool;
+  ps->found = true;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+static hsa_status_t find_agent_with_pool(hsa_agent_t agent, void *data) {
+  (void)agent;
+  auto *ps = static_cast<PoolSearch *>(data);
+  ps->found = false;
+  hsa_status_t st =
+      hsa_amd_agent_iterate_memory_pools(agent, find_alloc_pool, ps);
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
+  return HSA_STATUS_SUCCESS;
+}
+
+int main() {
+  if (hsa_init() != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_init failed\n");
+    return 1;
+  }
+
+  PoolSearch ps = {};
+  ps.pool.handle = 0;
+  ps.found = false;
+
+  hsa_status_t it = hsa_iterate_agents(find_agent_with_pool, &ps);
+  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
+    fprintf(stderr, "hsa_iterate_agents failed\n");
+    return 1;
+  }
+  if (!ps.found) {
+    fprintf(stderr, "no runtime-alloc HSA memory pool found\n");
+    return 1;
+  }
+
+  void *mem = nullptr;
+  if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
+          HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
+    return 1;
+  }
+
+  (void)hsa_amd_memory_pool_free(mem);
+  (void)hsa_amd_memory_pool_free(mem);
+
+  fprintf(stderr, "expected double-free report\n");
+  return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer: attempting double-free
+// CHECK: SUMMARY: AddressSanitizer: double-free
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
new file mode 100644
index 0000000000000..7895899a87dd5
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
@@ -0,0 +1,39 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// Regression test for the AddressSanitizer hsa_memory_copy interceptor: invalid
+// overlapping ranges are diagnosed (same family of checks as memcpy).
+//
+// Link against ROCm's HSA runtime. Tests under TestCases/AMDGPU run only when
+// lit finds a ROCm install (see lit.local.cfg.py): $ROCM_PATH or /opt/rocm,
+// with include/hsa/hsa.h and libhsa-runtime64. Compiler-rt must be built with
+// SANITIZER_AMDGPU enabled.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include <hsa/hsa.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+  if (hsa_init() != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_init failed\n");
+    return 1;
+  }
+
+  char buf[128];
+  char *dst = buf;
+  char *src = buf + 40;
+  // Ranges [buf, buf+64) and [buf+40, buf+104) overlap; dst != src so the
+  // interceptor runs CHECK_RANGES_OVERLAP.
+  (void)hsa_memory_copy(dst, src, 64);
+  fprintf(stderr, "expected hsa_memory_copy overlap report\n");
+  return 0;
+}
+
+// CHECK: hsa_memory_copy-param-overlap: memory ranges
+// CHECK: [{{0x.*,[ ]*0x.*}}) and [{{0x.*,[ ]*0x.*}}) overlap
+// CHECK: SUMMARY: AddressSanitizer: hsa_memory_copy-param-overlap
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
new file mode 100644
index 0000000000000..b825c27eacf21
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
@@ -0,0 +1,58 @@
+import glob
+import os
+
+
+def getRoot(config):
+    if not config.parent:
+        return config
+    return getRoot(config.parent)
+
+
+def rocm_lib_dir(rocm_root):
+    """Return lib or lib64 under rocm_root that provides libhsa-runtime64."""
+    for libname in ("lib", "lib64"):
+        libdir = os.path.join(rocm_root, libname)
+        if not os.path.isdir(libdir):
+            continue
+        if glob.glob(os.path.join(libdir, "libhsa-runtime64.so*")):
+            return libdir
+    return None
+
+
+def rocm_is_available(rocm_root):
+    if not rocm_root or not os.path.isdir(rocm_root):
+        return False
+    hsa_h = os.path.join(rocm_root, "include", "hsa", "hsa.h")
+    if not os.path.isfile(hsa_h):
+        return False
+    return rocm_lib_dir(rocm_root) is not None
+
+
+root = getRoot(config)
+
+# AMDGPU ASan tests are only run with the dynamic ASan runtime (-shared-libasan).
+if "asan-static-runtime" in root.available_features:
+    config.unsupported = True
+elif root.target_os != "Linux":
+    config.unsupported = True
+else:
+    rocm_root = os.environ.get("ROCM_PATH", "/opt/rocm")
+    if not rocm_is_available(rocm_root):
+        config.unsupported = True
+    else:
+        # Dynamic ASan (-shared-libasan) adds libclang_rt.asan*.so as DT_NEEDED; embed
+        # the host compiler-rt lib dir in RUNPATH so the loader finds it (same path as
+        # LD_LIBRARY_PATH in lit.common.cfg.py, but explicit in the linked binary).
+        rt_libdir = getattr(root, "compiler_rt_libdir", None)
+        if not rt_libdir or not os.path.isdir(rt_libdir):
+            config.unsupported = True
+        elif not glob.glob(os.path.join(rt_libdir, "libclang_rt.asan*.so")):
+            config.unsupported = True
+        else:
+            config.available_features.add("rocm")
+            rocm_lib = rocm_lib_dir(rocm_root)
+            rocm_include = os.path.join(rocm_root, "include")
+            config.substitutions.append(("%rocm_root", rocm_root))
+            config.substitutions.append(("%rocm_include", rocm_include))
+            config.substitutions.append(("%rocm_lib", rocm_lib))
+            config.substitutions.append(("%compiler_rt_libdir", rt_libdir))
diff --git a/compiler-rt/test/lit.common.cfg.py b/compiler-rt/test/lit.common.cfg.py
index 12446b4708d20..4821178500f49 100644
--- a/compiler-rt/test/lit.common.cfg.py
+++ b/compiler-rt/test/lit.common.cfg.py
@@ -562,6 +562,9 @@ def get_ios_commands_dir():
 if config.gwp_asan:
     config.available_features.add("gwp_asan")
 
+if getattr(config, "sanitizer_amdgpu", False):
+    config.available_features.add("sanitizer-amdgpu")
+
 lit.util.usePlatformSdkOnDarwin(config, lit_config)
 
 min_macos_deployment_target_substitutions = [
diff --git a/compiler-rt/test/lit.common.configured.in b/compiler-rt/test/lit.common.configured.in
index cad956aedf94a..0dde837fa52df 100644
--- a/compiler-rt/test/lit.common.configured.in
+++ b/compiler-rt/test/lit.common.configured.in
@@ -26,6 +26,7 @@ set_default("clang", "@COMPILER_RT_RESOLVED_TEST_COMPILER@")
 set_default("compiler_id", "@COMPILER_RT_TEST_COMPILER_ID@")
 set_default("python_executable", "@Python3_EXECUTABLE@")
 set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@)
+set_default("sanitizer_amdgpu", @SANITIZER_AMDGPU_PYBOOL@)
 set_default("compiler_rt_intercept_libdispatch", @COMPILER_RT_INTERCEPT_LIBDISPATCH_PYBOOL@)
 set_default("compiler_rt_output_dir", "@COMPILER_RT_RESOLVED_OUTPUT_DIR@")
 set_default("compiler_rt_bindir", "@COMPILER_RT_RESOLVED_EXEC_OUTPUT_DIR@")

>From 7d0b6a4340f15d3e7e090b4ec45764da5e211ecf Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 22 Apr 2026 15:37:45 +0530
Subject: [PATCH 05/46] [compiler-rt][sanitizer_common] Skip nolibc unittest
 subprocess for SANITIZER_AMDGPU
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

 For SANITIZER_AMDGPU builds, the sanitizer_common -nostdlib / nolibc
 helper binary and NolibcMain subprocess test are turned off in CMake
 (they clash with AMDGPU’s libdl / libc needs and with compiling every
 unittest with HSA).

 A new compile flag COMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST gates the
 test in sanitizer_nolibc_test.cpp so normal configs are unchanged.
---
 .../lib/sanitizer_common/tests/CMakeLists.txt  | 18 +++++++++++++++---
 .../tests/sanitizer_nolibc_test.cpp            |  5 ++++-
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
index 55c7d665e639f..b8c58041d31a4 100644
--- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
@@ -80,6 +80,15 @@ set(SANITIZER_TEST_CFLAGS_COMMON
   -Wno-gnu-zero-variadic-macro-arguments
   )
 
+# Do not pass -DSANITIZER_AMDGPU=1 to all unittests: that would pull <hsa.h>
+# and AMDGPU-only types into every TU without ROCm include paths. When
+# SANITIZER_AMDGPU is enabled, skip the NolibcMain subprocess test instead
+# (see NOT SANITIZER_AMDGPU targets below and sanitizer_nolibc_test.cpp).
+if(SANITIZER_AMDGPU)
+  list(APPEND SANITIZER_TEST_CFLAGS_COMMON
+       -DCOMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST=1)
+endif()
+
 set(SANITIZER_TEST_LINK_FLAGS_COMMON
   ${COMPILER_RT_UNITTEST_LINK_FLAGS}
   ${COMPILER_RT_UNWINDER_LINK_LIBS}
@@ -183,10 +192,13 @@ macro(add_sanitizer_tests_for_arch arch)
     CFLAGS  ${SANITIZER_TEST_CFLAGS_COMMON} ${extra_flags}
     LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS_COMMON} ${TARGET_LINK_FLAGS} ${extra_flags})
 
-  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64")
+  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64"
+     AND NOT SANITIZER_AMDGPU)
     # Test that the libc-independent part of sanitizer_common is indeed
     # independent of libc, by linking this binary without libc (here) and
-    # executing it (unit test in sanitizer_nolibc_test.cpp).
+    # executing it (unit test in sanitizer_nolibc_test.cpp). Omitted when
+    # SANITIZER_AMDGPU: AMDGPU runtime code needs libdl / libcdep and cannot
+    # link with -nostdlib.
     get_target_flags_for_arch(${arch} TARGET_FLAGS)
     clang_compile(sanitizer_nolibc_test_main.${arch}.o
                   sanitizer_nolibc_test_main.cpp
@@ -212,7 +224,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
                              $<TARGET_OBJECTS:RTSanitizerCommonLibc.osx>
                              $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.osx>)
   else()
-    if(CAN_TARGET_x86_64)
+    if(CAN_TARGET_x86_64 AND NOT SANITIZER_AMDGPU)
       add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64"
                                $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
                                $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.x86_64>)
diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
index 41376ee094307..b17cc731aaa0a 100644
--- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
+++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
@@ -19,7 +19,10 @@
 
 extern const char *argv0;
 
-#if SANITIZER_LINUX && defined(__x86_64__)
+// When SANITIZER_AMDGPU is enabled, CMake defines this macro and does not build
+// Sanitizer-*-Test-Nolibc (see tests/CMakeLists.txt).
+#if SANITIZER_LINUX && defined(__x86_64__) && \
+    !defined(COMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST)
 TEST(SanitizerCommon, NolibcMain) {
   std::string NolibcTestPath = argv0;
   NolibcTestPath += "-Nolibc";

>From 8e61fe2a7b3c1f512cfcbc70d867bbbd77f5bfdc Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 22 Apr 2026 16:02:12 +0530
Subject: [PATCH 06/46] Fix the header inclusion order for device allocator.

---
 .../lib/sanitizer_common/sanitizer_allocator.h      | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index dfaa50eabab13..1b058fd155d04 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -25,7 +25,6 @@
 #include "sanitizer_type_traits.h"
 
 #if SANITIZER_AMDGPU
-
 #  if defined(__has_include)
 #    if __has_include("hsa.h")
 #      include "hsa.h"
@@ -79,14 +78,14 @@ struct NoOpMapUnmapCallback {
   void OnUnmap(uptr p, uptr size) const {}
 };
 
-#include "sanitizer_allocator_combined.h"
-#include "sanitizer_allocator_device.h"
-#include "sanitizer_allocator_local_cache.h"
-#include "sanitizer_allocator_primary32.h"
-#include "sanitizer_allocator_primary64.h"
-#include "sanitizer_allocator_secondary.h"
 #include "sanitizer_allocator_size_class_map.h"
 #include "sanitizer_allocator_stats.h"
+#include "sanitizer_allocator_primary64.h"
+#include "sanitizer_allocator_primary32.h"
+#include "sanitizer_allocator_local_cache.h"
+#include "sanitizer_allocator_secondary.h"
+#include "sanitizer_allocator_device.h"
+#include "sanitizer_allocator_combined.h"
 
 bool IsRssLimitExceeded();
 void SetRssLimitExceeded(bool limit_exceeded);

>From a19191fc22a29384143104805c95415477e020c3 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 22 Apr 2026 16:59:28 +0530
Subject: [PATCH 07/46] Fix clang-format issue

---
 compiler-rt/lib/sanitizer_common/sanitizer_allocator.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index 1b058fd155d04..d803444f69dd6 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -78,6 +78,9 @@ struct NoOpMapUnmapCallback {
   void OnUnmap(uptr p, uptr size) const {}
 };
 
+// clang-format off
+// Include order is load-bearing (MemoryMapper, AllocatorStats, secondary
+// typedefs, then DeviceAllocatorT, then CombinedAllocator). Do not sort.
 #include "sanitizer_allocator_size_class_map.h"
 #include "sanitizer_allocator_stats.h"
 #include "sanitizer_allocator_primary64.h"
@@ -86,6 +89,7 @@ struct NoOpMapUnmapCallback {
 #include "sanitizer_allocator_secondary.h"
 #include "sanitizer_allocator_device.h"
 #include "sanitizer_allocator_combined.h"
+// clang-format on
 
 bool IsRssLimitExceeded();
 void SetRssLimitExceeded(bool limit_exceeded);

>From 382ec2951f60845026776123e9e899fbf1574f3b Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 22 Apr 2026 17:05:40 +0530
Subject: [PATCH 08/46] Fix extra closing brace  error.

```
error: extraneous closing brace ('}')
1718 | }  // namespace __asan
| ^
1 error generated.
```
---
 compiler-rt/lib/asan/asan_allocator.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 7477f9e52e533..4acc456a244ca 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1725,5 +1725,6 @@ hsa_status_t asan_hsa_init() {
   }
   return status;
 }
-#endif
 }  // namespace __asan
+
+#endif  // SANITIZER_AMDGPU

>From bea3ea6381d005331811490f5ff81658ce06cd78 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Thu, 23 Apr 2026 15:17:13 +0530
Subject: [PATCH 09/46] [compiler-rt][HSA-Test] Skip AMDGPU ROCm lit tests on
 32-bit x86.

ROCm ships libhsa-runtime64 as a 64-bit DSO only, so i386 ASan runs fail
at link time (elf32-i386 vs 64-bit library).  Mark TestCases/AMDGPU as
unsupported when the suite targets 32-bit x86.
---
 ...a_amd_memory_pool_allocate_double_free.cpp |  5 ----
 .../AMDGPU/hsa_memory_copy_overlap.cpp        |  5 ----
 .../asan/TestCases/AMDGPU/lit.local.cfg.py    | 23 +++++++++++++++----
 3 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index 76899716e2398..702ec5c100d25 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -6,11 +6,6 @@
 // hsa_amd_memory_pool_free interceptors: freeing the same pool allocation
 // twice is diagnosed (same family of checks as double-free on malloc).
 //
-// Link against ROCm's HSA runtime. Tests under TestCases/AMDGPU run only when
-// lit finds a ROCm install (see lit.local.cfg.py): $ROCM_PATH or /opt/rocm,
-// with include/hsa/hsa.h and libhsa-runtime64. Compiler-rt must be built with
-// SANITIZER_AMDGPU enabled. The suite uses the dynamic ASan runtime only.
-//
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
index 7895899a87dd5..51f90366d40b2 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
@@ -5,11 +5,6 @@
 // Regression test for the AddressSanitizer hsa_memory_copy interceptor: invalid
 // overlapping ranges are diagnosed (same family of checks as memcpy).
 //
-// Link against ROCm's HSA runtime. Tests under TestCases/AMDGPU run only when
-// lit finds a ROCm install (see lit.local.cfg.py): $ROCM_PATH or /opt/rocm,
-// with include/hsa/hsa.h and libhsa-runtime64. Compiler-rt must be built with
-// SANITIZER_AMDGPU enabled.
-//
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
index b825c27eacf21..7807c20f3a4cd 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
@@ -1,12 +1,25 @@
+# Link against ROCm's HSA runtime. Tests under TestCases/AMDGPU run only when
+# lit finds a ROCm install (see lit.local.cfg.py): $ROCM_PATH or /opt/rocm,
+# with include/hsa/hsa.h and libhsa-runtime64. Compiler-rt must be built with
+# SANITIZER_AMDGPU enabled. The suite uses the dynamic ASan runtime only.
+
 import glob
 import os
 
-
 def getRoot(config):
     if not config.parent:
         return config
     return getRoot(config.parent)
 
+def walk_config_attr(cfg, name):
+    """Return the first defined attribute `name` walking cfg -> parents."""
+    while cfg is not None:
+        if hasattr(cfg, name):
+            val = getattr(cfg, name)
+            if val is not None:
+                return val
+        cfg = cfg.parent
+    return None
 
 def rocm_lib_dir(rocm_root):
     """Return lib or lib64 under rocm_root that provides libhsa-runtime64."""
@@ -18,7 +31,6 @@ def rocm_lib_dir(rocm_root):
             return libdir
     return None
 
-
 def rocm_is_available(rocm_root):
     if not rocm_root or not os.path.isdir(rocm_root):
         return False
@@ -27,14 +39,17 @@ def rocm_is_available(rocm_root):
         return False
     return rocm_lib_dir(rocm_root) is not None
 
-
 root = getRoot(config)
-
 # AMDGPU ASan tests are only run with the dynamic ASan runtime (-shared-libasan).
 if "asan-static-runtime" in root.available_features:
     config.unsupported = True
 elif root.target_os != "Linux":
     config.unsupported = True
+elif walk_config_attr(config, "bits") == "32" or walk_config_attr(
+    config, "target_arch"
+) in ("i386", "i686"):
+    # ROCm libhsa-runtime64.so is 64-bit only (link fails: incompatible with elf32-i386).
+    config.unsupported = True
 else:
     rocm_root = os.environ.get("ROCM_PATH", "/opt/rocm")
     if not rocm_is_available(rocm_root):

>From ff0feccacd6aa164b664d6cd0c75bc7f7794f816 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Thu, 23 Apr 2026 20:53:07 +0530
Subject: [PATCH 10/46] [sanitizer_common] Add GetMetaDataFastLocked for locked
 allocator paths

Introduce CombinedAllocator::GetMetaDataFastLocked, mirroring
GetMetaData but using GetBlockBeginFastLocked for secondary (and AMDGPU
device) lookups so callers that already hold the allocator lock do not
take the secondary mutex again.

Use the fast path from LSAN (MetadataFastLocked) and HWASAN where
metadata is read under the same locking discipline as chunk iteration.
---
 compiler-rt/lib/hwasan/hwasan_allocator.cpp       | 15 ++++++++-------
 compiler-rt/lib/lsan/lsan_allocator.cpp           | 10 ++++++++--
 .../sanitizer_allocator_combined.h                | 15 +++++++++++++++
 3 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.cpp b/compiler-rt/lib/hwasan/hwasan_allocator.cpp
index 80cc8e1b69a23..7a03caac80778 100644
--- a/compiler-rt/lib/hwasan/hwasan_allocator.cpp
+++ b/compiler-rt/lib/hwasan/hwasan_allocator.cpp
@@ -571,8 +571,9 @@ uptr PointsIntoChunk(void *p) {
       reinterpret_cast<uptr>(__hwasan::allocator.GetBlockBeginFastLocked(p));
   if (!chunk)
     return 0;
-  __hwasan::Metadata *metadata = reinterpret_cast<__hwasan::Metadata *>(
-      __hwasan::allocator.GetMetaData(reinterpret_cast<void *>(chunk)));
+  __hwasan::Metadata* metadata = reinterpret_cast<__hwasan::Metadata*>(
+      __hwasan::allocator.GetMetaDataFastLocked(
+          reinterpret_cast<void*>(chunk)));
   if (!metadata || !metadata->IsAllocated())
     return 0;
   if (addr < chunk + metadata->GetRequestedSize())
@@ -588,8 +589,8 @@ uptr GetUserBegin(uptr chunk) {
       reinterpret_cast<void *>(chunk));
   if (!block)
     return 0;
-  __hwasan::Metadata *metadata = reinterpret_cast<__hwasan::Metadata *>(
-      __hwasan::allocator.GetMetaData(block));
+  __hwasan::Metadata* metadata = reinterpret_cast<__hwasan::Metadata*>(
+      __hwasan::allocator.GetMetaDataFastLocked(block));
   if (!metadata || !metadata->IsAllocated())
     return 0;
 
@@ -605,9 +606,9 @@ uptr GetUserAddr(uptr chunk) {
 
 LsanMetadata::LsanMetadata(uptr chunk) {
   CHECK_EQ(UntagAddr(chunk), chunk);
-  metadata_ =
-      chunk ? __hwasan::allocator.GetMetaData(reinterpret_cast<void *>(chunk))
-            : nullptr;
+  metadata_ = chunk ? __hwasan::allocator.GetMetaDataFastLocked(
+                          reinterpret_cast<void*>(chunk))
+                    : nullptr;
 }
 
 bool LsanMetadata::allocated() const {
diff --git a/compiler-rt/lib/lsan/lsan_allocator.cpp b/compiler-rt/lib/lsan/lsan_allocator.cpp
index 110c1cfc3bf92..b1f5b4fa96bb5 100644
--- a/compiler-rt/lib/lsan/lsan_allocator.cpp
+++ b/compiler-rt/lib/lsan/lsan_allocator.cpp
@@ -60,6 +60,12 @@ static ChunkMetadata *Metadata(const void *p) {
   return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
 }
 
+// Same as Metadata, but must be called with the allocator locked (via
+// ForceLock). Avoids re-acquiring the secondary allocator mutex.
+static ChunkMetadata* MetadataFastLocked(const void* p) {
+  return reinterpret_cast<ChunkMetadata*>(allocator.GetMetaDataFastLocked(p));
+}
+
 static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) {
   if (!p) return;
   ChunkMetadata *m = Metadata(p);
@@ -287,7 +293,7 @@ uptr PointsIntoChunk(void* p) {
   // LargeMmapAllocator considers pointers to the meta-region of a chunk to be
   // valid, but we don't want that.
   if (addr < chunk) return 0;
-  ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
+  ChunkMetadata* m = MetadataFastLocked(reinterpret_cast<void*>(chunk));
   CHECK(m);
   if (!m->allocated)
     return 0;
@@ -307,7 +313,7 @@ uptr GetUserAddr(uptr chunk) {
 }
 
 LsanMetadata::LsanMetadata(uptr chunk) {
-  metadata_ = Metadata(reinterpret_cast<void *>(chunk));
+  metadata_ = MetadataFastLocked(reinterpret_cast<void*>(chunk));
   CHECK(metadata_);
 }
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
index b3ec606f61cb1..1397959b66679 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -160,6 +160,21 @@ class CombinedAllocator {
     return nullptr;
   }
 
+  // Same as GetMetaData, but must be called with the allocator locked
+  // (via ForceLock). Uses GetBlockBeginFastLocked for secondary/device checks
+  // to avoid re-acquiring the mutex already held by the caller.
+  void* GetMetaDataFastLocked(const void* p) {
+    if (primary_.PointerIsMine(p))
+      return primary_.GetMetaData(p);
+    if (secondary_.GetBlockBeginFastLocked(p))
+      return secondary_.GetMetaData(p);
+#if SANITIZER_AMDGPU
+    if (device_.GetBlockBeginFastLocked(p))
+      return device_.GetMetaData(p);
+#endif
+    return nullptr;
+  }
+
   void *GetBlockBegin(const void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);

>From a73ff3c8b35994f5a377afce0dee6e807fdaf1a8 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Thu, 23 Apr 2026 21:08:32 +0530
Subject: [PATCH 11/46] Fix python code formatting issues for lit.local.cfg.py

---
 compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
index 7807c20f3a4cd..a544e8d3d0b9d 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
@@ -6,11 +6,13 @@
 import glob
 import os
 
+
 def getRoot(config):
     if not config.parent:
         return config
     return getRoot(config.parent)
 
+
 def walk_config_attr(cfg, name):
     """Return the first defined attribute `name` walking cfg -> parents."""
     while cfg is not None:
@@ -21,6 +23,7 @@ def walk_config_attr(cfg, name):
         cfg = cfg.parent
     return None
 
+
 def rocm_lib_dir(rocm_root):
     """Return lib or lib64 under rocm_root that provides libhsa-runtime64."""
     for libname in ("lib", "lib64"):
@@ -31,6 +34,7 @@ def rocm_lib_dir(rocm_root):
             return libdir
     return None
 
+
 def rocm_is_available(rocm_root):
     if not rocm_root or not os.path.isdir(rocm_root):
         return False
@@ -39,6 +43,7 @@ def rocm_is_available(rocm_root):
         return False
     return rocm_lib_dir(rocm_root) is not None
 
+
 root = getRoot(config)
 # AMDGPU ASan tests are only run with the dynamic ASan runtime (-shared-libasan).
 if "asan-static-runtime" in root.available_features:

>From 2f5561a26d50ea73abd279faaa6053ec086b459b Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Fri, 24 Apr 2026 14:12:10 +0530
Subject: [PATCH 12/46] [compiler-rt][AMDASAN-cmake] Add Find HSA/AMDComgr
 CMake modules

Add `FindHSA.cmake` and `FindAMDComgr.cmake` modules invoked via
find_package(... MODULE).

Discovery honors HSA_ROOT / AMDComgr_ROOT, ROCM_PATH (and ROCM_PATH
env), legacy SANITIZER_*_INCLUDE_PATH hints (including leaf include
dirs), and default legacy search path `/opt/rocm/include`, then sets
HSA_INCLUDE_DIR and AMDComgr_INCLUDE_DIR for include_directories.
---
 compiler-rt/CMakeLists.txt                   | 19 +-----
 compiler-rt/cmake/Modules/FindAMDComgr.cmake | 68 ++++++++++++++++++++
 compiler-rt/cmake/Modules/FindHSA.cmake      | 59 +++++++++++++++++
 3 files changed, 130 insertions(+), 16 deletions(-)
 create mode 100644 compiler-rt/cmake/Modules/FindAMDComgr.cmake
 create mode 100644 compiler-rt/cmake/Modules/FindHSA.cmake

diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index 50f5515422833..93fe212f1fafc 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -563,22 +563,9 @@ endif()
 
 if(SANITIZER_AMDGPU)
   list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDGPU=1)
-  message(STATUS "Looking 'hsa' and 'amd_comgr' header")
-  find_path(HSA_INCLUDE NAMES hsa.h HINTS ${SANITIZER_HSA_INCLUDE_PATH} /opt/rocm/include PATH_SUFFIXES hsa)
-  if(NOT HSA_INCLUDE)
-    message(FATAL_ERROR "Required header 'hsa.h' not found in path ${HSA_INCLUDE}. Aborting SANITIZER_AMDGPU build")
-  endif()
-  message(STATUS "Found 'hsa.h' in ${HSA_INCLUDE}")
-  include_directories(${HSA_INCLUDE})
-  find_path(COMgr_INCLUDE NAMES amd_comgr.h.in HINTS ${SANITIZER_COMGR_INCLUDE_PATH} PATH_SUFFIXES amd_comgr)
-  if(NOT COMgr_INCLUDE)
-    find_path(COMgr_INCLUDE NAMES amd_comgr.h HINTS /opt/rocm/include PATH_SUFFIXES amd_comgr)
-    if(NOT COMgr_INCLUDE)
-      message(FATAL_ERROR "Required header 'amd_comgr.h/amd_comgr.h.in' not found in path ${COMgr_INCLUDE}. Aborting SANITIZER_AMDGPU build")
-    endif()
-  endif()
-  message(STATUS "Found 'amd_comgr.h.in/amd_comgr.h' in ${COMgr_INCLUDE}")
-  include_directories(${COMgr_INCLUDE})
+  find_package(HSA REQUIRED MODULE)
+  find_package(AMDComgr REQUIRED MODULE)
+  include_directories(${HSA_INCLUDE_DIR} ${AMDComgr_INCLUDE_DIR})
 endif()
 
 if(LLVM_ENABLE_MODULES)
diff --git a/compiler-rt/cmake/Modules/FindAMDComgr.cmake b/compiler-rt/cmake/Modules/FindAMDComgr.cmake
new file mode 100644
index 0000000000000..2303cbd5d89f3
--- /dev/null
+++ b/compiler-rt/cmake/Modules/FindAMDComgr.cmake
@@ -0,0 +1,68 @@
+# Find AMD COMGR headers (amd_comgr.h or amd_comgr.h.in).
+#
+# This module is used by compiler-rt when SANITIZER_AMDGPU is enabled.
+#
+# The following variables may be set by the user:
+#   AMDComgr_ROOT         ROCm / amd_comgr install prefix
+#   ROCM_PATH             Typical ROCm install (expects include/amd_comgr/...)
+#   SANITIZER_COMGR_INCLUDE_PATH
+#                         Legacy hint: base directory searched with
+#                         PATH_SUFFIXES amd_comgr (same as original CMake).
+#
+# The following cache variables may be set by this module:
+#   AMDComgr_INCLUDE_DIR  Directory containing amd_comgr.h(.in)
+#
+# This module defines:
+#   AMDComgr_FOUND        TRUE if a supported amd_comgr header was found
+#
+# Example:
+#   find_package(AMDComgr REQUIRED)
+
+include(FindPackageHandleStandardArgs)
+
+set(_amdcomgr_search_paths "")
+foreach(_root IN ITEMS "${AMDComgr_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
+  if(_root)
+    list(APPEND _amdcomgr_search_paths "${_root}/include")
+  endif()
+endforeach()
+if(SANITIZER_COMGR_INCLUDE_PATH)
+  list(APPEND _amdcomgr_search_paths "${SANITIZER_COMGR_INCLUDE_PATH}")
+endif()
+# Default Legacy ROCm include path
+list(APPEND _amdcomgr_search_paths "/opt/rocm/include")
+
+find_path(
+  AMDComgr_INCLUDE_DIR
+  NAMES amd_comgr.h.in
+  HINTS ${_amdcomgr_search_paths}
+  PATH_SUFFIXES amd_comgr
+)
+
+if(NOT AMDComgr_INCLUDE_DIR)
+  find_path(
+    AMDComgr_INCLUDE_DIR
+    NAMES amd_comgr.h
+    HINTS ${_amdcomgr_search_paths}
+    PATH_SUFFIXES amd_comgr
+  )
+endif()
+
+# If SANITIZER_COMGR_INCLUDE_PATH points at the leaf `amd_comgr` directory,
+# retry without PATH_SUFFIXES.
+if(NOT AMDComgr_INCLUDE_DIR AND SANITIZER_COMGR_INCLUDE_PATH)
+  find_path(
+    AMDComgr_INCLUDE_DIR
+    NAMES amd_comgr.h.in amd_comgr.h
+    HINTS "${SANITIZER_COMGR_INCLUDE_PATH}"
+    NO_DEFAULT_PATH
+    NO_CMAKE_PATH
+    NO_CMAKE_ENVIRONMENT_PATH
+    NO_SYSTEM_ENVIRONMENT_PATH
+    NO_CMAKE_SYSTEM_PATH
+  )
+endif()
+
+find_package_handle_standard_args(AMDComgr REQUIRED_VARS AMDComgr_INCLUDE_DIR)
+
+mark_as_advanced(AMDComgr_INCLUDE_DIR)
diff --git a/compiler-rt/cmake/Modules/FindHSA.cmake b/compiler-rt/cmake/Modules/FindHSA.cmake
new file mode 100644
index 0000000000000..bd0f57a31c47f
--- /dev/null
+++ b/compiler-rt/cmake/Modules/FindHSA.cmake
@@ -0,0 +1,59 @@
+# Find ROCm HSA runtime headers (hsa.h/hsa_ext_amd.h).
+#
+# This module is used by compiler-rt when SANITIZER_AMDGPU is enabled.
+#
+# The following variables may be set by the user:
+#   HSA_ROOT              ROCm / HSA install prefix (expects include/hsa/hsa.h)
+#   ROCM_PATH             Same as typical ROCm install (expects include/hsa/hsa.h)
+#   SANITIZER_HSA_INCLUDE_PATH
+#                         Legacy hint: directory searched like the original
+#                         find_path(HINTS ... PATH_SUFFIXES hsa) entry.
+#
+# The following cache variables may be set by this module:
+#   HSA_INCLUDE_DIR       Directory containing hsa.h (typically .../include/hsa)
+#
+# This module defines:
+#   HSA_FOUND             TRUE if hsa.h was found
+#
+# Example:
+#   find_package(HSA REQUIRED)
+
+include(FindPackageHandleStandardArgs)
+
+set(_hsa_search_paths "")
+foreach(_root IN ITEMS "${HSA_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
+  if(_root)
+    list(APPEND _hsa_search_paths "${_root}/include")
+  endif()
+endforeach()
+if(SANITIZER_HSA_INCLUDE_PATH)
+  list(APPEND _hsa_search_paths "${SANITIZER_HSA_INCLUDE_PATH}")
+endif()
+# Default Legacy ROCm include path
+list(APPEND _hsa_search_paths "/opt/rocm/include")
+
+find_path(
+  HSA_INCLUDE_DIR
+  NAMES hsa.h
+  HINTS ${_hsa_search_paths}
+  PATH_SUFFIXES hsa
+)
+
+# If SANITIZER_HSA_INCLUDE_PATH points at the leaf `hsa` directory, retry
+# without PATH_SUFFIXES.
+if(NOT HSA_INCLUDE_DIR AND SANITIZER_HSA_INCLUDE_PATH)
+  find_path(
+    HSA_INCLUDE_DIR
+    NAMES hsa.h
+    HINTS "${SANITIZER_HSA_INCLUDE_PATH}"
+    NO_DEFAULT_PATH
+    NO_CMAKE_PATH
+    NO_CMAKE_ENVIRONMENT_PATH
+    NO_SYSTEM_ENVIRONMENT_PATH
+    NO_CMAKE_SYSTEM_PATH
+  )
+endif()
+
+find_package_handle_standard_args(HSA REQUIRED_VARS HSA_INCLUDE_DIR)
+
+mark_as_advanced(HSA_INCLUDE_DIR)

>From 29e418b652097026bca1b22d1eeb976daeb50035 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Fri, 24 Apr 2026 16:44:44 +0530
Subject: [PATCH 13/46] [compiler-rt][AMDGPU-Cmake] Fold ROCm HSA/COMGr find
 logic into CompilerRTAMDGPUUtils.

Replace FindHSA.cmake and FindAMDComgr.cmake with a single
CompilerRTAMDGPUUtils.cmake module that defines
compiler_rt_find_amdgpu_sanitizer_dependencies().
---
 compiler-rt/CMakeLists.txt                    |   4 +-
 .../cmake/Modules/CompilerRTAMDGPUUtils.cmake | 101 ++++++++++++++++++
 compiler-rt/cmake/Modules/FindAMDComgr.cmake  |  68 ------------
 compiler-rt/cmake/Modules/FindHSA.cmake       |  59 ----------
 4 files changed, 103 insertions(+), 129 deletions(-)
 create mode 100644 compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
 delete mode 100644 compiler-rt/cmake/Modules/FindAMDComgr.cmake
 delete mode 100644 compiler-rt/cmake/Modules/FindHSA.cmake

diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index 93fe212f1fafc..14d39ec7e74aa 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -563,8 +563,8 @@ endif()
 
 if(SANITIZER_AMDGPU)
   list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDGPU=1)
-  find_package(HSA REQUIRED MODULE)
-  find_package(AMDComgr REQUIRED MODULE)
+  include(CompilerRTAMDGPUUtils)
+  compiler_rt_find_amdgpu_sanitizer_dependencies()
   include_directories(${HSA_INCLUDE_DIR} ${AMDComgr_INCLUDE_DIR})
 endif()
 
diff --git a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
new file mode 100644
index 0000000000000..e9d3af7ca0518
--- /dev/null
+++ b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
@@ -0,0 +1,101 @@
+# ROCm header discovery for compiler-rt when SANITIZER_AMDGPU is enabled.
+#
+# Include this module and call:
+#   compiler_rt_find_amdgpu_sanitizer_dependencies()
+#
+# User-settable hints (optional):
+#   HSA_ROOT / AMDComgr_ROOT   Install prefix (expects include/hsa/... or include/amd_comgr/...)
+#   ROCM_PATH                  Typical ROCm layout; also honors $ENV{ROCM_PATH}
+#   SANITIZER_HSA_INCLUDE_PATH Legacy: same search as original find_path(... PATH_SUFFIXES hsa)
+#   SANITIZER_COMGR_INCLUDE_PATH
+#                              Legacy: same search as original find_path(... PATH_SUFFIXES amd_comgr)
+#
+# Output (same as the former FindHSA / FindAMDComgr modules):
+#   HSA_INCLUDE_DIR, HSA_FOUND
+#   AMDComgr_INCLUDE_DIR, AMDComgr_FOUND
+#
+# This call is REQUIRED-style: missing headers trigger a fatal error from
+# find_package_handle_standard_args.
+
+include(FindPackageHandleStandardArgs)
+
+macro(compiler_rt_find_amdgpu_sanitizer_dependencies)
+  # --- HSA (hsa.h) ---
+  set(_hsa_search_paths "")
+  foreach(_root IN ITEMS "${HSA_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
+    if(_root)
+      list(APPEND _hsa_search_paths "${_root}/include")
+    endif()
+  endforeach()
+  if(SANITIZER_HSA_INCLUDE_PATH)
+    list(APPEND _hsa_search_paths "${SANITIZER_HSA_INCLUDE_PATH}")
+  endif()
+  list(APPEND _hsa_search_paths "/opt/rocm/include")
+
+  find_path(
+    HSA_INCLUDE_DIR
+    NAMES hsa.h
+    HINTS ${_hsa_search_paths}
+    PATH_SUFFIXES hsa
+  )
+
+  if(NOT HSA_INCLUDE_DIR AND SANITIZER_HSA_INCLUDE_PATH)
+    find_path(
+      HSA_INCLUDE_DIR
+      NAMES hsa.h
+      HINTS "${SANITIZER_HSA_INCLUDE_PATH}"
+      NO_DEFAULT_PATH
+      NO_CMAKE_PATH
+      NO_CMAKE_ENVIRONMENT_PATH
+      NO_SYSTEM_ENVIRONMENT_PATH
+      NO_CMAKE_SYSTEM_PATH
+    )
+  endif()
+
+  find_package_handle_standard_args(HSA REQUIRED_VARS HSA_INCLUDE_DIR)
+  mark_as_advanced(HSA_INCLUDE_DIR)
+
+  # --- AMD COMGR (amd_comgr.h.in / amd_comgr.h) ---
+  set(_amdcomgr_search_paths "")
+  foreach(_root IN ITEMS "${AMDComgr_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
+    if(_root)
+      list(APPEND _amdcomgr_search_paths "${_root}/include")
+    endif()
+  endforeach()
+  if(SANITIZER_COMGR_INCLUDE_PATH)
+    list(APPEND _amdcomgr_search_paths "${SANITIZER_COMGR_INCLUDE_PATH}")
+  endif()
+  list(APPEND _amdcomgr_search_paths "/opt/rocm/include")
+
+  find_path(
+    AMDComgr_INCLUDE_DIR
+    NAMES amd_comgr.h.in
+    HINTS ${_amdcomgr_search_paths}
+    PATH_SUFFIXES amd_comgr
+  )
+
+  if(NOT AMDComgr_INCLUDE_DIR)
+    find_path(
+      AMDComgr_INCLUDE_DIR
+      NAMES amd_comgr.h
+      HINTS ${_amdcomgr_search_paths}
+      PATH_SUFFIXES amd_comgr
+    )
+  endif()
+
+  if(NOT AMDComgr_INCLUDE_DIR AND SANITIZER_COMGR_INCLUDE_PATH)
+    find_path(
+      AMDComgr_INCLUDE_DIR
+      NAMES amd_comgr.h.in amd_comgr.h
+      HINTS "${SANITIZER_COMGR_INCLUDE_PATH}"
+      NO_DEFAULT_PATH
+      NO_CMAKE_PATH
+      NO_CMAKE_ENVIRONMENT_PATH
+      NO_SYSTEM_ENVIRONMENT_PATH
+      NO_CMAKE_SYSTEM_PATH
+    )
+  endif()
+
+  find_package_handle_standard_args(AMDComgr REQUIRED_VARS AMDComgr_INCLUDE_DIR)
+  mark_as_advanced(AMDComgr_INCLUDE_DIR)
+endmacro()
diff --git a/compiler-rt/cmake/Modules/FindAMDComgr.cmake b/compiler-rt/cmake/Modules/FindAMDComgr.cmake
deleted file mode 100644
index 2303cbd5d89f3..0000000000000
--- a/compiler-rt/cmake/Modules/FindAMDComgr.cmake
+++ /dev/null
@@ -1,68 +0,0 @@
-# Find AMD COMGR headers (amd_comgr.h or amd_comgr.h.in).
-#
-# This module is used by compiler-rt when SANITIZER_AMDGPU is enabled.
-#
-# The following variables may be set by the user:
-#   AMDComgr_ROOT         ROCm / amd_comgr install prefix
-#   ROCM_PATH             Typical ROCm install (expects include/amd_comgr/...)
-#   SANITIZER_COMGR_INCLUDE_PATH
-#                         Legacy hint: base directory searched with
-#                         PATH_SUFFIXES amd_comgr (same as original CMake).
-#
-# The following cache variables may be set by this module:
-#   AMDComgr_INCLUDE_DIR  Directory containing amd_comgr.h(.in)
-#
-# This module defines:
-#   AMDComgr_FOUND        TRUE if a supported amd_comgr header was found
-#
-# Example:
-#   find_package(AMDComgr REQUIRED)
-
-include(FindPackageHandleStandardArgs)
-
-set(_amdcomgr_search_paths "")
-foreach(_root IN ITEMS "${AMDComgr_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
-  if(_root)
-    list(APPEND _amdcomgr_search_paths "${_root}/include")
-  endif()
-endforeach()
-if(SANITIZER_COMGR_INCLUDE_PATH)
-  list(APPEND _amdcomgr_search_paths "${SANITIZER_COMGR_INCLUDE_PATH}")
-endif()
-# Default Legacy ROCm include path
-list(APPEND _amdcomgr_search_paths "/opt/rocm/include")
-
-find_path(
-  AMDComgr_INCLUDE_DIR
-  NAMES amd_comgr.h.in
-  HINTS ${_amdcomgr_search_paths}
-  PATH_SUFFIXES amd_comgr
-)
-
-if(NOT AMDComgr_INCLUDE_DIR)
-  find_path(
-    AMDComgr_INCLUDE_DIR
-    NAMES amd_comgr.h
-    HINTS ${_amdcomgr_search_paths}
-    PATH_SUFFIXES amd_comgr
-  )
-endif()
-
-# If SANITIZER_COMGR_INCLUDE_PATH points at the leaf `amd_comgr` directory,
-# retry without PATH_SUFFIXES.
-if(NOT AMDComgr_INCLUDE_DIR AND SANITIZER_COMGR_INCLUDE_PATH)
-  find_path(
-    AMDComgr_INCLUDE_DIR
-    NAMES amd_comgr.h.in amd_comgr.h
-    HINTS "${SANITIZER_COMGR_INCLUDE_PATH}"
-    NO_DEFAULT_PATH
-    NO_CMAKE_PATH
-    NO_CMAKE_ENVIRONMENT_PATH
-    NO_SYSTEM_ENVIRONMENT_PATH
-    NO_CMAKE_SYSTEM_PATH
-  )
-endif()
-
-find_package_handle_standard_args(AMDComgr REQUIRED_VARS AMDComgr_INCLUDE_DIR)
-
-mark_as_advanced(AMDComgr_INCLUDE_DIR)
diff --git a/compiler-rt/cmake/Modules/FindHSA.cmake b/compiler-rt/cmake/Modules/FindHSA.cmake
deleted file mode 100644
index bd0f57a31c47f..0000000000000
--- a/compiler-rt/cmake/Modules/FindHSA.cmake
+++ /dev/null
@@ -1,59 +0,0 @@
-# Find ROCm HSA runtime headers (hsa.h/hsa_ext_amd.h).
-#
-# This module is used by compiler-rt when SANITIZER_AMDGPU is enabled.
-#
-# The following variables may be set by the user:
-#   HSA_ROOT              ROCm / HSA install prefix (expects include/hsa/hsa.h)
-#   ROCM_PATH             Same as typical ROCm install (expects include/hsa/hsa.h)
-#   SANITIZER_HSA_INCLUDE_PATH
-#                         Legacy hint: directory searched like the original
-#                         find_path(HINTS ... PATH_SUFFIXES hsa) entry.
-#
-# The following cache variables may be set by this module:
-#   HSA_INCLUDE_DIR       Directory containing hsa.h (typically .../include/hsa)
-#
-# This module defines:
-#   HSA_FOUND             TRUE if hsa.h was found
-#
-# Example:
-#   find_package(HSA REQUIRED)
-
-include(FindPackageHandleStandardArgs)
-
-set(_hsa_search_paths "")
-foreach(_root IN ITEMS "${HSA_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
-  if(_root)
-    list(APPEND _hsa_search_paths "${_root}/include")
-  endif()
-endforeach()
-if(SANITIZER_HSA_INCLUDE_PATH)
-  list(APPEND _hsa_search_paths "${SANITIZER_HSA_INCLUDE_PATH}")
-endif()
-# Default Legacy ROCm include path
-list(APPEND _hsa_search_paths "/opt/rocm/include")
-
-find_path(
-  HSA_INCLUDE_DIR
-  NAMES hsa.h
-  HINTS ${_hsa_search_paths}
-  PATH_SUFFIXES hsa
-)
-
-# If SANITIZER_HSA_INCLUDE_PATH points at the leaf `hsa` directory, retry
-# without PATH_SUFFIXES.
-if(NOT HSA_INCLUDE_DIR AND SANITIZER_HSA_INCLUDE_PATH)
-  find_path(
-    HSA_INCLUDE_DIR
-    NAMES hsa.h
-    HINTS "${SANITIZER_HSA_INCLUDE_PATH}"
-    NO_DEFAULT_PATH
-    NO_CMAKE_PATH
-    NO_CMAKE_ENVIRONMENT_PATH
-    NO_SYSTEM_ENVIRONMENT_PATH
-    NO_CMAKE_SYSTEM_PATH
-  )
-endif()
-
-find_package_handle_standard_args(HSA REQUIRED_VARS HSA_INCLUDE_DIR)
-
-mark_as_advanced(HSA_INCLUDE_DIR)

>From 40926ba91b055e668a366d658dbcad561668b6d1 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 27 Apr 2026 16:31:13 +0530
Subject: [PATCH 14/46] [compiler-rt][HSA-Test] Add
 hsa_amd_pointer_info_memory_pool test.

---
 .../hsa_amd_pointer_info_memory_pool.cpp      | 101 ++++++++++++++++++
 1 file changed, 101 insertions(+)
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
new file mode 100644
index 0000000000000..13a05b8b35237
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
@@ -0,0 +1,101 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %run %t 2>&1 | FileCheck %s
+//
+// Regression test for the AddressSanitizer hsa_amd_pointer_info interceptor on
+// hsa_amd_memory_pool_allocate pointers: reported sizeInBytes matches the user
+// request (ASan unwraps the page-sized host wrapper from pointer metadata).
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+struct PoolSearch {
+  hsa_amd_memory_pool_t pool;
+  bool found;
+};
+
+static hsa_status_t find_alloc_pool(hsa_amd_memory_pool_t pool, void *data) {
+  auto *ps = static_cast<PoolSearch *>(data);
+  bool allow = false;
+  if (hsa_amd_memory_pool_get_info(
+          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
+          HSA_STATUS_SUCCESS ||
+      !allow)
+    return HSA_STATUS_SUCCESS;
+  ps->pool = pool;
+  ps->found = true;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+static hsa_status_t find_agent_with_pool(hsa_agent_t agent, void *data) {
+  (void)agent;
+  auto *ps = static_cast<PoolSearch *>(data);
+  ps->found = false;
+  hsa_status_t st =
+      hsa_amd_agent_iterate_memory_pools(agent, find_alloc_pool, ps);
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
+  return HSA_STATUS_SUCCESS;
+}
+
+int main() {
+  if (hsa_init() != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_init failed\n");
+    return 1;
+  }
+
+  PoolSearch ps = {};
+  ps.pool.handle = 0;
+  ps.found = false;
+
+  hsa_status_t it = hsa_iterate_agents(find_agent_with_pool, &ps);
+  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
+    fprintf(stderr, "hsa_iterate_agents failed\n");
+    return 1;
+  }
+  if (!ps.found) {
+    fprintf(stderr, "no runtime-alloc HSA memory pool found\n");
+    return 1;
+  }
+
+  void *mem = nullptr;
+  if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
+          HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
+    return 1;
+  }
+
+  hsa_amd_pointer_info_t info = {};
+  info.size = sizeof(hsa_amd_pointer_info_t);
+
+  if (hsa_amd_pointer_info(mem, &info, nullptr, nullptr, nullptr) !=
+      HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_pointer_info failed\n");
+    return 1;
+  }
+
+  printf("pointer_info_pool type: %d\n", info.type);
+  printf("pointer_info_pool sizeInBytes: %zu\n", info.sizeInBytes);
+  printf("pointer_info_pool begin: %p\n", info.agentBaseAddress);
+  printf("pointer_info_pool end: %p\n",
+         (void *)((uintptr_t)info.agentBaseAddress + info.sizeInBytes));
+
+  if (hsa_amd_memory_pool_free(mem) != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_memory_pool_free failed\n");
+    return 1;
+  }
+  return 0;
+}
+
+// CHECK: pointer_info_pool type: 1
+// CHECK-NEXT: pointer_info_pool sizeInBytes: 64
+// CHECK-NEXT: pointer_info_pool begin: 0x{{[0-9a-f]+}}
+// CHECK-NEXT: pointer_info_pool end: 0x{{[0-9a-f]+}}

>From 39602ed438b7d0eb1491f98a102ed464dbb23b50 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 27 Apr 2026 16:57:11 +0530
Subject: [PATCH 15/46] [compiler-rt][Cmake] Rename macro.

Rename macro `compiler_rt_find_amdgpu_sanitizer_dependencies` to
`compiler_rt_find_amdgpu_runtime_headers`.
---
 compiler-rt/CMakeLists.txt                            | 2 +-
 compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index 14d39ec7e74aa..26f3767e96aad 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -564,7 +564,7 @@ endif()
 if(SANITIZER_AMDGPU)
   list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDGPU=1)
   include(CompilerRTAMDGPUUtils)
-  compiler_rt_find_amdgpu_sanitizer_dependencies()
+  compiler_rt_find_amdgpu_runtime_headers()
   include_directories(${HSA_INCLUDE_DIR} ${AMDComgr_INCLUDE_DIR})
 endif()
 
diff --git a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
index e9d3af7ca0518..0ce46b220d565 100644
--- a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
@@ -19,7 +19,7 @@
 
 include(FindPackageHandleStandardArgs)
 
-macro(compiler_rt_find_amdgpu_sanitizer_dependencies)
+macro(compiler_rt_find_amdgpu_runtime_headers)
   # --- HSA (hsa.h) ---
   set(_hsa_search_paths "")
   foreach(_root IN ITEMS "${HSA_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")

>From 20ab3786a1ccfbcdaddd2958a2dc8894a3c9a3b8 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 28 Apr 2026 09:48:36 +0530
Subject: [PATCH 16/46] [compiler-rt][asan] Force RTLD_GLOBAL for
 HSA/HIP/OpenCL runtime dlopen.

  - When ASan intercepts dlopen of libamdhip64.so, libhsa-runtime64.so,
    or libamdocl64.so, add RTLD_GLOBAL if it was omitted, so symbols are
    visible as required by the AMDGPU device sanitizer runtime.

  - Add PatchHsaRuntimeDlopenFlag in sanitizer_common/sanitizer_linux
    and call it from the dlopen interceptor.

  - Update CompilerRTAMDGPUUtils.cmake comments for the AMDGPU runtime
    header search.
---
 .../cmake/Modules/CompilerRTAMDGPUUtils.cmake | 25 ++++++++++---------
 .../lib/sanitizer_common/sanitizer_common.h   |  6 +++++
 .../lib/sanitizer_common/sanitizer_linux.cpp  | 18 +++++++++++++
 3 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
index 0ce46b220d565..58a81ffdffd68 100644
--- a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
@@ -1,21 +1,20 @@
-# ROCm header discovery for compiler-rt when SANITIZER_AMDGPU is enabled.
+# ADMGPU runtime headers discovery for compiler-rt when SANITIZER_AMDGPU is enabled.
 #
-# Include this module and call:
-#   compiler_rt_find_amdgpu_sanitizer_dependencies()
+#  Usage: Include this module and call
+#   `compiler_rt_find_amdgpu_runtime_headers()`
 #
 # User-settable hints (optional):
-#   HSA_ROOT / AMDComgr_ROOT   Install prefix (expects include/hsa/... or include/amd_comgr/...)
-#   ROCM_PATH                  Typical ROCm layout; also honors $ENV{ROCM_PATH}
-#   SANITIZER_HSA_INCLUDE_PATH Legacy: same search as original find_path(... PATH_SUFFIXES hsa)
-#   SANITIZER_COMGR_INCLUDE_PATH
-#                              Legacy: same search as original find_path(... PATH_SUFFIXES amd_comgr)
-#
-# Output (same as the former FindHSA / FindAMDComgr modules):
+#   HSA_ROOT / AMDComgr_ROOT     Install prefix (expects include/hsa/... or include/amd_comgr/...)
+#   ROCM_PATH                    Typical ROCm layout; also honors $ENV{ROCM_PATH}
+#   SANITIZER_HSA_INCLUDE_PATH   Custom HSA Include Path from source tree
+#   SANITIZER_COMGR_INCLUDE_PATH Custom COMGR Include Path from source tree
+#                             
+# Output CMake variables:
 #   HSA_INCLUDE_DIR, HSA_FOUND
 #   AMDComgr_INCLUDE_DIR, AMDComgr_FOUND
 #
-# This call is REQUIRED-style: missing headers trigger a fatal error from
-# find_package_handle_standard_args.
+# This call is REQUIRED-style: missing headers triggers a fatal error from
+# `find_package_handle_standard_args`.
 
 include(FindPackageHandleStandardArgs)
 
@@ -30,6 +29,7 @@ macro(compiler_rt_find_amdgpu_runtime_headers)
   if(SANITIZER_HSA_INCLUDE_PATH)
     list(APPEND _hsa_search_paths "${SANITIZER_HSA_INCLUDE_PATH}")
   endif()
+  # Default Search Fallback: ROCm include path.
   list(APPEND _hsa_search_paths "/opt/rocm/include")
 
   find_path(
@@ -65,6 +65,7 @@ macro(compiler_rt_find_amdgpu_runtime_headers)
   if(SANITIZER_COMGR_INCLUDE_PATH)
     list(APPEND _amdcomgr_search_paths "${SANITIZER_COMGR_INCLUDE_PATH}")
   endif()
+  # Default Search Fallback: ROCm include path.
   list(APPEND _amdcomgr_search_paths "/opt/rocm/include")
 
   find_path(
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index 6f76d10a28cf6..d3ded56bed58c 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -1096,6 +1096,12 @@ const s32 kReleaseToOSIntervalNever = -1;
 // checks (e.g. RTLD_DEEPBIND on Linux).
 void OnDlOpen(const char* filename, int flag);
 
+#if SANITIZER_AMDGPU
+void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag);
+#else
+inline void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag) {}
+#endif
+
 // Returns the requested amount of random data (up to 256 bytes) that can then
 // be used to seed a PRNG. Defaults to blocking like the underlying syscall.
 bool GetRandom(void *buffer, uptr length, bool blocking = true);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index 6f0259f31dbf5..88d109dffd74a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -2901,6 +2901,24 @@ void OnDlOpen(const char* filename, int flag) {
 #  endif
 }
 
+#  if SANITIZER_AMDGPU
+void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag) {
+  if (filename &&
+      (internal_strstr(filename, "libamdhip64.so") ||
+       internal_strstr(filename, "libhsa-runtime64.so") ||
+       internal_strstr(filename, "libamdocl64.so")) &&
+      !(flag & RTLD_GLOBAL)) {
+    flag |= RTLD_GLOBAL;
+    if (Verbosity() >= 2) {
+      Printf(
+          "RTLD_GLOBAL flag on dlopen call forced on for %s due to AMDGPU "
+          "device sanitizer runtime requirements.\n",
+          filename);
+    }
+  }
+}
+#  endif
+
 uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
                               uptr *largest_gap_found,
                               uptr *max_occupied_addr) {

>From affeceac9d5452ed2fd741015bb7e7846a3b453a Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 4 May 2026 13:51:15 +0530
Subject: [PATCH 17/46] [compiler-rt] Only use hidden sanitizer interface on
 true AMDGPU TUs

SANITIZER_AMDGPU=1 is also used for host (e.g. x86_64) runtimes that
include the HSA stack. In that configuration, mapping
SANITIZER_INTERFACE_ATTRIBUTE to visibility("hidden") clashes with
replaceable operator new / delete, which must match the
default-visibility ABI.

Require defined(__AMDGPU__) in addition to SANITIZER_AMDGPU for the
hidden/weak branch, so only AMDGPU-target translation units get the
stricter attributes; NVPTX behavior is unchanged.
---
 compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
index c694897b6556b..b0a61686aa8d4 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -40,7 +40,7 @@
 #  if SANITIZER_GO
 #    define SANITIZER_INTERFACE_ATTRIBUTE
 #    define SANITIZER_WEAK_ATTRIBUTE
-#  elif SANITIZER_AMDGPU || SANITIZER_NVPTX
+#  elif (SANITIZER_AMDGPU && defined(__AMDGPU__)) || SANITIZER_NVPTX
 #    define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("hidden")))
 #    define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
 #  else

>From 551a0739a53f199183b51aa31aaf1255c45d5f4d Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 6 May 2026 13:29:39 +0530
Subject: [PATCH 18/46] [compiler-rt][HSA-Test] Add more AMDGPU ASan HSA Lit
 Tests.

  - Add test hsa_amd_ipc_memory_roundtrip.cpp
  - Add test hsa_amd_memory_async_copy_overlap.cpp
  - Add test hsa_amd_ipc_memory_attach_heap_oob.cpp
---
 .../hsa_amd_ipc_memory_attach_heap_oob.cpp    | 159 ++++++++++++++++++
 .../AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp   | 149 ++++++++++++++++
 .../hsa_amd_memory_async_copy_overlap.cpp     |  62 +++++++
 ...a_amd_memory_pool_allocate_double_free.cpp |   2 +-
 4 files changed, 371 insertions(+), 1 deletion(-)
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
new file mode 100644
index 0000000000000..ec1f38b2038f9
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
@@ -0,0 +1,159 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// After hsa_amd_ipc_memory_attach, AddressSanitizer poisons a trailing redzone
+// matching the pool allocation layout. A one-past-end store must be reported as
+// a heap-buffer-overflow.
+// hsa_amd_ipc_memory_create only supports coarse-grained GPU allocations; skip
+// fine-grained pools and non-GPU agents.
+//
+// The bad store is instrumented host code; VRAM imports may need CPU access
+// enabled (best-effort hsa_amd_agents_allow_access) or the fault can be SIGSEGV
+// instead of AddressSanitizer.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+struct PoolSearch {
+  hsa_amd_memory_pool_t pool;
+  bool found;
+};
+
+static hsa_status_t find_coarse_gpu_ipc_pool(hsa_amd_memory_pool_t pool,
+                                             void *data) {
+  auto *ps = static_cast<PoolSearch *>(data);
+
+  hsa_amd_segment_t segment = HSA_AMD_SEGMENT_PRIVATE;
+  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT,
+                                   &segment) != HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (segment != HSA_AMD_SEGMENT_GLOBAL)
+    return HSA_STATUS_SUCCESS;
+
+  uint32_t global_flags = 0;
+  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS,
+                                   &global_flags) != HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if ((global_flags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_COARSE_GRAINED) == 0)
+    return HSA_STATUS_SUCCESS;
+
+  bool allow = false;
+  if (hsa_amd_memory_pool_get_info(
+          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
+          HSA_STATUS_SUCCESS ||
+      !allow)
+    return HSA_STATUS_SUCCESS;
+
+  ps->pool = pool;
+  ps->found = true;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+static hsa_agent_t g_cpu_agent = {};
+
+static hsa_status_t pick_first_cpu_agent(hsa_agent_t agent, void * /*data*/) {
+  hsa_device_type_t dev = HSA_DEVICE_TYPE_GPU;
+  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
+      HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_CPU)
+    return HSA_STATUS_SUCCESS;
+  g_cpu_agent = agent;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+static hsa_status_t find_gpu_agent_with_ipc_pool(hsa_agent_t agent,
+                                                 void *data) {
+  hsa_device_type_t dev = HSA_DEVICE_TYPE_CPU;
+  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
+      HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_GPU)
+    return HSA_STATUS_SUCCESS;
+
+  auto *ps = static_cast<PoolSearch *>(data);
+  ps->found = false;
+  hsa_status_t st =
+      hsa_amd_agent_iterate_memory_pools(agent, find_coarse_gpu_ipc_pool, ps);
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
+  return HSA_STATUS_SUCCESS;
+}
+
+int main() {
+  if (hsa_init() != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_init failed\n");
+    return 1;
+  }
+
+  PoolSearch ps = {};
+  ps.pool.handle = 0;
+  ps.found = false;
+
+  hsa_status_t it = hsa_iterate_agents(find_gpu_agent_with_ipc_pool, &ps);
+  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
+    fprintf(stderr, "hsa_iterate_agents failed\n");
+    return 1;
+  }
+  if (!ps.found) {
+    fprintf(stderr,
+            "no coarse-grained GPU runtime-alloc HSA memory pool found\n");
+    return 1;
+  }
+
+  constexpr size_t kBytes = 64;
+  void *mem = nullptr;
+  if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
+          HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
+    return 1;
+  }
+
+  hsa_amd_ipc_memory_t ipc = {};
+  if (hsa_amd_ipc_memory_create(mem, kBytes, &ipc) != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_ipc_memory_create failed\n");
+    (void)hsa_amd_memory_pool_free(mem);
+    return 1;
+  }
+
+  void *mapped = nullptr;
+  if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
+                                /*mapping_agents=*/nullptr,
+                                &mapped) != HSA_STATUS_SUCCESS ||
+      !mapped) {
+    fprintf(stderr, "hsa_amd_ipc_memory_attach failed\n");
+    (void)hsa_amd_memory_pool_free(mem);
+    return 1;
+  }
+
+  g_cpu_agent.handle = 0;
+  (void)hsa_iterate_agents(pick_first_cpu_agent, nullptr);
+  if (g_cpu_agent.handle != 0) {
+    /* Best-effort: allow the host CPU to access the imported range so the store
+       below is a normal fault checked by ASan, not an unmapped-device SIGSEGV. */
+    (void)hsa_amd_agents_allow_access(/*num_agents=*/1, &g_cpu_agent,
+                                      /*flags=*/nullptr, mapped);
+  }
+
+  auto *p = reinterpret_cast<volatile char *>(mapped);
+  // One byte past the 64-byte imported region; should land in ASan's tail redzone.
+  p[kBytes] = 1;
+
+  fprintf(stderr, "expected heap-buffer-overflow after ipc attach\n");
+  (void)hsa_amd_ipc_memory_detach(mapped);
+  (void)hsa_amd_memory_pool_free(mem);
+  return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
+// CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
+// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:149:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
new file mode 100644
index 0000000000000..2994a9dc47460
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
@@ -0,0 +1,149 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %run %t 2>&1 | FileCheck %s
+//
+// Regression test for AddressSanitizer hsa_amd_ipc_memory_{create,attach,detach}:
+// same-process IPC round-trip on a pool allocation (unwrap on create, user pointer
+// adjustment and shadow on attach, base adjustment on detach).
+// hsa_amd_ipc_memory_create only supports coarse-grained GPU allocations; skip
+// fine-grained pools and non-GPU agents.
+//
+// Coarse-grained device memory is often not mapped for CPU stores; do not
+// read/write *mapped from the host. Validate with hsa_amd_pointer_info instead.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+struct PoolSearch {
+  hsa_amd_memory_pool_t pool;
+  bool found;
+};
+
+static hsa_status_t find_coarse_gpu_ipc_pool(hsa_amd_memory_pool_t pool,
+                                             void *data) {
+  auto *ps = static_cast<PoolSearch *>(data);
+
+  hsa_amd_segment_t segment = HSA_AMD_SEGMENT_PRIVATE;
+  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT,
+                                   &segment) != HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (segment != HSA_AMD_SEGMENT_GLOBAL)
+    return HSA_STATUS_SUCCESS;
+
+  uint32_t global_flags = 0;
+  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS,
+                                   &global_flags) != HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if ((global_flags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_COARSE_GRAINED) == 0)
+    return HSA_STATUS_SUCCESS;
+
+  bool allow = false;
+  if (hsa_amd_memory_pool_get_info(
+          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
+          HSA_STATUS_SUCCESS ||
+      !allow)
+    return HSA_STATUS_SUCCESS;
+
+  ps->pool = pool;
+  ps->found = true;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+static hsa_status_t find_gpu_agent_with_ipc_pool(hsa_agent_t agent,
+                                                 void *data) {
+  hsa_device_type_t dev = HSA_DEVICE_TYPE_CPU;
+  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
+      HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_GPU)
+    return HSA_STATUS_SUCCESS;
+
+  auto *ps = static_cast<PoolSearch *>(data);
+  ps->found = false;
+  hsa_status_t st =
+      hsa_amd_agent_iterate_memory_pools(agent, find_coarse_gpu_ipc_pool, ps);
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
+  return HSA_STATUS_SUCCESS;
+}
+
+int main() {
+  if (hsa_init() != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_init failed\n");
+    return 1;
+  }
+
+  PoolSearch ps = {};
+  ps.pool.handle = 0;
+  ps.found = false;
+
+  hsa_status_t it = hsa_iterate_agents(find_gpu_agent_with_ipc_pool, &ps);
+  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
+    fprintf(stderr, "hsa_iterate_agents failed\n");
+    return 1;
+  }
+  if (!ps.found) {
+    fprintf(stderr,
+            "no coarse-grained GPU runtime-alloc HSA memory pool found\n");
+    return 1;
+  }
+
+  constexpr size_t kBytes = 64;
+  void *mem = nullptr;
+  if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
+          HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
+    return 1;
+  }
+
+  hsa_amd_ipc_memory_t ipc = {};
+  if (hsa_amd_ipc_memory_create(mem, kBytes, &ipc) != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_ipc_memory_create failed\n");
+    (void)hsa_amd_memory_pool_free(mem);
+    return 1;
+  }
+
+  void *mapped = nullptr;
+  if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
+                                /*mapping_agents=*/nullptr,
+                                &mapped) != HSA_STATUS_SUCCESS ||
+      !mapped) {
+    fprintf(stderr, "hsa_amd_ipc_memory_attach failed\n");
+    (void)hsa_amd_memory_pool_free(mem);
+    return 1;
+  }
+
+  hsa_amd_pointer_info_t info = {};
+  info.size = sizeof(hsa_amd_pointer_info_t);
+  if (hsa_amd_pointer_info(mapped, &info, nullptr, nullptr, nullptr) !=
+      HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_pointer_info on imported mapping failed\n");
+    (void)hsa_amd_ipc_memory_detach(mapped);
+    (void)hsa_amd_memory_pool_free(mem);
+    return 1;
+  }
+
+  if (hsa_amd_ipc_memory_detach(mapped) != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_ipc_memory_detach failed\n");
+    (void)hsa_amd_memory_pool_free(mem);
+    return 1;
+  }
+
+  if (hsa_amd_memory_pool_free(mem) != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_memory_pool_free failed\n");
+    return 1;
+  }
+
+  printf("ipc roundtrip ok\n");
+  return 0;
+}
+
+// CHECK: ipc roundtrip ok
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
new file mode 100644
index 0000000000000..7d4cc994d9fa1
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
@@ -0,0 +1,62 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// Regression test for the AddressSanitizer hsa_amd_memory_async_copy interceptor:
+// invalid overlapping ranges are diagnosed (same family of checks as memcpy).
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static hsa_agent_t g_agent = {};
+
+static hsa_status_t pick_first_agent(hsa_agent_t agent, void * /*data*/) {
+  g_agent = agent;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+int main() {
+  if (hsa_init() != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_init failed\n");
+    return 1;
+  }
+
+  hsa_status_t it = hsa_iterate_agents(pick_first_agent, nullptr);
+  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
+    fprintf(stderr, "hsa_iterate_agents failed\n");
+    return 1;
+  }
+  if (g_agent.handle == 0) {
+    fprintf(stderr, "no HSA agent found\n");
+    return 1;
+  }
+
+  hsa_signal_t completion = {};
+  if (hsa_signal_create(/*initial_value=*/0, /*num_consumers=*/0,
+                        /*consumers=*/nullptr,
+                        &completion) != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_signal_create failed\n");
+    return 1;
+  }
+
+  char buf[128];
+  char *dst = buf;
+  char *src = buf + 40;
+  // Ranges [buf, buf+64) and [buf+40, buf+104) overlap; dst != src so the
+  // interceptor runs CHECK_RANGES_OVERLAP before scheduling the async copy.
+  (void)hsa_amd_memory_async_copy(dst, g_agent, src, g_agent, 64,
+                                  /*num_dep_signals=*/0,
+                                  /*dep_signals=*/nullptr, completion);
+  fprintf(stderr, "expected hsa_amd_memory_async_copy overlap report\n");
+  return 0;
+}
+
+// CHECK: hsa_amd_memory_async_copy-param-overlap: memory ranges
+// CHECK: [{{0x.*,[ ]*0x.*}}) and [{{0x.*,[ ]*0x.*}}) overlap
+// CHECK: SUMMARY: AddressSanitizer: hsa_amd_memory_async_copy-param-overlap
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index 702ec5c100d25..119b463af89e5 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -81,4 +81,4 @@ int main() {
 }
 
 // CHECK: ERROR: AddressSanitizer: attempting double-free
-// CHECK: SUMMARY: AddressSanitizer: double-free
+// CHECK: SUMMARY: AddressSanitizer: double-free {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:77:{{[0-9]+}} in main

>From e423c77e0a0168c24e3902ce9ddc8a67c4e09b73 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 11 May 2026 11:31:02 +0530
Subject: [PATCH 19/46] [compiler-rt][asan-test][AMDGPU] Disable LeakSanitizer
 for  HSA lit tests.

When the AMDGPU ASan TestCases/AMDGPU suite is enabled, append
`detect_leaks=0` to ASAN_OPTIONS.

Also extend the %env_asan_opts= and %export_asan_opts= substitution
prefixes so RUN lines that override ASAN_OPTIONS stay consistent with
environment passed to plain %run invocations.
---
 .../asan/TestCases/AMDGPU/lit.local.cfg.py    | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
index a544e8d3d0b9d..fc2aa6def57e4 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
@@ -44,6 +44,29 @@ def rocm_is_available(rocm_root):
     return rocm_lib_dir(rocm_root) is not None
 
 
+def append_detect_leaks_off_asan_subst(substitutions):
+    """Append detect_leaks=0 to %env_asan_opts= / %export_asan_opts= default prefixes."""
+
+    def patch_body(body):
+        if body:
+            return (
+                body + "detect_leaks=0:"
+                if body.endswith(":")
+                else body + ":detect_leaks=0:"
+            )
+        return "detect_leaks=0:"
+
+    pairs = (
+        ("%env_asan_opts=", "env ASAN_OPTIONS="),
+        ("%export_asan_opts=", "export ASAN_OPTIONS="),
+    )
+    for i, (pat, repl) in enumerate(substitutions):
+        for sub_pat, opt_prefix in pairs:
+            if pat == sub_pat and repl.startswith(opt_prefix):
+                body = repl[len(opt_prefix) :]
+                substitutions[i] = (pat, opt_prefix + patch_body(body))
+
+
 root = getRoot(config)
 # AMDGPU ASan tests are only run with the dynamic ASan runtime (-shared-libasan).
 if "asan-static-runtime" in root.available_features:
@@ -70,6 +93,13 @@ def rocm_is_available(rocm_root):
             config.unsupported = True
         else:
             config.available_features.add("rocm")
+            # Linux ASan defaults to leak detection; disable for ROCm/HSA tests.
+            _asan = config.environment.get("ASAN_OPTIONS", "")
+            if _asan:
+                config.environment["ASAN_OPTIONS"] = _asan + ":detect_leaks=0"
+            else:
+                config.environment["ASAN_OPTIONS"] = "detect_leaks=0"
+            append_detect_leaks_off_asan_subst(config.substitutions)
             rocm_lib = rocm_lib_dir(rocm_root)
             rocm_include = os.path.join(rocm_root, "include")
             config.substitutions.append(("%rocm_root", rocm_root))

>From 8e956a5d00c0c9930ddb963b4e67c80772996e00 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 11 May 2026 11:39:44 +0530
Subject: [PATCH 20/46] [compiler-rt][asan][AMDGPU] Refactor shared common HSA
 utility functions into 'hsa_amd_test_helpers.h'

Add 'hsa_amd_test_helpers.h' with inline helpers for hsa_init,iterating
agents, finding a runtime-alloc pool, finding a coarse-grained GPU IPC
pool, picking  CPU/first agents without globals and remove duplicated
callbacks invocation.
---
 .../cmake/Modules/CompilerRTAMDGPUUtils.cmake |   6 +-
 .../hsa_amd_ipc_memory_attach_heap_oob.cpp    | 100 ++--------
 .../AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp   |  76 +-------
 .../hsa_amd_memory_async_copy_overlap.cpp     |  25 +--
 ...a_amd_memory_pool_allocate_double_free.cpp |  53 +----
 .../hsa_amd_pointer_info_memory_pool.cpp      |  51 +----
 .../TestCases/AMDGPU/hsa_amd_test_helpers.h   | 184 ++++++++++++++++++
 7 files changed, 225 insertions(+), 270 deletions(-)
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h

diff --git a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
index 58a81ffdffd68..9ad77d4efb66e 100644
--- a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
@@ -1,4 +1,4 @@
-# ADMGPU runtime headers discovery for compiler-rt when SANITIZER_AMDGPU is enabled.
+# AMDGPU runtime headers discovery for compiler-rt when SANITIZER_AMDGPU is enabled.
 #
 #  Usage: Include this module and call
 #   `compiler_rt_find_amdgpu_runtime_headers()`
@@ -39,6 +39,8 @@ macro(compiler_rt_find_amdgpu_runtime_headers)
     PATH_SUFFIXES hsa
   )
 
+  # If SANITIZER_HSA_INCLUDE_PATH points at the leaf `hsa` directory,
+  # retry without PATH_SUFFIXES.
   if(NOT HSA_INCLUDE_DIR AND SANITIZER_HSA_INCLUDE_PATH)
     find_path(
       HSA_INCLUDE_DIR
@@ -84,6 +86,8 @@ macro(compiler_rt_find_amdgpu_runtime_headers)
     )
   endif()
 
+  # If SANITIZER_COMGR_INCLUDE_PATH points at the leaf `amd_comgr` directory,
+  # retry without PATH_SUFFIXES.
   if(NOT AMDComgr_INCLUDE_DIR AND SANITIZER_COMGR_INCLUDE_PATH)
     find_path(
       AMDComgr_INCLUDE_DIR
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
index ec1f38b2038f9..f39b86e6fe333 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
@@ -15,99 +15,20 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
 
 #include <stdio.h>
 
-struct PoolSearch {
-  hsa_amd_memory_pool_t pool;
-  bool found;
-};
-
-static hsa_status_t find_coarse_gpu_ipc_pool(hsa_amd_memory_pool_t pool,
-                                             void *data) {
-  auto *ps = static_cast<PoolSearch *>(data);
-
-  hsa_amd_segment_t segment = HSA_AMD_SEGMENT_PRIVATE;
-  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT,
-                                   &segment) != HSA_STATUS_SUCCESS)
-    return HSA_STATUS_SUCCESS;
-  if (segment != HSA_AMD_SEGMENT_GLOBAL)
-    return HSA_STATUS_SUCCESS;
-
-  uint32_t global_flags = 0;
-  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS,
-                                   &global_flags) != HSA_STATUS_SUCCESS)
-    return HSA_STATUS_SUCCESS;
-  if ((global_flags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_COARSE_GRAINED) == 0)
-    return HSA_STATUS_SUCCESS;
-
-  bool allow = false;
-  if (hsa_amd_memory_pool_get_info(
-          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
-          HSA_STATUS_SUCCESS ||
-      !allow)
-    return HSA_STATUS_SUCCESS;
-
-  ps->pool = pool;
-  ps->found = true;
-  return HSA_STATUS_INFO_BREAK;
-}
-
-static hsa_agent_t g_cpu_agent = {};
-
-static hsa_status_t pick_first_cpu_agent(hsa_agent_t agent, void * /*data*/) {
-  hsa_device_type_t dev = HSA_DEVICE_TYPE_GPU;
-  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
-      HSA_STATUS_SUCCESS)
-    return HSA_STATUS_SUCCESS;
-  if (dev != HSA_DEVICE_TYPE_CPU)
-    return HSA_STATUS_SUCCESS;
-  g_cpu_agent = agent;
-  return HSA_STATUS_INFO_BREAK;
-}
-
-static hsa_status_t find_gpu_agent_with_ipc_pool(hsa_agent_t agent,
-                                                 void *data) {
-  hsa_device_type_t dev = HSA_DEVICE_TYPE_CPU;
-  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
-      HSA_STATUS_SUCCESS)
-    return HSA_STATUS_SUCCESS;
-  if (dev != HSA_DEVICE_TYPE_GPU)
-    return HSA_STATUS_SUCCESS;
-
-  auto *ps = static_cast<PoolSearch *>(data);
-  ps->found = false;
-  hsa_status_t st =
-      hsa_amd_agent_iterate_memory_pools(agent, find_coarse_gpu_ipc_pool, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
-    return st;
-  if (ps->found)
-    return HSA_STATUS_INFO_BREAK;
-  return HSA_STATUS_SUCCESS;
-}
-
 int main() {
-  if (hsa_init() != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_init failed\n");
+  if (hsa_amd_test_require_init())
     return 1;
-  }
-
-  PoolSearch ps = {};
-  ps.pool.handle = 0;
-  ps.found = false;
 
-  hsa_status_t it = hsa_iterate_agents(find_gpu_agent_with_ipc_pool, &ps);
-  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
-    fprintf(stderr, "hsa_iterate_agents failed\n");
+  HsaAmdPoolSearch ps;
+  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
     return 1;
-  }
-  if (!ps.found) {
-    fprintf(stderr,
-            "no coarse-grained GPU runtime-alloc HSA memory pool found\n");
-    return 1;
-  }
 
   constexpr size_t kBytes = 64;
   void *mem = nullptr;
@@ -135,12 +56,13 @@ int main() {
     return 1;
   }
 
-  g_cpu_agent.handle = 0;
-  (void)hsa_iterate_agents(pick_first_cpu_agent, nullptr);
-  if (g_cpu_agent.handle != 0) {
+  HsaAmdCpuAgentPick cpu;
+  hsa_amd_test_cpu_agent_pick_init(&cpu);
+  (void)hsa_iterate_agents(hsa_amd_test_pick_first_cpu_agent_cb, &cpu);
+  if (cpu.agent.handle != 0) {
     /* Best-effort: allow the host CPU to access the imported range so the store
        below is a normal fault checked by ASan, not an unmapped-device SIGSEGV. */
-    (void)hsa_amd_agents_allow_access(/*num_agents=*/1, &g_cpu_agent,
+    (void)hsa_amd_agents_allow_access(/*num_agents=*/1, &cpu.agent,
                                       /*flags=*/nullptr, mapped);
   }
 
@@ -156,4 +78,4 @@ int main() {
 
 // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
 // CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
-// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:149:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:71:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
index 2994a9dc47460..f84b1bbdde2a2 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
@@ -14,86 +14,20 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
 
 #include <stdio.h>
 
-struct PoolSearch {
-  hsa_amd_memory_pool_t pool;
-  bool found;
-};
-
-static hsa_status_t find_coarse_gpu_ipc_pool(hsa_amd_memory_pool_t pool,
-                                             void *data) {
-  auto *ps = static_cast<PoolSearch *>(data);
-
-  hsa_amd_segment_t segment = HSA_AMD_SEGMENT_PRIVATE;
-  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT,
-                                   &segment) != HSA_STATUS_SUCCESS)
-    return HSA_STATUS_SUCCESS;
-  if (segment != HSA_AMD_SEGMENT_GLOBAL)
-    return HSA_STATUS_SUCCESS;
-
-  uint32_t global_flags = 0;
-  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS,
-                                   &global_flags) != HSA_STATUS_SUCCESS)
-    return HSA_STATUS_SUCCESS;
-  if ((global_flags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_COARSE_GRAINED) == 0)
-    return HSA_STATUS_SUCCESS;
-
-  bool allow = false;
-  if (hsa_amd_memory_pool_get_info(
-          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
-          HSA_STATUS_SUCCESS ||
-      !allow)
-    return HSA_STATUS_SUCCESS;
-
-  ps->pool = pool;
-  ps->found = true;
-  return HSA_STATUS_INFO_BREAK;
-}
-
-static hsa_status_t find_gpu_agent_with_ipc_pool(hsa_agent_t agent,
-                                                 void *data) {
-  hsa_device_type_t dev = HSA_DEVICE_TYPE_CPU;
-  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
-      HSA_STATUS_SUCCESS)
-    return HSA_STATUS_SUCCESS;
-  if (dev != HSA_DEVICE_TYPE_GPU)
-    return HSA_STATUS_SUCCESS;
-
-  auto *ps = static_cast<PoolSearch *>(data);
-  ps->found = false;
-  hsa_status_t st =
-      hsa_amd_agent_iterate_memory_pools(agent, find_coarse_gpu_ipc_pool, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
-    return st;
-  if (ps->found)
-    return HSA_STATUS_INFO_BREAK;
-  return HSA_STATUS_SUCCESS;
-}
-
 int main() {
-  if (hsa_init() != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_init failed\n");
+  if (hsa_amd_test_require_init())
     return 1;
-  }
 
-  PoolSearch ps = {};
-  ps.pool.handle = 0;
-  ps.found = false;
-
-  hsa_status_t it = hsa_iterate_agents(find_gpu_agent_with_ipc_pool, &ps);
-  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
-    fprintf(stderr, "hsa_iterate_agents failed\n");
-    return 1;
-  }
-  if (!ps.found) {
-    fprintf(stderr,
-            "no coarse-grained GPU runtime-alloc HSA memory pool found\n");
+  HsaAmdPoolSearch ps;
+  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
     return 1;
-  }
 
   constexpr size_t kBytes = 64;
   void *mem = nullptr;
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
index 7d4cc994d9fa1..c773b55b8e866 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
@@ -8,31 +8,24 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 
-static hsa_agent_t g_agent = {};
-
-static hsa_status_t pick_first_agent(hsa_agent_t agent, void * /*data*/) {
-  g_agent = agent;
-  return HSA_STATUS_INFO_BREAK;
-}
-
 int main() {
-  if (hsa_init() != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_init failed\n");
+  if (hsa_amd_test_require_init())
     return 1;
-  }
 
-  hsa_status_t it = hsa_iterate_agents(pick_first_agent, nullptr);
-  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
-    fprintf(stderr, "hsa_iterate_agents failed\n");
+  HsaAmdAgentPick pick;
+  hsa_amd_test_agent_pick_init(&pick);
+  hsa_status_t it = hsa_iterate_agents(hsa_amd_test_pick_first_agent_cb, &pick);
+  if (hsa_amd_test_iterate_agents_ok(it))
     return 1;
-  }
-  if (g_agent.handle == 0) {
+  if (pick.agent.handle == 0) {
     fprintf(stderr, "no HSA agent found\n");
     return 1;
   }
@@ -50,7 +43,7 @@ int main() {
   char *src = buf + 40;
   // Ranges [buf, buf+64) and [buf+40, buf+104) overlap; dst != src so the
   // interceptor runs CHECK_RANGES_OVERLAP before scheduling the async copy.
-  (void)hsa_amd_memory_async_copy(dst, g_agent, src, g_agent, 64,
+  (void)hsa_amd_memory_async_copy(dst, pick.agent, src, pick.agent, 64,
                                   /*num_dep_signals=*/0,
                                   /*dep_signals=*/nullptr, completion);
   fprintf(stderr, "expected hsa_amd_memory_async_copy overlap report\n");
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index 119b463af89e5..82b21962b0948 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -9,61 +9,20 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
 
 #include <stdio.h>
 
-struct PoolSearch {
-  hsa_amd_memory_pool_t pool;
-  bool found;
-};
-
-static hsa_status_t find_alloc_pool(hsa_amd_memory_pool_t pool, void *data) {
-  auto *ps = static_cast<PoolSearch *>(data);
-  bool allow = false;
-  if (hsa_amd_memory_pool_get_info(
-          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
-          HSA_STATUS_SUCCESS ||
-      !allow)
-    return HSA_STATUS_SUCCESS;
-  ps->pool = pool;
-  ps->found = true;
-  return HSA_STATUS_INFO_BREAK;
-}
-
-static hsa_status_t find_agent_with_pool(hsa_agent_t agent, void *data) {
-  (void)agent;
-  auto *ps = static_cast<PoolSearch *>(data);
-  ps->found = false;
-  hsa_status_t st =
-      hsa_amd_agent_iterate_memory_pools(agent, find_alloc_pool, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
-    return st;
-  if (ps->found)
-    return HSA_STATUS_INFO_BREAK;
-  return HSA_STATUS_SUCCESS;
-}
-
 int main() {
-  if (hsa_init() != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_init failed\n");
+  if (hsa_amd_test_require_init())
     return 1;
-  }
-
-  PoolSearch ps = {};
-  ps.pool.handle = 0;
-  ps.found = false;
 
-  hsa_status_t it = hsa_iterate_agents(find_agent_with_pool, &ps);
-  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
-    fprintf(stderr, "hsa_iterate_agents failed\n");
+  HsaAmdPoolSearch ps;
+  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
     return 1;
-  }
-  if (!ps.found) {
-    fprintf(stderr, "no runtime-alloc HSA memory pool found\n");
-    return 1;
-  }
 
   void *mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
@@ -81,4 +40,4 @@ int main() {
 }
 
 // CHECK: ERROR: AddressSanitizer: attempting double-free
-// CHECK: SUMMARY: AddressSanitizer: double-free {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:77:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: double-free {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:36:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
index 13a05b8b35237..c6c54373210e5 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
@@ -9,61 +9,20 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
 
 #include <stdio.h>
 
-struct PoolSearch {
-  hsa_amd_memory_pool_t pool;
-  bool found;
-};
-
-static hsa_status_t find_alloc_pool(hsa_amd_memory_pool_t pool, void *data) {
-  auto *ps = static_cast<PoolSearch *>(data);
-  bool allow = false;
-  if (hsa_amd_memory_pool_get_info(
-          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
-          HSA_STATUS_SUCCESS ||
-      !allow)
-    return HSA_STATUS_SUCCESS;
-  ps->pool = pool;
-  ps->found = true;
-  return HSA_STATUS_INFO_BREAK;
-}
-
-static hsa_status_t find_agent_with_pool(hsa_agent_t agent, void *data) {
-  (void)agent;
-  auto *ps = static_cast<PoolSearch *>(data);
-  ps->found = false;
-  hsa_status_t st =
-      hsa_amd_agent_iterate_memory_pools(agent, find_alloc_pool, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
-    return st;
-  if (ps->found)
-    return HSA_STATUS_INFO_BREAK;
-  return HSA_STATUS_SUCCESS;
-}
-
 int main() {
-  if (hsa_init() != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_init failed\n");
+  if (hsa_amd_test_require_init())
     return 1;
-  }
-
-  PoolSearch ps = {};
-  ps.pool.handle = 0;
-  ps.found = false;
 
-  hsa_status_t it = hsa_iterate_agents(find_agent_with_pool, &ps);
-  if (it != HSA_STATUS_SUCCESS && it != HSA_STATUS_INFO_BREAK) {
-    fprintf(stderr, "hsa_iterate_agents failed\n");
+  HsaAmdPoolSearch ps;
+  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
     return 1;
-  }
-  if (!ps.found) {
-    fprintf(stderr, "no runtime-alloc HSA memory pool found\n");
-    return 1;
-  }
 
   void *mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
new file mode 100644
index 0000000000000..53b7f571fcbcf
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
@@ -0,0 +1,184 @@
+//===-- hsa_amd_test_helpers.h - shared helpers for AMDGPU ASan HSA tests --===//
+//
+// Common ROCm/HSA discovery and init helpers for hsa_amd*.cpp tests in this
+// directory. Intended only for compiler-rt lit tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef COMPILER_RT_ASAN_AMDGPU_HSA_AMD_TEST_HELPERS_H
+#define COMPILER_RT_ASAN_AMDGPU_HSA_AMD_TEST_HELPERS_H
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+inline int hsa_amd_test_require_init() {
+  if (hsa_init() != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_init failed\n");
+    return 1;
+  }
+  return 0;
+}
+
+/// Return 1 if `st` indicates a hard failure from hsa_iterate_agents.
+inline int hsa_amd_test_iterate_agents_ok(hsa_status_t st) {
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK) {
+    fprintf(stderr, "hsa_iterate_agents failed\n");
+    return 1;
+  }
+  return 0;
+}
+
+struct HsaAmdPoolSearch {
+  hsa_amd_memory_pool_t pool;
+  bool found;
+};
+
+inline hsa_status_t
+hsa_amd_test_find_runtime_alloc_pool_cb(hsa_amd_memory_pool_t pool,
+                                        void *data) {
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+  bool allow = false;
+  if (hsa_amd_memory_pool_get_info(
+          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
+          HSA_STATUS_SUCCESS ||
+      !allow)
+    return HSA_STATUS_SUCCESS;
+  ps->pool = pool;
+  ps->found = true;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+inline hsa_status_t
+hsa_amd_test_find_agent_with_runtime_alloc_pool_cb(hsa_agent_t agent,
+                                                   void *data) {
+  (void)agent;
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+  ps->found = false;
+  hsa_status_t st = hsa_amd_agent_iterate_memory_pools(
+      agent, hsa_amd_test_find_runtime_alloc_pool_cb, ps);
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
+  return HSA_STATUS_SUCCESS;
+}
+
+inline int hsa_amd_test_find_first_runtime_alloc_pool(HsaAmdPoolSearch *ps) {
+  ps->pool.handle = 0;
+  ps->found = false;
+  hsa_status_t it = hsa_iterate_agents(
+      hsa_amd_test_find_agent_with_runtime_alloc_pool_cb, ps);
+  if (hsa_amd_test_iterate_agents_ok(it))
+    return 1;
+  if (!ps->found) {
+    fprintf(stderr, "no runtime-alloc HSA memory pool found\n");
+    return 1;
+  }
+  return 0;
+}
+
+inline hsa_status_t
+hsa_amd_test_find_coarse_gpu_ipc_pool_cb(hsa_amd_memory_pool_t pool,
+                                         void *data) {
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+
+  hsa_amd_segment_t segment = HSA_AMD_SEGMENT_PRIVATE;
+  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT,
+                                   &segment) != HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (segment != HSA_AMD_SEGMENT_GLOBAL)
+    return HSA_STATUS_SUCCESS;
+
+  uint32_t global_flags = 0;
+  if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS,
+                                   &global_flags) != HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if ((global_flags & HSA_AMD_MEMORY_POOL_GLOBAL_FLAG_COARSE_GRAINED) == 0)
+    return HSA_STATUS_SUCCESS;
+
+  bool allow = false;
+  if (hsa_amd_memory_pool_get_info(
+          pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
+          HSA_STATUS_SUCCESS ||
+      !allow)
+    return HSA_STATUS_SUCCESS;
+
+  ps->pool = pool;
+  ps->found = true;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+inline hsa_status_t
+hsa_amd_test_find_gpu_agent_with_ipc_pool_cb(hsa_agent_t agent, void *data) {
+  hsa_device_type_t dev = HSA_DEVICE_TYPE_CPU;
+  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
+      HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_GPU)
+    return HSA_STATUS_SUCCESS;
+
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+  ps->found = false;
+  hsa_status_t st = hsa_amd_agent_iterate_memory_pools(
+      agent, hsa_amd_test_find_coarse_gpu_ipc_pool_cb, ps);
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
+  return HSA_STATUS_SUCCESS;
+}
+
+inline int hsa_amd_test_find_first_coarse_gpu_ipc_pool(HsaAmdPoolSearch *ps) {
+  ps->pool.handle = 0;
+  ps->found = false;
+  hsa_status_t it =
+      hsa_iterate_agents(hsa_amd_test_find_gpu_agent_with_ipc_pool_cb, ps);
+  if (hsa_amd_test_iterate_agents_ok(it))
+    return 1;
+  if (!ps->found) {
+    fprintf(stderr,
+            "no coarse-grained GPU runtime-alloc HSA memory pool found\n");
+    return 1;
+  }
+  return 0;
+}
+
+struct HsaAmdCpuAgentPick {
+  hsa_agent_t agent;
+};
+
+inline void hsa_amd_test_cpu_agent_pick_init(HsaAmdCpuAgentPick *out) {
+  out->agent.handle = 0;
+}
+
+inline hsa_status_t hsa_amd_test_pick_first_cpu_agent_cb(hsa_agent_t agent,
+                                                         void *data) {
+  auto *out = static_cast<HsaAmdCpuAgentPick *>(data);
+  hsa_device_type_t dev = HSA_DEVICE_TYPE_GPU;
+  if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
+      HSA_STATUS_SUCCESS)
+    return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_CPU)
+    return HSA_STATUS_SUCCESS;
+  out->agent = agent;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+struct HsaAmdAgentPick {
+  hsa_agent_t agent;
+};
+
+inline void hsa_amd_test_agent_pick_init(HsaAmdAgentPick *out) {
+  out->agent.handle = 0;
+}
+
+inline hsa_status_t hsa_amd_test_pick_first_agent_cb(hsa_agent_t agent,
+                                                     void *data) {
+  auto *out = static_cast<HsaAmdAgentPick *>(data);
+  out->agent = agent;
+  return HSA_STATUS_INFO_BREAK;
+}
+
+#endif // COMPILER_RT_ASAN_AMDGPU_HSA_AMD_TEST_HELPERS_H

>From 05295ce57878614be5ffc1f9e6480e29b7a99bdf Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Thu, 14 May 2026 14:19:47 +0530
Subject: [PATCH 21/46] Address Comments of @bing-ma.

  - Add same reserved va/size check validation logic at the time of
    freeing VMEM memory.

  - Ensure ASan HSA interceptors never return `HSA_STATUS_SUCCESS` when
    allocation yields a null pointer through following cases like early
    device runtime shutdown, failed device runtime initialization,
    device allocator size overflow, or unsuccessful device allocation
    without an updated HSA status).

  - ROCr can return from hsa_amd_ipc_memory_detach before the import is
    fully torn down (e.g. thunk_bo when hsaKmtMemoryVaUnmap fails).
    Defer PoisonShadow and FlushUnneededASanShadowMemory calls until
    detach succeeds so synthetic redzones remain while the mapping may
    still be live.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 26 +++++++++++++++----
 .../sanitizer_allocator_amdgpu.cpp            |  6 +++--
 .../sanitizer_allocator_amdgpu.h              |  6 +++++
 .../sanitizer_allocator_device.h              | 15 +++++++++--
 4 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 4acc456a244ca..1fbbbb0fded2d 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1615,16 +1615,25 @@ hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr) {
   static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
   uptr mapped_base = reinterpret_cast<uptr>(mapped_ptr) - kPageSize_;
 
+  // Snapshot mapping size before detach: ROCr may return an error while the
+  // import is still live (e.g. thunk_bo path if hsaKmtMemoryVaUnmap fails).
+  // Only clear ASan shadow after a successful detach so redzones stay valid
+  // if teardown did not complete.
   hsa_amd_pointer_info_t info;
   info.size = sizeof(hsa_amd_pointer_info_t);
+  uptr mapped_sz = 0;
   if (REAL(hsa_amd_pointer_info)(reinterpret_cast<void*>(mapped_base), &info,
                                  nullptr, nullptr,
-                                 nullptr) == HSA_STATUS_SUCCESS) {
-    PoisonShadow(mapped_base, info.sizeInBytes, 0);
-    FlushUnneededASanShadowMemory(mapped_base, info.sizeInBytes);
-  }
+                                 nullptr) == HSA_STATUS_SUCCESS)
+    mapped_sz = static_cast<uptr>(info.sizeInBytes);
 
-  return REAL(hsa_amd_ipc_memory_detach)(reinterpret_cast<void*>(mapped_base));
+  const hsa_status_t status =
+      REAL(hsa_amd_ipc_memory_detach)(reinterpret_cast<void*>(mapped_base));
+  if (status == HSA_STATUS_SUCCESS && mapped_sz) {
+    PoisonShadow(mapped_base, mapped_sz, 0);
+    FlushUnneededASanShadowMemory(mapped_base, mapped_sz);
+  }
+  return status;
 }
 
 hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
@@ -1675,6 +1684,13 @@ hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
 
   void* p = get_allocator().GetBlockBegin(ptr);
   if (p) {
+    // Match ROCr: require the same va/size as reserve before freeing metadata.
+    uptr ptr_ = reinterpret_cast<uptr>(ptr);
+    AsanChunk* m = reinterpret_cast<AsanChunk*>(ptr_ - kChunkHeaderSize);
+    if (m->Beg() != ptr_ || size != m->UsedSize()) {
+      errno = errno_EINVAL;
+      return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+    }
     instance.Deallocate(ptr, 0, 0, stack, FROM_MALLOC);
     return HSA_STATUS_SUCCESS;
   }
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 3597db7c9d1bf..87db693476abd 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -99,17 +99,19 @@ bool AmdgpuMemFuncs::Init() {
 
 void* AmdgpuMemFuncs::Allocate(uptr size, uptr alignment,
                                DeviceAllocationInfo* da_info) {
+  AmdgpuAllocationInfo* aa_info =
+      reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
+
   // Do not allocate if AMDGPU runtime is shutdown
   if (UNLIKELY(IsAmdgpuRuntimeShutdown())) {
     VReport(1,
             "Amdgpu Allocate: Runtime shutdown, skipping allocation for size "
             "%zu alignment %zu\n",
             size, alignment);
+    aa_info->EnsureFailureStatus(HSA_STATUS_ERROR_INVALID_RUNTIME_STATE);
     return nullptr;
   }
 
-  AmdgpuAllocationInfo* aa_info =
-      reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
   if (!aa_info->memory_pool.handle) {
     aa_info->status = hsa_amd.vmem_address_reserve_align(
         &aa_info->ptr, size, aa_info->address, aa_info->alignment,
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index ff77911efb546..0f11b7a343d0a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -35,6 +35,12 @@ struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
     status = HSA_STATUS_SUCCESS;
     alloc_func = nullptr;
   }
+  // If allocation fails without an HSA API status, record one so callers never
+  // see *ptr == nullptr with status still SUCCESS.
+  void EnsureFailureStatus(hsa_status_t err) {
+    if (status == HSA_STATUS_SUCCESS)
+      status = err;
+  }
   hsa_status_t status;
   void* alloc_func;
   hsa_amd_memory_pool_t memory_pool;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index c6a79760e902a..eb29ed7bde0f1 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -57,8 +57,12 @@ class DeviceAllocatorT {
 
   void* Allocate(AllocatorStats* stat, uptr size, uptr alignment,
                  DeviceAllocationInfo* da_info) {
-    if (!da_info || !InitMemFuncs())
+    if (!da_info || !InitMemFuncs()) {
+      if (da_info && da_info->type_ == DAT_AMDGPU)
+        reinterpret_cast<AmdgpuAllocationInfo*>(da_info)->EnsureFailureStatus(
+            HSA_STATUS_ERROR_NOT_INITIALIZED);
       return nullptr;
+    }
 
     // Allocate an extra page for Metadata
     if (kMetadataSize_ + (size % page_size_) > page_size_) {
@@ -74,11 +78,18 @@ class DeviceAllocatorT {
           "WARNING: %s: DeviceAllocator allocation overflow: "
           "0x%zx bytes with 0x%zx alignment requested\n",
           SanitizerToolName, map_size, alignment);
+      if (da_info->type_ == DAT_AMDGPU)
+        reinterpret_cast<AmdgpuAllocationInfo*>(da_info)->EnsureFailureStatus(
+            HSA_STATUS_ERROR_OUT_OF_RESOURCES);
       return nullptr;
     }
     void* ptr = DeviceMemFuncs::Allocate(map_size, alignment, da_info);
-    if (!ptr)
+    if (!ptr) {
+      if (da_info->type_ == DAT_AMDGPU)
+        reinterpret_cast<AmdgpuAllocationInfo*>(da_info)->EnsureFailureStatus(
+            HSA_STATUS_ERROR_OUT_OF_RESOURCES);
       return nullptr;
+    }
     uptr map_beg = reinterpret_cast<uptr>(ptr);
     CHECK(IsAligned(map_beg, page_size_));
     MapUnmapCallback().OnMap(map_beg, map_size);

>From 77c40a09e12c7fc12dc00bd69ecbd5de9131f9dd Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 18 May 2026 13:58:48 +0530
Subject: [PATCH 22/46] [compiler-rt][asan] Fix asan_hsa_amd_pointer_info query
 for asan tracked vmem memory.

`hsa_amd_pointer_info()` used a fixed +page shift from the device map
base to the user-visible pointer. That only matches the IPC-attach
layout, not intercepted pool/vmem allocations where the gap can include
alignment padding and ASan redzones (e.g. reserve_align with alignment >
page size).

Query ROCr on the reservation base (map_beg), then adjust reported bases
by the actual offset from the live user chunk. Passthrough to the real
API when the pointer is not tracked or is not the allocated user base.

Add lit tests for pointer_info after 64 KiB-aligned vmem reserve and for
vmem double-free diagnosis.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 56 ++++++++-------
 ...sa_amd_pointer_info_vmem_reserve_align.cpp | 72 +++++++++++++++++++
 ...vmem_address_reserve_align_double_free.cpp | 47 ++++++++++++
 3 files changed, 150 insertions(+), 25 deletions(-)
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 1fbbbb0fded2d..66dcb65890bb3 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1547,9 +1547,6 @@ hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
                                            p ? p : ptr);
 }
 
-// For asan allocator, kMetadataSize is 0 and maximum redzone size is 2048. This
-// implies for device allocation, the gap between user_beg and GetBlockBegin()
-// is always one kPageSize_
 // IPC calls use static_assert to make sure kMetadataSize = 0
 //
 #  if SANITIZER_CAN_USE_ALLOCATOR64
@@ -1702,29 +1699,38 @@ hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
                                        void* (*alloc)(size_t),
                                        uint32_t* num_agents_accessible,
                                        hsa_agent_t** accessible) {
-  void* ptr_ = get_allocator().GetBlockBegin(ptr);
-  AsanChunk* m = ptr_
-                     ? instance.GetAsanChunkByAddr(reinterpret_cast<uptr>(ptr_))
-                     : nullptr;
-  if (ptr_ && m) {
-    hsa_status_t status = REAL(hsa_amd_pointer_info)(
-        ptr_, info, alloc, num_agents_accessible, accessible);
-    if (status == HSA_STATUS_SUCCESS && info) {
-      static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
-      // Adjust base address of agent,host and sizeInBytes so as to return
-      // the actual pointer information of user allocation rather than asan
-      // allocation. Asan allocation pointer info can be acquired using internal
-      // 'GetPointerInfo'
-      info->agentBaseAddress = reinterpret_cast<void*>(
-          reinterpret_cast<uptr>(info->agentBaseAddress) + kPageSize_);
-      info->hostBaseAddress = reinterpret_cast<void*>(
-          reinterpret_cast<uptr>(info->hostBaseAddress) + kPageSize_);
-      info->sizeInBytes = m->UsedSize();
-    }
-    return status;
+  // Device/pool mappings are keyed by the ROCr reservation base (map_beg), not
+  // the user pointer returned from intercepted allocate/reserve.
+  void* hsa_map_base = get_allocator().GetBlockBegin(ptr);
+  if (!hsa_map_base)
+    // Not tracked by ASan; query ROCr on the caller's pointer unchanged.
+    return REAL(hsa_amd_pointer_info)(ptr, info, alloc, num_agents_accessible,
+                                      accessible);
+
+  uptr user = reinterpret_cast<uptr>(ptr);
+  AsanChunk* m = reinterpret_cast<AsanChunk*>(user - kChunkHeaderSize);
+  if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED ||
+      m->Beg() != user)
+    // Inside a tracked mapping but not the live user base (redzone, interior,
+    // or freed); do not apply the user-visible pointer_info adjustment.
+    return REAL(hsa_amd_pointer_info)(ptr, info, alloc, num_agents_accessible,
+                                      accessible);
+
+  hsa_status_t status = REAL(hsa_amd_pointer_info)(
+      hsa_map_base, info, alloc, num_agents_accessible, accessible);
+  if (status == HSA_STATUS_SUCCESS && info) {
+    static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
+    // User VA may be above hsa_map_base when VMem API's(i.e
+    // hsa_amd_vmem_address_reserve_align())  uses alignment > page size (device
+    // allocator padding and ASan redzones/headers).
+    const uptr offset = user - reinterpret_cast<uptr>(hsa_map_base);
+    info->agentBaseAddress = reinterpret_cast<void*>(
+        reinterpret_cast<uptr>(info->agentBaseAddress) + offset);
+    info->hostBaseAddress = reinterpret_cast<void*>(
+        reinterpret_cast<uptr>(info->hostBaseAddress) + offset);
+    info->sizeInBytes = m->UsedSize();
   }
-  return REAL(hsa_amd_pointer_info)(ptr, info, alloc, num_agents_accessible,
-                                    accessible);
+  return status;
 }
 
 hsa_status_t asan_hsa_init() {
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
new file mode 100644
index 0000000000000..06c09d041dd67
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -0,0 +1,72 @@
+// XFAIL: *
+//
+//
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %run %t 2>&1 | FileCheck %s
+//
+// hsa_amd_pointer_info on vmem reserved via reserve_align() must report the
+// user-visible base and size, not the internal HSA backing mapping. A fixed
+// +page offset is wrong when alignment is larger than the page size.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include "hsa_amd_test_helpers.h"
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+int main() {
+  if (hsa_amd_test_require_init())
+    return 1;
+
+  const size_t kSize = 4096;
+  const uint64_t kAlign = 65536;
+  void *mem = nullptr;
+  if (hsa_amd_vmem_address_reserve_align(&mem, kSize, /*address=*/0, kAlign,
+                                         /*flags=*/0) != HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
+    return 1;
+  }
+
+  const uintptr_t user = reinterpret_cast<uintptr_t>(mem);
+  if (user % kAlign != 0) {
+    fprintf(stderr, "reserved address not %" PRIu64 "-byte aligned\n", kAlign);
+    return 1;
+  }
+
+  hsa_amd_pointer_info_t info = {};
+  info.size = sizeof(hsa_amd_pointer_info_t);
+  if (hsa_amd_pointer_info(mem, &info, nullptr, nullptr, nullptr) !=
+      HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_pointer_info failed\n");
+    return 1;
+  }
+
+  if (info.agentBaseAddress != mem ||
+      reinterpret_cast<uintptr_t>(info.agentBaseAddress) + info.sizeInBytes !=
+          user + kSize) {
+    fprintf(stderr,
+            "pointer_info mismatch: user=%p begin=%p size=%zu (expected %zu)\n",
+            mem, info.agentBaseAddress, info.sizeInBytes, kSize);
+    return 1;
+  }
+
+  printf("pointer_info_vmem sizeInBytes: %zu\n", info.sizeInBytes);
+  printf("pointer_info_vmem begin: %p\n", info.agentBaseAddress);
+
+  if (hsa_amd_vmem_address_free(mem, kSize) != HSA_STATUS_SUCCESS) {
+    fprintf(stderr, "hsa_amd_vmem_address_free failed\n");
+    return 1;
+  }
+  return 0;
+}
+
+// CHECK: pointer_info_vmem sizeInBytes: 4096
+// CHECK-NEXT: pointer_info_vmem begin: 0x{{[0-9a-f]+}}
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
new file mode 100644
index 0000000000000..038fec02f7be3
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -0,0 +1,47 @@
+// XFAIL: *
+//
+//
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
+// hsa_amd_vmem_address_free interceptors: freeing the same reserved range twice
+// is diagnosed.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include "hsa_amd_test_helpers.h"
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+int main() {
+  if (hsa_amd_test_require_init())
+    return 1;
+
+  // Size must be a non-zero multiple of the page size; address must be 0 so
+  // the interceptor records the reservation for double-free diagnosis (see
+  // asan_hsa_amd_vmem_address_reserve_align).
+  const size_t kSize = 4096;
+  void *mem = nullptr;
+  if (hsa_amd_vmem_address_reserve_align(&mem, kSize, /*address=*/0,
+                                         /*alignment=*/4096,
+                                         /*flags=*/0) != HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
+    return 1;
+  }
+
+  (void)hsa_amd_vmem_address_free(mem, kSize);
+  (void)hsa_amd_vmem_address_free(mem, kSize);
+
+  fprintf(stderr, "expected double-free report\n");
+  return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer: attempting double-free
+// CHECK: SUMMARY: AddressSanitizer: double-free

>From 4279353d9692947927930da5dd8be23862268412 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 18 May 2026 16:34:06 +0530
Subject: [PATCH 23/46] [compiler-rt] Move device allocator HSA failure status
 into AMDGPU layer

DeviceAllocatorT failure paths in sanitizer_allocator_device.h recorded
HSA_STATUS_* via AmdgpuAllocationInfo casts, which tied the generic
device allocator header to ROCr/HSA types.

Add AmdgpuMemFuncs::NoteDeviceAllocatorFailure() and call it from the
device allocator on init failure, size overflow, and null allocation.
Map failure kinds to hsa_status_t in sanitizer_allocator_amdgpu.cpp
only.
---
 .../sanitizer_allocator_amdgpu.cpp               | 16 ++++++++++++++++
 .../sanitizer_allocator_amdgpu.h                 |  9 +++++++++
 .../sanitizer_allocator_device.h                 | 16 +++++++---------
 3 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 87db693476abd..f1aab70cf189b 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -75,6 +75,22 @@ void AmdgpuMemFuncs::ClearAmdgpuRuntimeShutdownState() {
   atomic_store(&amdgpu_event_registered, 0, memory_order_release);
 }
 
+void AmdgpuMemFuncs::NoteDeviceAllocatorFailure(DeviceAllocationInfo* da_info,
+                                                DeviceAllocFailure failure) {
+  if (!da_info || da_info->type_ != DAT_AMDGPU)
+    return;
+  AmdgpuAllocationInfo* aa_info =
+      reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
+  switch (failure) {
+    case kNotInitialized:
+      aa_info->EnsureFailureStatus(HSA_STATUS_ERROR_NOT_INITIALIZED);
+      break;
+    case kOutOfResources:
+      aa_info->EnsureFailureStatus(HSA_STATUS_ERROR_OUT_OF_RESOURCES);
+      break;
+  }
+}
+
 bool AmdgpuMemFuncs::Init() {
   bool success = true;
   LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.memory_pool_allocate,
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index 0f11b7a343d0a..884b6b30d7459 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -16,6 +16,11 @@
 #if SANITIZER_AMDGPU
 class AmdgpuMemFuncs {
  public:
+  enum DeviceAllocFailure {
+    kNotInitialized,
+    kOutOfResources,
+  };
+
   static bool Init();
   static void* Allocate(uptr size, uptr alignment,
                         DeviceAllocationInfo* da_info);
@@ -25,6 +30,10 @@ class AmdgpuMemFuncs {
   static void RegisterSystemEventHandlers();
   static bool IsAmdgpuRuntimeShutdown();
   static void ClearAmdgpuRuntimeShutdownState();
+  // Record an HSA error on da_info when DeviceAllocatorT fails before/without a
+  // successful AmdgpuMemFuncs::Allocate (keeps HSA types out of device.h).
+  static void NoteDeviceAllocatorFailure(DeviceAllocationInfo* da_info,
+                                         DeviceAllocFailure failure);
 
  private:
   static void NotifyAmdgpuRuntimeShutdown();
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index eb29ed7bde0f1..d1a0981e32fa1 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -58,9 +58,9 @@ class DeviceAllocatorT {
   void* Allocate(AllocatorStats* stat, uptr size, uptr alignment,
                  DeviceAllocationInfo* da_info) {
     if (!da_info || !InitMemFuncs()) {
-      if (da_info && da_info->type_ == DAT_AMDGPU)
-        reinterpret_cast<AmdgpuAllocationInfo*>(da_info)->EnsureFailureStatus(
-            HSA_STATUS_ERROR_NOT_INITIALIZED);
+      if (da_info)
+        DeviceMemFuncs::NoteDeviceAllocatorFailure(
+            da_info, DeviceMemFuncs::kNotInitialized);
       return nullptr;
     }
 
@@ -78,16 +78,14 @@ class DeviceAllocatorT {
           "WARNING: %s: DeviceAllocator allocation overflow: "
           "0x%zx bytes with 0x%zx alignment requested\n",
           SanitizerToolName, map_size, alignment);
-      if (da_info->type_ == DAT_AMDGPU)
-        reinterpret_cast<AmdgpuAllocationInfo*>(da_info)->EnsureFailureStatus(
-            HSA_STATUS_ERROR_OUT_OF_RESOURCES);
+      DeviceMemFuncs::NoteDeviceAllocatorFailure(
+          da_info, DeviceMemFuncs::kOutOfResources);
       return nullptr;
     }
     void* ptr = DeviceMemFuncs::Allocate(map_size, alignment, da_info);
     if (!ptr) {
-      if (da_info->type_ == DAT_AMDGPU)
-        reinterpret_cast<AmdgpuAllocationInfo*>(da_info)->EnsureFailureStatus(
-            HSA_STATUS_ERROR_OUT_OF_RESOURCES);
+      DeviceMemFuncs::NoteDeviceAllocatorFailure(
+          da_info, DeviceMemFuncs::kOutOfResources);
       return nullptr;
     }
     uptr map_beg = reinterpret_cast<uptr>(ptr);

>From 9e4f2b07e91fb10f1cbff44a762527b83d7070c5 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Thu, 21 May 2026 12:16:51 +0530
Subject: [PATCH 24/46] [compiler-rt] Decouple DeviceAllocatorT from AMDGPU/HSA
 backend.

Make DeviceAllocatorT vendor-neutral with a pluggable DeviceAllocator
backend (NoOpDeviceAllocator / AmdgpuDeviceAllocator).  Always wire the
device layer through CombinedAllocator; keep HSA types and ROCr dlsym in
the AMDGPU backend only.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 21 ++---
 .../sanitizer_common/sanitizer_allocator.h    |  3 +
 .../sanitizer_allocator_amdgpu.cpp            | 42 ++++-----
 .../sanitizer_allocator_amdgpu.h              | 28 +++---
 .../sanitizer_allocator_combined.h            | 47 +---------
 .../sanitizer_allocator_device.h              | 94 +++++++++++++------
 6 files changed, 117 insertions(+), 118 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 66dcb65890bb3..24b22035a10fb 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -393,14 +393,11 @@ struct Allocator {
 
   void InitLinkerInitialized(const AllocatorOptions& options) {
     SetAllocatorMayReturnNull(options.may_return_null);
-#if SANITIZER_AMDGPU
-    // Device-backed HSA allocations (e.g. hsa_amd_memory_pool_allocate) use
-    // CombinedAllocator's device path; it must be enabled for InitMemFuncs/
-    // AmdgpuMemFuncs::Init to run at startup.
-    allocator.InitLinkerInitialized(options.release_to_os_interval_ms, 0, true);
-#else
-    allocator.InitLinkerInitialized(options.release_to_os_interval_ms);
-#endif
+    // Device-backed allocations use CombinedAllocator's device path. On
+    // SANITIZER_AMDGPU builds, enable it so InitMemFuncs() /
+    // AmdgpuDeviceAllocator::Init() run at startup.
+    allocator.InitLinkerInitialized(options.release_to_os_interval_ms, 0,
+                                    SANITIZER_AMDGPU);
     SharedInitCode(options);
     max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
                                        ? common_flags()->max_allocation_size_mb
@@ -1738,12 +1735,12 @@ hsa_status_t asan_hsa_init() {
   if (status == HSA_STATUS_SUCCESS) {
     // Only clear state when recovering from a prior shutdown (avoids clearing
     // amdgpu_event_registered on every refcount bump and re-registering).
-    if (__sanitizer::AmdgpuMemFuncs::IsAmdgpuRuntimeShutdown())
-      __sanitizer::AmdgpuMemFuncs::ClearAmdgpuRuntimeShutdownState();
+    if (__sanitizer::AmdgpuDeviceAllocator::IsRuntimeShutdown())
+      __sanitizer::AmdgpuDeviceAllocator::ClearRuntimeShutdownState();
     // Load HSA entry points once the runtime is up; device allocator may stay
     // disabled, but interceptors and RegisterSystemEventHandlers need them.
-    if (__sanitizer::AmdgpuMemFuncs::Init())
-      __sanitizer::AmdgpuMemFuncs::RegisterSystemEventHandlers();
+    if (__sanitizer::AmdgpuDeviceAllocator::Init())
+      __sanitizer::AmdgpuDeviceAllocator::RegisterSystemEventHandlers();
   }
   return status;
 }
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index d803444f69dd6..726ef132d0e40 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -88,6 +88,9 @@ struct NoOpMapUnmapCallback {
 #include "sanitizer_allocator_local_cache.h"
 #include "sanitizer_allocator_secondary.h"
 #include "sanitizer_allocator_device.h"
+#if SANITIZER_AMDGPU
+#  include "sanitizer_allocator_amdgpu.h"
+#endif
 #include "sanitizer_allocator_combined.h"
 // clang-format on
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index f1aab70cf189b..461ad033cac9f 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -51,14 +51,12 @@ static atomic_uint8_t amdgpu_event_registered{0};
       success = false;                                                \
     }
 
-// Check AMDGPU runtime shutdown state
-bool AmdgpuMemFuncs::IsAmdgpuRuntimeShutdown() {
+bool AmdgpuDeviceAllocator::IsRuntimeShutdown() {
   return static_cast<bool>(
       atomic_load(&amdgpu_runtime_shutdown, memory_order_acquire));
 }
 
-// Notify AMDGPU runtime shutdown to allocator
-void AmdgpuMemFuncs::NotifyAmdgpuRuntimeShutdown() {
+void AmdgpuDeviceAllocator::NotifyRuntimeShutdown() {
   uint8_t shutdown = 0;
   if (atomic_compare_exchange_strong(&amdgpu_runtime_shutdown, &shutdown, 1,
                                      memory_order_acq_rel)) {
@@ -70,28 +68,28 @@ void AmdgpuMemFuncs::NotifyAmdgpuRuntimeShutdown() {
 // Resets amdgpu_runtime_shutdown so allocator operations are enabled, and
 // amdgpu_event_registered so RegisterSystemEventHandlers() will register the
 // shutdown callback for the new runtime instance.
-void AmdgpuMemFuncs::ClearAmdgpuRuntimeShutdownState() {
+void AmdgpuDeviceAllocator::ClearRuntimeShutdownState() {
   atomic_store(&amdgpu_runtime_shutdown, 0, memory_order_release);
   atomic_store(&amdgpu_event_registered, 0, memory_order_release);
 }
 
-void AmdgpuMemFuncs::NoteDeviceAllocatorFailure(DeviceAllocationInfo* da_info,
-                                                DeviceAllocFailure failure) {
+void AmdgpuDeviceAllocator::NoteDeviceAllocatorFailure(
+    DeviceAllocationInfo* da_info, DeviceAllocFailure failure) {
   if (!da_info || da_info->type_ != DAT_AMDGPU)
     return;
   AmdgpuAllocationInfo* aa_info =
       reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
   switch (failure) {
-    case kNotInitialized:
+    case DEV_ALLOC_FAILURE_NOT_INITIALIZED:
       aa_info->EnsureFailureStatus(HSA_STATUS_ERROR_NOT_INITIALIZED);
       break;
-    case kOutOfResources:
+    case DEV_ALLOC_FAILURE_OUT_OF_RESOURCES:
       aa_info->EnsureFailureStatus(HSA_STATUS_ERROR_OUT_OF_RESOURCES);
       break;
   }
 }
 
-bool AmdgpuMemFuncs::Init() {
+bool AmdgpuDeviceAllocator::Init() {
   bool success = true;
   LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.memory_pool_allocate,
                                  "hsa_amd_memory_pool_allocate", success);
@@ -113,13 +111,13 @@ bool AmdgpuMemFuncs::Init() {
   return true;
 }
 
-void* AmdgpuMemFuncs::Allocate(uptr size, uptr alignment,
-                               DeviceAllocationInfo* da_info) {
+void* AmdgpuDeviceAllocator::Allocate(uptr size, uptr alignment,
+                                      DeviceAllocationInfo* da_info) {
   AmdgpuAllocationInfo* aa_info =
       reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
 
   // Do not allocate if AMDGPU runtime is shutdown
-  if (UNLIKELY(IsAmdgpuRuntimeShutdown())) {
+  if (UNLIKELY(IsRuntimeShutdown())) {
     VReport(1,
             "Amdgpu Allocate: Runtime shutdown, skipping allocation for size "
             "%zu alignment %zu\n",
@@ -142,9 +140,9 @@ void* AmdgpuMemFuncs::Allocate(uptr size, uptr alignment,
   return aa_info->ptr;
 }
 
-void AmdgpuMemFuncs::Deallocate(void* p) {
+void AmdgpuDeviceAllocator::Deallocate(void* p) {
   // Deallocate does nothing after AMDGPU runtime shutdown
-  if (UNLIKELY(IsAmdgpuRuntimeShutdown())) {
+  if (UNLIKELY(IsRuntimeShutdown())) {
     VReport(
         1,
         "Amdgpu Deallocate: Runtime shutdown, skipping deallocation for %p\n",
@@ -153,7 +151,8 @@ void AmdgpuMemFuncs::Deallocate(void* p) {
   }
 
   DevicePointerInfo DevPtrInfo;
-  if (AmdgpuMemFuncs::GetPointerInfo(reinterpret_cast<uptr>(p), &DevPtrInfo)) {
+  if (AmdgpuDeviceAllocator::GetPointerInfo(reinterpret_cast<uptr>(p),
+                                            &DevPtrInfo)) {
     if (DevPtrInfo.type == HSA_EXT_POINTER_TYPE_HSA) {
       UNUSED hsa_status_t status = hsa_amd.memory_pool_free(p);
     } else if (DevPtrInfo.type == HSA_EXT_POINTER_TYPE_RESERVED_ADDR) {
@@ -163,9 +162,10 @@ void AmdgpuMemFuncs::Deallocate(void* p) {
   }
 }
 
-bool AmdgpuMemFuncs::GetPointerInfo(uptr ptr, DevicePointerInfo* ptr_info) {
+bool AmdgpuDeviceAllocator::GetPointerInfo(uptr ptr,
+                                           DevicePointerInfo* ptr_info) {
   // GetPointerInfo returns false after AMDGPU runtime shutdown
-  if (UNLIKELY(IsAmdgpuRuntimeShutdown())) {
+  if (UNLIKELY(IsRuntimeShutdown())) {
     VReport(1,
             "Amdgpu GetPointerInfo: Runtime shutdown, skipping query for %p\n",
             reinterpret_cast<void*>(ptr));
@@ -191,7 +191,7 @@ bool AmdgpuMemFuncs::GetPointerInfo(uptr ptr, DevicePointerInfo* ptr_info) {
 }
 // Register shutdown system event handler only once
 // TODO: Register multiple event handlers if needed in future
-void AmdgpuMemFuncs::RegisterSystemEventHandlers() {
+void AmdgpuDeviceAllocator::RegisterSystemEventHandlers() {
   uint8_t registered = 0;
   // Check if shutdown event handler is already registered
   if (atomic_compare_exchange_strong(&amdgpu_event_registered, &registered, 1,
@@ -202,7 +202,7 @@ void AmdgpuMemFuncs::RegisterSystemEventHandlers() {
       if (!event)
         return HSA_STATUS_ERROR_INVALID_ARGUMENT;
       if (event->event_type == HSA_AMD_SYSTEM_SHUTDOWN_EVENT)
-        AmdgpuMemFuncs::NotifyAmdgpuRuntimeShutdown();
+        AmdgpuDeviceAllocator::NotifyRuntimeShutdown();
       return HSA_STATUS_SUCCESS;
     };
     // Register the event callback
@@ -222,6 +222,6 @@ void AmdgpuMemFuncs::RegisterSystemEventHandlers() {
   }
 }
 
-uptr AmdgpuMemFuncs::GetPageSize() { return kPageSize_; }
+uptr AmdgpuDeviceAllocator::GetPageSize() { return kPageSize_; }
 }  // namespace __sanitizer
 #endif  // SANITIZER_AMDGPU
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index 884b6b30d7459..efc6c8f869e3b 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -6,21 +6,20 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Part of the Sanitizer Allocator.
+// AMDGPU / HSA device memory backend for DeviceAllocatorT.
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#  error This file must be included inside sanitizer_allocator_device.h
+#  error This file must be included inside sanitizer_allocator.h
 #endif
 
 #if SANITIZER_AMDGPU
-class AmdgpuMemFuncs {
- public:
-  enum DeviceAllocFailure {
-    kNotInitialized,
-    kOutOfResources,
-  };
 
+static const DeviceAllocationType DAT_AMDGPU =
+    static_cast<DeviceAllocationType>(1);
+
+class AmdgpuDeviceAllocator {
+ public:
   static bool Init();
   static void* Allocate(uptr size, uptr alignment,
                         DeviceAllocationInfo* da_info);
@@ -28,15 +27,13 @@ class AmdgpuMemFuncs {
   static bool GetPointerInfo(uptr ptr, DevicePointerInfo* ptr_info);
   static uptr GetPageSize();
   static void RegisterSystemEventHandlers();
-  static bool IsAmdgpuRuntimeShutdown();
-  static void ClearAmdgpuRuntimeShutdownState();
-  // Record an HSA error on da_info when DeviceAllocatorT fails before/without a
-  // successful AmdgpuMemFuncs::Allocate (keeps HSA types out of device.h).
+  static bool IsRuntimeShutdown();
+  static void ClearRuntimeShutdownState();
   static void NoteDeviceAllocatorFailure(DeviceAllocationInfo* da_info,
                                          DeviceAllocFailure failure);
 
  private:
-  static void NotifyAmdgpuRuntimeShutdown();
+  static void NotifyRuntimeShutdown();
 };
 
 struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
@@ -60,4 +57,9 @@ struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
   u32 flags;
   void* ptr;
 };
+
+template <class MapUnmapCallback>
+using DefaultDeviceAllocator =
+    DeviceAllocatorT<MapUnmapCallback, AmdgpuDeviceAllocator>;
+
 #endif  // SANITIZER_AMDGPU
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
index 1397959b66679..fc2a2e5b50c8f 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -28,19 +28,14 @@ class CombinedAllocator {
       LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback,
                          LargeMmapAllocatorPtrArray,
                          typename PrimaryAllocator::AddressSpaceView>;
-
-#if SANITIZER_AMDGPU
   using DeviceAllocator =
-      DeviceAllocatorT<typename PrimaryAllocator::MapUnmapCallback>;
-#endif
+      DefaultDeviceAllocator<typename PrimaryAllocator::MapUnmapCallback>;
 
   void InitLinkerInitialized(s32 release_to_os_interval_ms, uptr heap_start = 0,
                              bool enable_device_allocator = false) {
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.InitLinkerInitialized();
-#if SANITIZER_AMDGPU
     device_.Init(enable_device_allocator, primary_.kMetadataSize);
-#endif
   }
 
   void Init(s32 release_to_os_interval_ms, uptr heap_start = 0,
@@ -48,9 +43,7 @@ class CombinedAllocator {
     stats_.Init();
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.Init();
-#if SANITIZER_AMDGPU
     device_.Init(enable_device_allocator, primary_.kMetadataSize);
-#endif
   }
 
   void* Allocate(AllocatorCache* cache, uptr size, uptr alignment,
@@ -77,13 +70,10 @@ class CombinedAllocator {
     // using a non-fixed base address). The secondary takes care of the
     // alignment without such requirement, and allocating 'size' would use
     // extraneous memory, so we employ 'original_size'.
-    void *res;
-#if SANITIZER_AMDGPU
+    void* res;
     if (da_info)
       res = device_.Allocate(&stats_, original_size, alignment, da_info);
-    else
-#endif
-        if (primary_.CanAllocate(size, alignment))
+    else if (primary_.CanAllocate(size, alignment))
       res = cache->Allocate(&primary_, primary_.ClassID(size));
     else
       res = secondary_.Allocate(&stats_, original_size, alignment);
@@ -110,10 +100,8 @@ class CombinedAllocator {
       cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
     else if (secondary_.PointerIsMine(p))
       secondary_.Deallocate(&stats_, p);
-#if SANITIZER_AMDGPU
     else if (device_.PointerIsMine(p))
       device_.Deallocate(&stats_, p);
-#endif
   }
 
   void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
@@ -139,10 +127,8 @@ class CombinedAllocator {
       return true;
     if (secondary_.PointerIsMine(p))
       return true;
-#if SANITIZER_AMDGPU
     if (device_.PointerIsMine(p))
       return true;
-#endif
     return false;
   }
 
@@ -153,10 +139,8 @@ class CombinedAllocator {
       return primary_.GetMetaData(p);
     if (secondary_.PointerIsMine(p))
       return secondary_.GetMetaData(p);
-#if SANITIZER_AMDGPU
     if (device_.PointerIsMine(p))
       return device_.GetMetaData(p);
-#endif
     return nullptr;
   }
 
@@ -168,10 +152,8 @@ class CombinedAllocator {
       return primary_.GetMetaData(p);
     if (secondary_.GetBlockBeginFastLocked(p))
       return secondary_.GetMetaData(p);
-#if SANITIZER_AMDGPU
     if (device_.GetBlockBeginFastLocked(p))
       return device_.GetMetaData(p);
-#endif
     return nullptr;
   }
 
@@ -180,10 +162,8 @@ class CombinedAllocator {
       return primary_.GetBlockBegin(p);
     if (secondary_.PointerIsMine(p))
       return secondary_.GetBlockBegin(p);
-#if SANITIZER_AMDGPU
     if (device_.PointerIsMine(p))
       return device_.GetBlockBegin(p);
-#endif
     return nullptr;
   }
 
@@ -195,10 +175,8 @@ class CombinedAllocator {
       return primary_.GetBlockBegin(p);
     if ((beg = secondary_.GetBlockBeginFastLocked(p)))
       return beg;
-#if SANITIZER_AMDGPU
     if ((beg = device_.GetBlockBeginFastLocked(p)))
       return beg;
-#endif
     return nullptr;
   }
 
@@ -207,19 +185,14 @@ class CombinedAllocator {
       return primary_.GetActuallyAllocatedSize(p);
     if (secondary_.PointerIsMine(p))
       return secondary_.GetActuallyAllocatedSize(p);
-#if SANITIZER_AMDGPU
     if (device_.PointerIsMine(p))
       return device_.GetActuallyAllocatedSize(p);
-#endif
     return 0;
   }
 
   uptr TotalMemoryUsed() {
-    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed()
-#if SANITIZER_AMDGPU
-           + device_.TotalMemoryUsed()
-#endif
-        ;
+    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed() +
+           device_.TotalMemoryUsed();
   }
 
   void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
@@ -243,17 +216,13 @@ class CombinedAllocator {
   void PrintStats() {
     primary_.PrintStats();
     secondary_.PrintStats();
-#if SANITIZER_AMDGPU
     device_.PrintStats();
-#endif
   }
 
   // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
   // introspection API.
   void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
-#if SANITIZER_AMDGPU
     device_.ForceLock();
-#endif
     primary_.ForceLock();
     secondary_.ForceLock();
   }
@@ -261,9 +230,7 @@ class CombinedAllocator {
   void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
     secondary_.ForceUnlock();
     primary_.ForceUnlock();
-#if SANITIZER_AMDGPU
     device_.ForceUnlock();
-#endif
   }
 
   // Iterate over all existing chunks.
@@ -271,16 +238,12 @@ class CombinedAllocator {
   void ForEachChunk(ForEachChunkCallback callback, void *arg) {
     primary_.ForEachChunk(callback, arg);
     secondary_.ForEachChunk(callback, arg);
-#if SANITIZER_AMDGPU
     device_.ForEachChunk(callback, arg);
-#endif
   }
 
  private:
   PrimaryAllocator primary_;
   SecondaryAllocator secondary_;
-#if SANITIZER_AMDGPU
   DeviceAllocator device_;
-#endif
   AllocatorGlobalStats stats_;
 };
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index d1a0981e32fa1..00d8a5385ce8d 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -13,21 +13,22 @@
 #  error This file must be included inside sanitizer_allocator.h
 #endif
 
-struct DeviceAllocationInfo;
-#if SANITIZER_AMDGPU
-// Device memory allocation usually requires additional information, we can put
-// all the additional information into a data structure DeviceAllocationInfo.
-// This is only a parent structure since different vendors may require
-// different allocation info.
-typedef enum {
+// Vendor-neutral device heap book-keeping. Runtime access (allocate, pointer
+// queries, deallocate etc.) is provided by a DeviceAllocator template
+// parameter.
+
+enum DeviceAllocationType {
   DAT_UNKNOWN = 0,
-  DAT_AMDGPU = 1,
-} DeviceAllocationType;
+};
+
+enum DeviceAllocFailure {
+  DEV_ALLOC_FAILURE_NOT_INITIALIZED,
+  DEV_ALLOC_FAILURE_OUT_OF_RESOURCES,
+};
 
 struct DeviceAllocationInfo {
-  DeviceAllocationInfo(DeviceAllocationType type = DAT_UNKNOWN) {
-    type_ = type;
-  }
+  explicit DeviceAllocationInfo(DeviceAllocationType type = DAT_UNKNOWN)
+      : type_(type) {}
   DeviceAllocationType type_;
 };
 
@@ -37,13 +38,41 @@ struct DevicePointerInfo {
   uptr map_size;
 };
 
-#  include "sanitizer_allocator_amdgpu.h"
+struct NoOpDeviceAllocator {
+  static bool Init() { return false; }
 
-template <class MapUnmapCallback = NoOpMapUnmapCallback>
+  static void* Allocate(uptr size, uptr alignment,
+                        DeviceAllocationInfo* da_info) {
+    (void)size;
+    (void)alignment;
+    (void)da_info;
+    return nullptr;
+  }
+
+  static void Deallocate(void* p) { (void)p; }
+
+  static bool GetPointerInfo(uptr ptr, DevicePointerInfo* ptr_info) {
+    (void)ptr;
+    (void)ptr_info;
+    return false;
+  }
+
+  static uptr GetPageSize() { return 4096; }
+
+  static bool IsRuntimeShutdown() { return false; }
+
+  static void NoteDeviceAllocatorFailure(DeviceAllocationInfo* da_info,
+                                         DeviceAllocFailure failure) {
+    (void)da_info;
+    (void)failure;
+  }
+};
+
+template <class MapUnmapCallback = NoOpMapUnmapCallback,
+          class DeviceAllocator = NoOpDeviceAllocator>
 class DeviceAllocatorT {
  public:
   using PtrArrayT = DefaultLargeMmapAllocatorPtrArray;
-  using DeviceMemFuncs = AmdgpuMemFuncs;
 
   void Init(bool enable, uptr kMetadataSize) {
     internal_memset(this, 0, sizeof(*this));
@@ -59,8 +88,8 @@ class DeviceAllocatorT {
                  DeviceAllocationInfo* da_info) {
     if (!da_info || !InitMemFuncs()) {
       if (da_info)
-        DeviceMemFuncs::NoteDeviceAllocatorFailure(
-            da_info, DeviceMemFuncs::kNotInitialized);
+        DeviceAllocator::NoteDeviceAllocatorFailure(
+            da_info, DEV_ALLOC_FAILURE_NOT_INITIALIZED);
       return nullptr;
     }
 
@@ -78,14 +107,14 @@ class DeviceAllocatorT {
           "WARNING: %s: DeviceAllocator allocation overflow: "
           "0x%zx bytes with 0x%zx alignment requested\n",
           SanitizerToolName, map_size, alignment);
-      DeviceMemFuncs::NoteDeviceAllocatorFailure(
-          da_info, DeviceMemFuncs::kOutOfResources);
+      DeviceAllocator::NoteDeviceAllocatorFailure(
+          da_info, DEV_ALLOC_FAILURE_OUT_OF_RESOURCES);
       return nullptr;
     }
-    void* ptr = DeviceMemFuncs::Allocate(map_size, alignment, da_info);
+    void* ptr = DeviceAllocator::Allocate(map_size, alignment, da_info);
     if (!ptr) {
-      DeviceMemFuncs::NoteDeviceAllocatorFailure(
-          da_info, DeviceMemFuncs::kOutOfResources);
+      DeviceAllocator::NoteDeviceAllocatorFailure(
+          da_info, DEV_ALLOC_FAILURE_OUT_OF_RESOURCES);
       return nullptr;
     }
     uptr map_beg = reinterpret_cast<uptr>(ptr);
@@ -139,7 +168,7 @@ class DeviceAllocatorT {
       stat->Sub(AllocatorStatMapped, h->map_size);
     }
     MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
-    DeviceMemFuncs::Deallocate(p);
+    DeviceAllocator::Deallocate(p);
   }
 
   uptr TotalMemoryUsed() {
@@ -302,10 +331,10 @@ class DeviceAllocatorT {
     if (!enabled_ || mem_funcs_inited_ || mem_funcs_init_count_ >= 2) {
       return mem_funcs_inited_;
     }
-    mem_funcs_inited_ = DeviceMemFuncs::Init();
+    mem_funcs_inited_ = DeviceAllocator::Init();
     mem_funcs_init_count_++;
     if (mem_funcs_inited_)
-      page_size_ = DeviceMemFuncs::GetPageSize();
+      page_size_ = DeviceAllocator::GetPageSize();
     return mem_funcs_inited_;
   }
 
@@ -313,7 +342,7 @@ class DeviceAllocatorT {
 
   Header* GetHeaderAnyPointer(uptr p, Header* h) const {
     CHECK(IsAligned(p, page_size_));
-    return DeviceMemFuncs::GetPointerInfo(p, h) ? h : nullptr;
+    return DeviceAllocator::GetPointerInfo(p, h) ? h : nullptr;
   }
 
   Header* GetHeader(uptr chunk, Header* h) const {
@@ -321,12 +350,12 @@ class DeviceAllocatorT {
     // is unloaded, GetPointerInfo() will fail. For such case, we can still
     // return a valid value for map_beg, map_size will be limited to one page
     if (LIKELY(!dev_runtime_unloaded_)) {
-      if (DeviceMemFuncs::GetPointerInfo(chunk, h))
+      if (DeviceAllocator::GetPointerInfo(chunk, h))
         return h;
       // If GetPointerInfo() fails, we don't assume the runtime is unloaded yet.
       // We just return a conservative single-page header. Here mark/check the
       // runtime shutdown state
-      dev_runtime_unloaded_ = DeviceMemFuncs::IsAmdgpuRuntimeShutdown();
+      dev_runtime_unloaded_ = DeviceAllocator::IsRuntimeShutdown();
     }
     // If we reach here, device runtime is unloaded.
     // Fallback: conservative single-page header
@@ -346,7 +375,7 @@ class DeviceAllocatorT {
   mutable bool dev_runtime_unloaded_;
   // Maximum of mem_funcs_init_count_ is 2:
   //   1. The initial init called from Init(...), it could fail if
-  //      libhsa-runtime64.so is dynamically loaded with dlopen()
+  //      the device runtime is dynamically loaded with dlopen()
   //   2. A potential deferred init called by Allocate(...)
   u32 mem_funcs_init_count_;
   uptr kMetadataSize_;
@@ -360,4 +389,9 @@ class DeviceAllocatorT {
   } stats;
   mutable StaticSpinMutex mutex_;
 };
-#endif  // SANITIZER_AMDGPU
+
+#if !SANITIZER_AMDGPU
+template <class MapUnmapCallback>
+using DefaultDeviceAllocator =
+    DeviceAllocatorT<MapUnmapCallback, NoOpDeviceAllocator>;
+#endif

>From 226312c883e8e2d9afe7b5cccf477fe18592d2b1 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 26 May 2026 16:41:36 +0530
Subject: [PATCH 25/46] [ASan][AMDGPU] Fix GPU-only vmem reserve tracking and
 double-free reporting

hsa_amd_vmem_address_reserve_align without HSA_AMD_VMEM_ADDRESS_NO_REGISTER
reserves KFD address-only VA that is not host-writable. Sending those calls
through instance.Allocate() faulted when writing AsanChunk metadata before the
user pointer.

For that path, call the real HSA reserve/free APIs and record reservations in
VmemGpuReserveTracker.  Report double-free from asan_hsa_amd_vmem_address_free
when the same range is freed twice. Reservations with NO_REGISTER still use the
existing device-heap path (e.g. HIP managed memory).

Drop XFAIL from the vmem reserve_align lit tests now that the interceptors
behave correctly.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 24 +++++++++++++
 compiler-rt/lib/asan/asan_descriptions.cpp    |  3 ++
 compiler-rt/lib/asan/asan_errors.h            |  5 ++-
 compiler-rt/lib/asan/asan_report.cpp          | 16 +++++++++
 .../sanitizer_allocator_amdgpu.cpp            | 32 +++++++++++++++++
 .../sanitizer_allocator_amdgpu.h              | 34 +++++++++++++++++++
 ...sa_amd_pointer_info_vmem_reserve_align.cpp |  3 --
 ...vmem_address_reserve_align_double_free.cpp | 13 +++----
 8 files changed, 120 insertions(+), 10 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 24b22035a10fb..a479241605a26 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1650,6 +1650,15 @@ hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
     return HSA_STATUS_ERROR_INVALID_ARGUMENT;
   }
 
+  if (!__sanitizer::AmdgpuVmemReserveUsesHostMapping(flags)) {
+    const hsa_status_t status = REAL(hsa_amd_vmem_address_reserve_align)(
+        ptr, size, address, alignment, flags);
+    if (status == HSA_STATUS_SUCCESS && ptr && *ptr)
+      __sanitizer::VmemGpuReserveTracker::Get().OnReserve(
+          reinterpret_cast<uptr>(*ptr), size);
+    return status;
+  }
+
   AmdgpuAllocationInfo aa_info;
   aa_info.alloc_func =
       reinterpret_cast<void*>(asan_hsa_amd_vmem_address_reserve_align);
@@ -1688,6 +1697,21 @@ hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
     instance.Deallocate(ptr, 0, 0, stack, FROM_MALLOC);
     return HSA_STATUS_SUCCESS;
   }
+
+  using VmemGpuReserveTracker = __sanitizer::VmemGpuReserveTracker;
+  switch (
+      VmemGpuReserveTracker::Get().OnFree(reinterpret_cast<uptr>(ptr), size)) {
+    case VmemGpuReserveTracker::kNotTracked:
+      break;
+    case VmemGpuReserveTracker::kFirstFree:
+      return REAL(hsa_amd_vmem_address_free)(ptr, size);
+    case VmemGpuReserveTracker::kSizeMismatch:
+      errno = errno_EINVAL;
+      return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+    case VmemGpuReserveTracker::kDoubleFree:
+      ReportDoubleFree(reinterpret_cast<uptr>(ptr), stack);
+      return HSA_STATUS_SUCCESS;
+  }
   return REAL(hsa_amd_vmem_address_free)(ptr, size);
 }
 
diff --git a/compiler-rt/lib/asan/asan_descriptions.cpp b/compiler-rt/lib/asan/asan_descriptions.cpp
index 551b819a4c436..e6e815591f4f6 100644
--- a/compiler-rt/lib/asan/asan_descriptions.cpp
+++ b/compiler-rt/lib/asan/asan_descriptions.cpp
@@ -418,6 +418,9 @@ void StackAddressDescription::Print() const {
 }
 
 void HeapAddressDescription::Print() const {
+  if (alloc_tid == kInvalidTid)
+    return;
+
   PrintHeapChunkAccess(addr, chunk_access);
 
   asanThreadRegistry().CheckLocked();
diff --git a/compiler-rt/lib/asan/asan_errors.h b/compiler-rt/lib/asan/asan_errors.h
index d9a626e711282..3c5872ac2bef1 100644
--- a/compiler-rt/lib/asan/asan_errors.h
+++ b/compiler-rt/lib/asan/asan_errors.h
@@ -73,7 +73,10 @@ struct ErrorDoubleFree : ErrorBase {
       : ErrorBase(tid, 42, "double-free"),
         second_free_stack(stack) {
     CHECK_GT(second_free_stack->size, 0);
-    GetHeapAddressInformation(addr, 1, &addr_description);
+    if (!GetHeapAddressInformation(addr, 1, &addr_description)) {
+      internal_memset(&addr_description, 0, sizeof(addr_description));
+      addr_description.addr = addr;
+    }
   }
   void Print();
 };
diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp
index df797deaa5dbd..331ce8e321c11 100644
--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -249,6 +249,22 @@ void ReportDeadlySignal(const SignalContext &sig) {
 }
 
 void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
+  // GPU-only vmem reservations (VmemGpuReserveTracker) are not backed by
+  // AsanChunk metadata. Avoid reading chunk headers at such addresses to
+  // prevent potential crashes in StackDepot (e.g. 0xaa poison patterns).
+  if (!FindHeapChunkByAddress(addr).IsValid()) {
+    ScopedInErrorReport in_report;
+    Decorator d;
+    Printf("%s", d.Error());
+    Report(
+        "ERROR: AddressSanitizer: attempting double-free on %p in thread %s:\n",
+        (void*)addr, AsanThreadIdAndName(GetCurrentTidOrInvalid()).c_str());
+    Printf("%s", d.Default());
+    CHECK_GT(free_stack->size, 0);
+    free_stack->Print();
+    ReportErrorSummary("double-free", free_stack);
+    return;
+  }
   ScopedInErrorReport in_report;
   ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr);
   in_report.ReportError(error);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 461ad033cac9f..879f892499922 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -223,5 +223,37 @@ void AmdgpuDeviceAllocator::RegisterSystemEventHandlers() {
 }
 
 uptr AmdgpuDeviceAllocator::GetPageSize() { return kPageSize_; }
+
+VmemGpuReserveTracker& VmemGpuReserveTracker::Get() {
+  static VmemGpuReserveTracker tracker;
+  return tracker;
+}
+
+void VmemGpuReserveTracker::OnReserve(uptr ptr, uptr size) {
+  SpinMutexLock l(&mu_);
+  VmemGpuReservation entry;
+  entry.ptr = ptr;
+  entry.size = size;
+  entry.freed = false;
+  reservations_.push_back(entry);
+}
+
+VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::OnFree(uptr ptr,
+                                                                uptr size) {
+  SpinMutexLock l(&mu_);
+  for (uptr i = 0; i < reservations_.size(); ++i) {
+    VmemGpuReservation& r = reservations_[i];
+    if (r.ptr != ptr)
+      continue;
+    if (r.size != size)
+      return kSizeMismatch;
+    if (r.freed)
+      return kDoubleFree;
+    r.freed = true;
+    return kFirstFree;
+  }
+  return kNotTracked;
+}
+
 }  // namespace __sanitizer
 #endif  // SANITIZER_AMDGPU
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index efc6c8f869e3b..c33d8ace22fec 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -36,6 +36,40 @@ class AmdgpuDeviceAllocator {
   static void NotifyRuntimeShutdown();
 };
 
+// GPU-only hsa_amd_vmem_address_reserve_align (without
+// HSA_AMD_VMEM_ADDRESS_NO_REGISTER) returns KFD "address only" VA that is not
+// host-writable. Sanitizer runtimes that cannot place metadata in the reserved
+// range track such reservations here for double-free detection on free.
+class VmemGpuReserveTracker {
+ public:
+  enum FreeResult {
+    kNotTracked,
+    kFirstFree,
+    kSizeMismatch,
+    kDoubleFree,
+  };
+
+  static VmemGpuReserveTracker& Get();
+
+  void OnReserve(uptr ptr, uptr size);
+  FreeResult OnFree(uptr ptr, uptr size);
+
+ private:
+  struct VmemGpuReservation {
+    uptr ptr;
+    uptr size;
+    bool freed;
+  };
+
+  StaticSpinMutex mu_;
+  InternalMmapVector<VmemGpuReservation> reservations_;
+};
+
+// True when ROCr reserves host-accessible VA (e.g. HIP managed / SVM).
+inline bool AmdgpuVmemReserveUsesHostMapping(u64 flags) {
+  return (flags & HSA_AMD_VMEM_ADDRESS_NO_REGISTER) != 0;
+}
+
 struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
   AmdgpuAllocationInfo() : DeviceAllocationInfo(DAT_AMDGPU) {
     status = HSA_STATUS_SUCCESS;
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
index 06c09d041dd67..5c22d85b23f21 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -1,6 +1,3 @@
-// XFAIL: *
-//
-//
 // RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
 // RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: %run %t 2>&1 | FileCheck %s
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index 038fec02f7be3..1828e6e930bbd 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -1,6 +1,3 @@
-// XFAIL: *
-//
-//
 // RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
 // RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
@@ -28,9 +25,13 @@ int main() {
   // asan_hsa_amd_vmem_address_reserve_align).
   const size_t kSize = 4096;
   void *mem = nullptr;
-  if (hsa_amd_vmem_address_reserve_align(&mem, kSize, /*address=*/0,
-                                         /*alignment=*/4096,
-                                         /*flags=*/0) != HSA_STATUS_SUCCESS ||
+
+  // NOTE: To use `hipMallocManaged` way of reserving memory,
+  // use either `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in flags.
+  if (hsa_amd_vmem_address_reserve_align(
+          &mem, kSize, /*address=*/0,
+          /*alignment=*/4096,
+          /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER) != HSA_STATUS_SUCCESS ||
       !mem) {
     fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
     return 1;

>From a23085b0fafebcb46e0e1da29ac44cf9e3d9ab19 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 1 Jun 2026 12:36:35 +0530
Subject: [PATCH 26/46] Fix merge confilct in asan_allocator.cpp

---
 compiler-rt/lib/asan/asan_allocator.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index a479241605a26..f9da22f7f1902 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -544,7 +544,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,DeviceAllocationInfo *da_info = nullptr) {
+                     bool may_return_null,DeviceAllocationInfo *da_info) {
     if (UNLIKELY(!AsanInited()))
       AsanInitFromRtl();
     if (UNLIKELY(IsRssLimitExceeded())) {
@@ -688,9 +688,9 @@ 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, DeviceAllocationInfo *da_info = nullptr) {
     return AllocateImpl(size, alignment, stack, alloc_type, can_fill,
-                        AllocatorMayReturnNull());
+                        AllocatorMayReturnNull(),da_info);
   }
 
   // Set quarantine flag if chunk is allocated, issue ASan error report on

>From a14fd8459d26b4da2260463f2b0bd685696ff7fe Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 1 Jun 2026 15:05:56 +0530
Subject: [PATCH 27/46] [compiler-rt][asan][AMDGPU] Fix pointer_info bases for
 vmem and device ASan internal GetPointerInfo bases.

Only apply hsa_amd_pointer_info offset fixups to agentBaseAddress and
hostBaseAddress when ROCr returned a non-null value, so unmapped
RESERVED_ADDR reservations do not get a fabricated agent base from NULL
+ offset.  Still shift hostBaseAddress for NO_REGISTER reserves where
  ROCr reports os_addr below the aligned user VA.

Improve AmdgpuDeviceAllocator::GetPointerInfo to derive map_beg for all
relevant HSA pointer types, handle aligned NO_REGISTER vmem
reservations, and reject UNKNOWN or zero-size mappings.  Update the vmem
pointer_info lit test to use NO_REGISTER host-assisted reserves and
assert on hostBaseAddress instead of agentBaseAddress.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 23 ++++++----
 .../sanitizer_allocator_amdgpu.cpp            | 42 +++++++++++++++++--
 ...sa_amd_pointer_info_vmem_reserve_align.cpp | 19 +++++----
 3 files changed, 65 insertions(+), 19 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index f9da22f7f1902..f6973eef30e86 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -543,8 +543,8 @@ struct Allocator {
   // may_return_null tells AllocateImpl() whether OOM should produce a nullptr
   // (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,DeviceAllocationInfo *da_info) {
+                     AllocType alloc_type, bool can_fill, bool may_return_null,
+                     DeviceAllocationInfo* da_info) {
     if (UNLIKELY(!AsanInited()))
       AsanInitFromRtl();
     if (UNLIKELY(IsRssLimitExceeded())) {
@@ -688,9 +688,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, DeviceAllocationInfo *da_info = nullptr) {
+                 AllocType alloc_type, bool can_fill,
+                 DeviceAllocationInfo* da_info = nullptr) {
     return AllocateImpl(size, alignment, stack, alloc_type, can_fill,
-                        AllocatorMayReturnNull(),da_info);
+                        AllocatorMayReturnNull(), da_info);
   }
 
   // Set quarantine flag if chunk is allocated, issue ASan error report on
@@ -1745,10 +1746,16 @@ hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
     // hsa_amd_vmem_address_reserve_align())  uses alignment > page size (device
     // allocator padding and ASan redzones/headers).
     const uptr offset = user - reinterpret_cast<uptr>(hsa_map_base);
-    info->agentBaseAddress = reinterpret_cast<void*>(
-        reinterpret_cast<uptr>(info->agentBaseAddress) + offset);
-    info->hostBaseAddress = reinterpret_cast<void*>(
-        reinterpret_cast<uptr>(info->hostBaseAddress) + offset);
+    // hostBaseAddress must reflect the user-visible reservation base (ROCr may
+    // report os_addr below an aligned user VA for NO_REGISTER reserves).
+    if (info->hostBaseAddress)
+      info->hostBaseAddress = reinterpret_cast<void*>(
+          reinterpret_cast<uptr>(info->hostBaseAddress) + offset);
+    // agentBaseAddress is NULL for unmapped RESERVED_ADDR (no GPU backing).
+    // Do not turn NULL + offset into a fake agent address.
+    if (info->agentBaseAddress)
+      info->agentBaseAddress = reinterpret_cast<void*>(
+          reinterpret_cast<uptr>(info->agentBaseAddress) + offset);
     info->sizeInBytes = m->UsedSize();
   }
   return status;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 879f892499922..5844fffaba386 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -162,8 +162,38 @@ void AmdgpuDeviceAllocator::Deallocate(void* p) {
   }
 }
 
+static uptr AmdgpuPointerInfoMapBase(uptr ptr,
+                                     const hsa_amd_pointer_info_t& info) {
+  switch (info.type) {
+    case HSA_EXT_POINTER_TYPE_RESERVED_ADDR: {
+      uptr map_beg = reinterpret_cast<uptr>(info.hostBaseAddress);
+      // ROCr sets hostBaseAddress to the OS/KFD reservation base (os_addr).
+      // With HSA_AMD_VMEM_ADDRESS_NO_REGISTER the user VA from reserve_align
+      // is AlignUp(os_addr, alignment) and is the handle for vmem_address_free;
+      // it can differ from hostBaseAddress when alignment > page size.
+      if (map_beg && ptr >= map_beg && ptr < map_beg + info.sizeInBytes)
+        return map_beg;
+      return ptr;
+    }
+    case HSA_EXT_POINTER_TYPE_HSA:
+    case HSA_EXT_POINTER_TYPE_HSA_VMEM:
+    case HSA_EXT_POINTER_TYPE_IPC:
+      // Match ROCr PtrInfo block_info: prefer host mapping when present.
+      if (info.hostBaseAddress)
+        return reinterpret_cast<uptr>(info.hostBaseAddress);
+      return reinterpret_cast<uptr>(info.agentBaseAddress);
+    case HSA_EXT_POINTER_TYPE_LOCKED:
+      return reinterpret_cast<uptr>(info.hostBaseAddress);
+    default:
+      return 0;
+  }
+}
+
 bool AmdgpuDeviceAllocator::GetPointerInfo(uptr ptr,
                                            DevicePointerInfo* ptr_info) {
+  if (!ptr_info)
+    return false;
+
   // GetPointerInfo returns false after AMDGPU runtime shutdown
   if (UNLIKELY(IsRuntimeShutdown())) {
     VReport(1,
@@ -180,10 +210,14 @@ bool AmdgpuDeviceAllocator::GetPointerInfo(uptr ptr,
   if (status != HSA_STATUS_SUCCESS)
     return false;
 
-  if (info.type == HSA_EXT_POINTER_TYPE_RESERVED_ADDR)
-    ptr_info->map_beg = reinterpret_cast<uptr>(info.hostBaseAddress);
-  else if (info.type == HSA_EXT_POINTER_TYPE_HSA)
-    ptr_info->map_beg = reinterpret_cast<uptr>(info.agentBaseAddress);
+  if (info.type == HSA_EXT_POINTER_TYPE_UNKNOWN || !info.sizeInBytes)
+    return false;
+
+  const uptr map_beg = AmdgpuPointerInfoMapBase(ptr, info);
+  if (!map_beg)
+    return false;
+
+  ptr_info->map_beg = map_beg;
   ptr_info->map_size = info.sizeInBytes;
   ptr_info->type = reinterpret_cast<hsa_amd_pointer_type_t>(info.type);
 
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
index 5c22d85b23f21..dceb8eca54d41 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -22,11 +22,14 @@ int main() {
   if (hsa_amd_test_require_init())
     return 1;
 
-  const size_t kSize = 4096;
-  const uint64_t kAlign = 65536;
+  const size_t kSize = 4096; //4KiB size
+  // Here alignment is larger than a page size, so the reserved address will be aligned to 64KiB.
+  const uint64_t kAlign = 65536; //64KiB alignment
   void *mem = nullptr;
-  if (hsa_amd_vmem_address_reserve_align(&mem, kSize, /*address=*/0, kAlign,
-                                         /*flags=*/0) != HSA_STATUS_SUCCESS ||
+  // Host-accessible vmem reserve via NO_REGISTER (host mmap, not KFD VA).
+  if (hsa_amd_vmem_address_reserve_align(
+          &mem, kSize, /*address=*/0, kAlign,
+          /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER) != HSA_STATUS_SUCCESS ||
       !mem) {
     fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
     return 1;
@@ -40,14 +43,16 @@ int main() {
 
   hsa_amd_pointer_info_t info = {};
   info.size = sizeof(hsa_amd_pointer_info_t);
+  // Intercepted by ASan, where hostBaseAddress is the user pointer.
   if (hsa_amd_pointer_info(mem, &info, nullptr, nullptr, nullptr) !=
       HSA_STATUS_SUCCESS) {
     fprintf(stderr, "hsa_amd_pointer_info failed\n");
     return 1;
   }
 
-  if (info.agentBaseAddress != mem ||
-      reinterpret_cast<uintptr_t>(info.agentBaseAddress) + info.sizeInBytes !=
+  // Verify: User Pointer(mem) returned is hostBaseAddress as expected.
+  if (info.hostBaseAddress != mem ||
+      reinterpret_cast<uintptr_t>(info.hostBaseAddress) + info.sizeInBytes !=
           user + kSize) {
     fprintf(stderr,
             "pointer_info mismatch: user=%p begin=%p size=%zu (expected %zu)\n",
@@ -56,7 +61,7 @@ int main() {
   }
 
   printf("pointer_info_vmem sizeInBytes: %zu\n", info.sizeInBytes);
-  printf("pointer_info_vmem begin: %p\n", info.agentBaseAddress);
+  printf("pointer_info_vmem begin: %p\n", info.hostBaseAddress);
 
   if (hsa_amd_vmem_address_free(mem, kSize) != HSA_STATUS_SUCCESS) {
     fprintf(stderr, "hsa_amd_vmem_address_free failed\n");

>From 1746fb3c79bb43855729025528f834fb1afebff5 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 1 Jun 2026 16:19:14 +0530
Subject: [PATCH 28/46] [compiler-rt][AMDGPU] Mark Vmem reservations freed only
 after successful ROCr Free.

Mark GPU-only vmem reservations as freed only after
hsa_amd_vmem_address_free returns success, so a failed free can be
retried without a false double-free report.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 20 +++++++++++++------
 .../sanitizer_allocator_amdgpu.cpp            | 18 +++++++++++++----
 .../sanitizer_allocator_amdgpu.h              |  5 ++++-
 3 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index f6973eef30e86..b8dbd44576ec4 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -602,7 +602,7 @@ struct Allocator {
       allocated = allocator.Allocate(cache, needed_size, 8, da_info);
     } else {
       SpinMutexLock l(&fallback_mutex);
-      AllocatorCache *cache = &fallback_allocator_cache;
+      AllocatorCache* cache = &fallback_allocator_cache;
       allocated = allocator.Allocate(cache, needed_size, 8, da_info);
     }
     if (UNLIKELY(!allocated)) {
@@ -1699,20 +1699,28 @@ hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
     return HSA_STATUS_SUCCESS;
   }
 
+  // GPU-only free: only supported when host-accessible VA (NO_REGISTER) is
+  // used.
   using VmemGpuReserveTracker = __sanitizer::VmemGpuReserveTracker;
-  switch (
-      VmemGpuReserveTracker::Get().OnFree(reinterpret_cast<uptr>(ptr), size)) {
+  const uptr ptr_uptr = reinterpret_cast<uptr>(ptr);
+  switch (VmemGpuReserveTracker::Get().CheckFree(ptr_uptr, size)) {
     case VmemGpuReserveTracker::kNotTracked:
       break;
-    case VmemGpuReserveTracker::kFirstFree:
-      return REAL(hsa_amd_vmem_address_free)(ptr, size);
+    case VmemGpuReserveTracker::kFirstFree: {
+      const hsa_status_t status = REAL(hsa_amd_vmem_address_free)(ptr, size);
+      if (status == HSA_STATUS_SUCCESS)
+        VmemGpuReserveTracker::Get().MarkFreed(ptr_uptr, size);
+      return status;
+    }
     case VmemGpuReserveTracker::kSizeMismatch:
       errno = errno_EINVAL;
       return HSA_STATUS_ERROR_INVALID_ARGUMENT;
     case VmemGpuReserveTracker::kDoubleFree:
-      ReportDoubleFree(reinterpret_cast<uptr>(ptr), stack);
+      ReportDoubleFree(ptr_uptr, stack);
       return HSA_STATUS_SUCCESS;
   }
+  // Passthrough: untracked vmem frees (eg: fixed-address reservations) go
+  // straight to ROCr.
   return REAL(hsa_amd_vmem_address_free)(ptr, size);
 }
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 5844fffaba386..068b8911de3de 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -272,22 +272,32 @@ void VmemGpuReserveTracker::OnReserve(uptr ptr, uptr size) {
   reservations_.push_back(entry);
 }
 
-VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::OnFree(uptr ptr,
-                                                                uptr size) {
+VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::CheckFree(uptr ptr,
+                                                                   uptr size) {
   SpinMutexLock l(&mu_);
   for (uptr i = 0; i < reservations_.size(); ++i) {
-    VmemGpuReservation& r = reservations_[i];
+    const VmemGpuReservation& r = reservations_[i];
     if (r.ptr != ptr)
       continue;
     if (r.size != size)
       return kSizeMismatch;
     if (r.freed)
       return kDoubleFree;
-    r.freed = true;
     return kFirstFree;
   }
   return kNotTracked;
 }
 
+void VmemGpuReserveTracker::MarkFreed(uptr ptr, uptr size) {
+  SpinMutexLock l(&mu_);
+  for (uptr i = 0; i < reservations_.size(); ++i) {
+    VmemGpuReservation& r = reservations_[i];
+    if (r.ptr == ptr && r.size == size) {
+      r.freed = true;
+      return;
+    }
+  }
+}
+
 }  // namespace __sanitizer
 #endif  // SANITIZER_AMDGPU
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index c33d8ace22fec..398af140c8235 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -52,7 +52,10 @@ class VmemGpuReserveTracker {
   static VmemGpuReserveTracker& Get();
 
   void OnReserve(uptr ptr, uptr size);
-  FreeResult OnFree(uptr ptr, uptr size);
+  // CheckFree validates without updating state; call MarkFreed only after the
+  // real hsa_amd_vmem_address_free succeeds.
+  FreeResult CheckFree(uptr ptr, uptr size);
+  void MarkFreed(uptr ptr, uptr size);
 
  private:
   struct VmemGpuReservation {

>From 486c75735d1292e7c7788b5bfc4576087eb0420a Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 2 Jun 2026 14:36:38 +0530
Subject: [PATCH 29/46] [compiler-rt][asan] Define `SANITIZER_AMDHSA` with
 in-tree HSA stubs and asan_hsa_linux.cpp

Use `SANITIZER_AMDHSA` for host ROCm HSA interception (alias legacy
SANITIZER_AMDGPU). Add sanitizer_hsa/ stubs, drop ROCm CMake includes,
and move HSA interceptors into asan_hsa_linux.cpp.
---
 compiler-rt/CMakeLists.txt                    |  12 +-
 .../cmake/Modules/CompilerRTAMDGPUUtils.cmake | 106 ---------
 compiler-rt/lib/asan/CMakeLists.txt           |   2 +
 compiler-rt/lib/asan/asan_allocator.cpp       |  35 ++-
 compiler-rt/lib/asan/asan_allocator.h         |  49 ----
 compiler-rt/lib/asan/asan_hsa_linux.cpp       | 222 ++++++++++++++++++
 compiler-rt/lib/asan/asan_hsa_linux.h         |  62 +++++
 compiler-rt/lib/asan/asan_interceptors.cpp    | 166 +------------
 .../sanitizer_common/sanitizer_allocator.h    |  17 +-
 .../sanitizer_allocator_amdgpu.cpp            |   4 +-
 .../sanitizer_allocator_amdgpu.h              |   4 +-
 .../sanitizer_allocator_device.h              |   2 +-
 .../lib/sanitizer_common/sanitizer_common.h   |   2 +-
 .../lib/sanitizer_common/sanitizer_hsa.h      |  19 ++
 .../lib/sanitizer_common/sanitizer_hsa/hsa.h  |  49 ++++
 .../sanitizer_hsa/hsa_ext_amd.h               | 141 +++++++++++
 .../lib/sanitizer_common/sanitizer_linux.cpp  |   2 +-
 .../lib/sanitizer_common/sanitizer_platform.h |   6 +-
 .../lib/sanitizer_common/tests/CMakeLists.txt |  15 +-
 .../tests/sanitizer_nolibc_test.cpp           |   2 +-
 compiler-rt/test/CMakeLists.txt               |   2 +-
 .../asan/TestCases/AMDGPU/lit.local.cfg.py    |   2 +-
 compiler-rt/test/lit.common.configured.in     |   2 +-
 23 files changed, 555 insertions(+), 368 deletions(-)
 delete mode 100644 compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
 create mode 100644 compiler-rt/lib/asan/asan_hsa_linux.cpp
 create mode 100644 compiler-rt/lib/asan/asan_hsa_linux.h
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_hsa.h
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa.h
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa_ext_amd.h

diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index 26f3767e96aad..f8b2c0d746e3a 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -561,11 +561,13 @@ elseif(COMPILER_RT_HAS_G_FLAG)
   list(APPEND SANITIZER_COMMON_CFLAGS -g)
 endif()
 
-if(SANITIZER_AMDGPU)
-  list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDGPU=1)
-  include(CompilerRTAMDGPUUtils)
-  compiler_rt_find_amdgpu_runtime_headers()
-  include_directories(${HSA_INCLUDE_DIR} ${AMDComgr_INCLUDE_DIR})
+if(SANITIZER_AMDGPU AND NOT SANITIZER_AMDHSA)
+  set(SANITIZER_AMDHSA TRUE CACHE BOOL
+      "Enable ROCm HSA API interception in ASan" FORCE)
+endif()
+
+if(SANITIZER_AMDHSA)
+  list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDHSA=1)
 endif()
 
 if(LLVM_ENABLE_MODULES)
diff --git a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
deleted file mode 100644
index 9ad77d4efb66e..0000000000000
--- a/compiler-rt/cmake/Modules/CompilerRTAMDGPUUtils.cmake
+++ /dev/null
@@ -1,106 +0,0 @@
-# AMDGPU runtime headers discovery for compiler-rt when SANITIZER_AMDGPU is enabled.
-#
-#  Usage: Include this module and call
-#   `compiler_rt_find_amdgpu_runtime_headers()`
-#
-# User-settable hints (optional):
-#   HSA_ROOT / AMDComgr_ROOT     Install prefix (expects include/hsa/... or include/amd_comgr/...)
-#   ROCM_PATH                    Typical ROCm layout; also honors $ENV{ROCM_PATH}
-#   SANITIZER_HSA_INCLUDE_PATH   Custom HSA Include Path from source tree
-#   SANITIZER_COMGR_INCLUDE_PATH Custom COMGR Include Path from source tree
-#                             
-# Output CMake variables:
-#   HSA_INCLUDE_DIR, HSA_FOUND
-#   AMDComgr_INCLUDE_DIR, AMDComgr_FOUND
-#
-# This call is REQUIRED-style: missing headers triggers a fatal error from
-# `find_package_handle_standard_args`.
-
-include(FindPackageHandleStandardArgs)
-
-macro(compiler_rt_find_amdgpu_runtime_headers)
-  # --- HSA (hsa.h) ---
-  set(_hsa_search_paths "")
-  foreach(_root IN ITEMS "${HSA_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
-    if(_root)
-      list(APPEND _hsa_search_paths "${_root}/include")
-    endif()
-  endforeach()
-  if(SANITIZER_HSA_INCLUDE_PATH)
-    list(APPEND _hsa_search_paths "${SANITIZER_HSA_INCLUDE_PATH}")
-  endif()
-  # Default Search Fallback: ROCm include path.
-  list(APPEND _hsa_search_paths "/opt/rocm/include")
-
-  find_path(
-    HSA_INCLUDE_DIR
-    NAMES hsa.h
-    HINTS ${_hsa_search_paths}
-    PATH_SUFFIXES hsa
-  )
-
-  # If SANITIZER_HSA_INCLUDE_PATH points at the leaf `hsa` directory,
-  # retry without PATH_SUFFIXES.
-  if(NOT HSA_INCLUDE_DIR AND SANITIZER_HSA_INCLUDE_PATH)
-    find_path(
-      HSA_INCLUDE_DIR
-      NAMES hsa.h
-      HINTS "${SANITIZER_HSA_INCLUDE_PATH}"
-      NO_DEFAULT_PATH
-      NO_CMAKE_PATH
-      NO_CMAKE_ENVIRONMENT_PATH
-      NO_SYSTEM_ENVIRONMENT_PATH
-      NO_CMAKE_SYSTEM_PATH
-    )
-  endif()
-
-  find_package_handle_standard_args(HSA REQUIRED_VARS HSA_INCLUDE_DIR)
-  mark_as_advanced(HSA_INCLUDE_DIR)
-
-  # --- AMD COMGR (amd_comgr.h.in / amd_comgr.h) ---
-  set(_amdcomgr_search_paths "")
-  foreach(_root IN ITEMS "${AMDComgr_ROOT}" "${ROCM_PATH}" "$ENV{ROCM_PATH}")
-    if(_root)
-      list(APPEND _amdcomgr_search_paths "${_root}/include")
-    endif()
-  endforeach()
-  if(SANITIZER_COMGR_INCLUDE_PATH)
-    list(APPEND _amdcomgr_search_paths "${SANITIZER_COMGR_INCLUDE_PATH}")
-  endif()
-  # Default Search Fallback: ROCm include path.
-  list(APPEND _amdcomgr_search_paths "/opt/rocm/include")
-
-  find_path(
-    AMDComgr_INCLUDE_DIR
-    NAMES amd_comgr.h.in
-    HINTS ${_amdcomgr_search_paths}
-    PATH_SUFFIXES amd_comgr
-  )
-
-  if(NOT AMDComgr_INCLUDE_DIR)
-    find_path(
-      AMDComgr_INCLUDE_DIR
-      NAMES amd_comgr.h
-      HINTS ${_amdcomgr_search_paths}
-      PATH_SUFFIXES amd_comgr
-    )
-  endif()
-
-  # If SANITIZER_COMGR_INCLUDE_PATH points at the leaf `amd_comgr` directory,
-  # retry without PATH_SUFFIXES.
-  if(NOT AMDComgr_INCLUDE_DIR AND SANITIZER_COMGR_INCLUDE_PATH)
-    find_path(
-      AMDComgr_INCLUDE_DIR
-      NAMES amd_comgr.h.in amd_comgr.h
-      HINTS "${SANITIZER_COMGR_INCLUDE_PATH}"
-      NO_DEFAULT_PATH
-      NO_CMAKE_PATH
-      NO_CMAKE_ENVIRONMENT_PATH
-      NO_SYSTEM_ENVIRONMENT_PATH
-      NO_CMAKE_SYSTEM_PATH
-    )
-  endif()
-
-  find_package_handle_standard_args(AMDComgr REQUIRED_VARS AMDComgr_INCLUDE_DIR)
-  mark_as_advanced(AMDComgr_INCLUDE_DIR)
-endmacro()
diff --git a/compiler-rt/lib/asan/CMakeLists.txt b/compiler-rt/lib/asan/CMakeLists.txt
index 6d25cdc367742..2e36e6a07904c 100644
--- a/compiler-rt/lib/asan/CMakeLists.txt
+++ b/compiler-rt/lib/asan/CMakeLists.txt
@@ -12,6 +12,7 @@ set(ASAN_SOURCES
   asan_fuchsia.cpp
   asan_globals.cpp
   asan_globals_win.cpp
+  asan_hsa_linux.cpp
   asan_interceptors.cpp
   asan_interceptors_memintrinsics.cpp
   asan_linux.cpp
@@ -80,6 +81,7 @@ SET(ASAN_HEADERS
   asan_fake_stack.h
   asan_flags.h
   asan_flags.inc
+  asan_hsa_linux.h
   asan_init_version.h
   asan_interceptors.h
   asan_interceptors_memintrinsics.h
diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index b8dbd44576ec4..cabec24177a66 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -394,10 +394,10 @@ struct Allocator {
   void InitLinkerInitialized(const AllocatorOptions& options) {
     SetAllocatorMayReturnNull(options.may_return_null);
     // Device-backed allocations use CombinedAllocator's device path. On
-    // SANITIZER_AMDGPU builds, enable it so InitMemFuncs() /
+    // SANITIZER_AMDHSA builds, enable it so InitMemFuncs() /
     // AmdgpuDeviceAllocator::Init() run at startup.
     allocator.InitLinkerInitialized(options.release_to_os_interval_ms, 0,
-                                    SANITIZER_AMDGPU);
+                                    SANITIZER_AMDHSA);
     SharedInitCode(options);
     max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
                                        ? common_flags()->max_allocation_size_mb
@@ -1481,7 +1481,12 @@ int __asan_update_allocation_context(void* addr) {
   return instance.UpdateAllocationStack((uptr)addr, &stack);
 }
 
-#if SANITIZER_AMDGPU
+#if SANITIZER_AMDHSA
+
+// Pull in the in-tree HSA type stubs for the wrapper implementations below.
+#  include "sanitizer_common/sanitizer_hsa.h"
+
+namespace __asan {
 
 DECLARE_REAL(hsa_status_t, hsa_init);
 DECLARE_REAL(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
@@ -1506,8 +1511,12 @@ DECLARE_REAL(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
 DECLARE_REAL(hsa_status_t, hsa_amd_register_system_event_handler,
              hsa_amd_system_event_callback_t, void*)
 
-namespace __asan {
-// Always align to page boundary to match current ROCr behavior
+// HSA allocation wrappers live in this TU (not `asan_hsa_linux.cpp`) because
+// they need access to ASan allocator internals (e.g. `instance`,
+// `get_allocator()`, `AsanChunk` layout) to translate between ROCr-visible
+// mappings and ASan user pointers / metadata.
+//
+// Always align to page boundary to match current ROCr behavior.
 static const size_t kPageSize_ = 4096;
 
 hsa_status_t asan_hsa_amd_memory_pool_allocate(
@@ -1515,7 +1524,7 @@ hsa_status_t asan_hsa_amd_memory_pool_allocate(
     BufferedStackTrace* stack) {
   AmdgpuAllocationInfo aa_info;
   aa_info.alloc_func =
-      reinterpret_cast<void*>(asan_hsa_amd_memory_pool_allocate);
+      reinterpret_cast<void*>((uptr)&__asan::asan_hsa_amd_memory_pool_allocate);
   aa_info.memory_pool = memory_pool;
   aa_info.size = size;
   aa_info.flags = flags;
@@ -1540,17 +1549,16 @@ hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
                                               const uint32_t* flags,
                                               const void* ptr,
                                               BufferedStackTrace* stack) {
+  (void)stack;
   void* p = get_allocator().GetBlockBegin(ptr);
   return REAL(hsa_amd_agents_allow_access)(num_agents, agents, flags,
                                            p ? p : ptr);
 }
 
-// IPC calls use static_assert to make sure kMetadataSize = 0
-//
 #  if SANITIZER_CAN_USE_ALLOCATOR64
-static struct AP64<LocalAddressSpaceView> AP_;
+static struct __asan::AP64<LocalAddressSpaceView> AP_;
 #  else
-static struct AP32<LocalAddressSpaceView> AP_;
+static struct __asan::AP32<LocalAddressSpaceView> AP_;
 #  endif
 
 hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
@@ -1661,8 +1669,8 @@ hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
   }
 
   AmdgpuAllocationInfo aa_info;
-  aa_info.alloc_func =
-      reinterpret_cast<void*>(asan_hsa_amd_vmem_address_reserve_align);
+  aa_info.alloc_func = reinterpret_cast<void*>(
+      (uptr)&__asan::asan_hsa_amd_vmem_address_reserve_align);
   aa_info.memory_pool = {0};
   aa_info.size = size;
   aa_info.flags64 = flags;
@@ -1783,6 +1791,7 @@ hsa_status_t asan_hsa_init() {
   }
   return status;
 }
+
 }  // namespace __asan
 
-#endif  // SANITIZER_AMDGPU
+#endif  // SANITIZER_AMDHSA
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index 4f38e7de6a15e..9a8601f80430e 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -332,53 +332,4 @@ void AsanSoftRssLimitExceededCallback(bool exceeded);
 
 }  // namespace __asan
 
-#if SANITIZER_AMDGPU
-
-#  if defined(__has_include)
-#    if __has_include("hsa.h")
-#      include "hsa.h"
-#      include "hsa_ext_amd.h"
-#    elif __has_include("hsa/hsa.h")
-#      include "hsa/hsa.h"
-#      include "hsa/hsa_ext_amd.h"
-#    endif
-#  else
-#    include "hsa/hsa.h"
-#    include "hsa/hsa_ext_amd.h"
-#  endif
-
-namespace __asan {
-hsa_status_t asan_hsa_amd_memory_pool_allocate(
-    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
-    BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
-                                           BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
-                                              const hsa_agent_t* agents,
-                                              const uint32_t* flags,
-                                              const void* ptr,
-                                              BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
-                                            hsa_amd_ipc_memory_t* handle);
-hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
-                                            size_t len, uint32_t num_agents,
-                                            const hsa_agent_t* mapping_agents,
-                                            void** mapped_ptr);
-hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr);
-hsa_status_t asan_hsa_amd_vmem_address_reserve_align(void** ptr, size_t size,
-                                                     uint64_t address,
-                                                     uint64_t alignment,
-                                                     uint64_t flags,
-                                                     BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
-                                            BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
-                                       hsa_amd_pointer_info_t* info,
-                                       void* (*alloc)(size_t),
-                                       uint32_t* num_agents_accessible,
-                                       hsa_agent_t** accessible);
-hsa_status_t asan_hsa_init();
-}  // namespace __asan
-#endif
-
 #endif  // ASAN_ALLOCATOR_H
diff --git a/compiler-rt/lib/asan/asan_hsa_linux.cpp b/compiler-rt/lib/asan/asan_hsa_linux.cpp
new file mode 100644
index 0000000000000..a6ea062a23595
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_hsa_linux.cpp
@@ -0,0 +1,222 @@
+//===-- asan_hsa_linux.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Linux host HSA API interception for AddressSanitizer (SANITIZER_AMDHSA).
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+
+#if SANITIZER_AMDHSA
+
+#  include "asan_hsa_linux.h"
+#  include "asan_interceptors.h"
+#  include "asan_interceptors_memintrinsics.h"
+#  include "asan_internal.h"
+#  include "asan_report.h"
+#  include "asan_stack.h"
+#  include "asan_suppressions.h"
+
+using namespace __asan;
+
+// This TU provides the HSA interceptors, so it must also define the underlying
+// REAL() slots (e.g. __interception::real_hsa_init). Otherwise the shared
+// runtime fails to link when any interceptor references REAL(hsa_*).
+DEFINE_REAL(hsa_status_t, hsa_init, );
+DEFINE_REAL(hsa_status_t, hsa_memory_copy, void*, const void*, size_t)
+DEFINE_REAL(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
+            const hsa_agent_t* agents, const uint32_t* flags, const void* ptr)
+DEFINE_REAL(hsa_status_t, hsa_amd_memory_pool_allocate,
+            hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
+            void** ptr)
+DEFINE_REAL(hsa_status_t, hsa_amd_memory_pool_free, void* ptr)
+DEFINE_REAL(hsa_status_t, hsa_amd_memory_async_copy, void*, hsa_agent_t,
+            const void*, hsa_agent_t, size_t, uint32_t, const hsa_signal_t*,
+            hsa_signal_t)
+#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+DEFINE_REAL(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void*,
+            hsa_agent_t, const void*, hsa_agent_t, size_t, uint32_t,
+            const hsa_signal_t*, hsa_signal_t, hsa_amd_sdma_engine_id_t, bool)
+#  endif
+DEFINE_REAL(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
+            hsa_amd_ipc_memory_t* handle)
+DEFINE_REAL(hsa_status_t, hsa_amd_ipc_memory_attach,
+            const hsa_amd_ipc_memory_t* handle, size_t len, uint32_t num_agents,
+            const hsa_agent_t* mapping_agents, void** mapped_ptr)
+DEFINE_REAL(hsa_status_t, hsa_amd_ipc_memory_detach, void* mapped_ptr)
+DEFINE_REAL(hsa_status_t, hsa_amd_vmem_address_reserve_align, void** ptr,
+            size_t size, uint64_t address, uint64_t alignment, uint64_t flags)
+DEFINE_REAL(hsa_status_t, hsa_amd_vmem_address_free, void* ptr, size_t size)
+DEFINE_REAL(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
+            hsa_amd_pointer_info_t* info, void* (*alloc)(size_t),
+            uint32_t* num_agents_accessible, hsa_agent_t** accessible)
+
+namespace __asan {
+
+static void ENSURE_HSA_INITED() {
+  // The HSA interceptors are initialized lazily: if the program calls into HSA
+  // before ASan's global interceptor init runs, we still need the REAL() slots
+  // to be populated so we can call through to ROCr.
+  if (!REAL(hsa_init))
+    InitializeAmdgpuInterceptors();
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_allocate,
+            hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
+            void** ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_MALLOC;
+  return asan_hsa_amd_memory_pool_allocate(memory_pool, size, flags, ptr,
+                                           &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_free, void* ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_FREE;
+  return asan_hsa_amd_memory_pool_free(ptr, &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
+            const hsa_agent_t* agents, const uint32_t* flags, const void* ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_FREE;
+  return asan_hsa_amd_agents_allow_access(num_agents, agents, flags, ptr,
+                                          &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_memory_copy, void* dst, const void* src,
+            size_t size) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  if (flags()->replace_intrin) {
+    if (dst != src) {
+      CHECK_RANGES_OVERLAP("hsa_memory_copy", dst, size, src, size);
+    }
+    ASAN_READ_RANGE(nullptr, src, size);
+    ASAN_WRITE_RANGE(nullptr, dst, size);
+  }
+  return REAL(hsa_memory_copy)(dst, src, size);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy, void* dst,
+            hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
+            size_t size, uint32_t num_dep_signals,
+            const hsa_signal_t* dep_signals, hsa_signal_t completion_signal) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  if (flags()->replace_intrin) {
+    if (dst != src) {
+      CHECK_RANGES_OVERLAP("hsa_amd_memory_async_copy", dst, size, src, size);
+    }
+    ASAN_READ_RANGE(nullptr, src, size);
+    ASAN_WRITE_RANGE(nullptr, dst, size);
+  }
+  return REAL(hsa_amd_memory_async_copy)(dst, dst_agent, src, src_agent, size,
+                                         num_dep_signals, dep_signals,
+                                         completion_signal);
+}
+
+#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void* dst,
+            hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
+            size_t size, uint32_t num_dep_signals,
+            const hsa_signal_t* dep_signals, hsa_signal_t completion_signal,
+            hsa_amd_sdma_engine_id_t engine_id, bool force_copy_on_sdma) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  if (flags()->replace_intrin) {
+    if (dst != src) {
+      CHECK_RANGES_OVERLAP("hsa_amd_memory_async_copy_on_engine", dst, size,
+                           src, size);
+    }
+    ASAN_READ_RANGE(nullptr, src, size);
+    ASAN_WRITE_RANGE(nullptr, dst, size);
+  }
+  return REAL(hsa_amd_memory_async_copy_on_engine)(
+      dst, dst_agent, src, src_agent, size, num_dep_signals, dep_signals,
+      completion_signal, engine_id, force_copy_on_sdma);
+}
+#  endif
+
+INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
+            hsa_amd_ipc_memory_t* handle) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_ipc_memory_create(ptr, len, handle);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_attach,
+            const hsa_amd_ipc_memory_t* handle, size_t len, uint32_t num_agents,
+            const hsa_agent_t* mapping_agents, void** mapped_ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_ipc_memory_attach(handle, len, num_agents, mapping_agents,
+                                        mapped_ptr);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_detach, void* mapped_ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_ipc_memory_detach(mapped_ptr);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_vmem_address_reserve_align, void** ptr,
+            size_t size, uint64_t address, uint64_t alignment, uint64_t flags) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_MALLOC;
+  return asan_hsa_amd_vmem_address_reserve_align(ptr, size, address, alignment,
+                                                 flags, &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_vmem_address_free, void* ptr, size_t size) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_FREE;
+  return asan_hsa_amd_vmem_address_free(ptr, size, &stack);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
+            hsa_amd_pointer_info_t* info, void* (*alloc)(size_t),
+            uint32_t* num_agents_accessible, hsa_agent_t** accessible) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_amd_pointer_info(ptr, info, alloc, num_agents_accessible,
+                                   accessible);
+}
+
+INTERCEPTOR(hsa_status_t, hsa_init) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  return asan_hsa_init();
+}
+
+void InitializeAmdgpuInterceptors() {
+  ASAN_INTERCEPT_FUNC(hsa_init);
+  ASAN_INTERCEPT_FUNC(hsa_memory_copy);
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_allocate);
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_free);
+  ASAN_INTERCEPT_FUNC(hsa_amd_agents_allow_access);
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy);
+#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+  ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy_on_engine);
+#  endif
+  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_create);
+  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_attach);
+  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_detach);
+  ASAN_INTERCEPT_FUNC(hsa_amd_vmem_address_reserve_align);
+  ASAN_INTERCEPT_FUNC(hsa_amd_vmem_address_free);
+  ASAN_INTERCEPT_FUNC(hsa_amd_pointer_info);
+}
+
+}  // namespace __asan
+
+#endif  // SANITIZER_AMDHSA
diff --git a/compiler-rt/lib/asan/asan_hsa_linux.h b/compiler-rt/lib/asan/asan_hsa_linux.h
new file mode 100644
index 0000000000000..ca788a8f4bcc5
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_hsa_linux.h
@@ -0,0 +1,62 @@
+//===-- asan_hsa_linux.h ---------------------------------------- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Linux host HSA API interception for AddressSanitizer (SANITIZER_AMDHSA).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_HSA_LINUX_H
+#define ASAN_HSA_LINUX_H
+
+#include "sanitizer_common/sanitizer_platform.h"
+
+#if SANITIZER_AMDHSA
+
+#  include "asan_stack.h"
+#  include "sanitizer_common/sanitizer_hsa.h"
+
+namespace __asan {
+
+void InitializeAmdgpuInterceptors();
+
+hsa_status_t asan_hsa_amd_memory_pool_allocate(
+    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
+    BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
+                                           BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
+                                              const hsa_agent_t* agents,
+                                              const uint32_t* flags,
+                                              const void* ptr,
+                                              BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
+                                            hsa_amd_ipc_memory_t* handle);
+hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
+                                            size_t len, uint32_t num_agents,
+                                            const hsa_agent_t* mapping_agents,
+                                            void** mapped_ptr);
+hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr);
+hsa_status_t asan_hsa_amd_vmem_address_reserve_align(void** ptr, size_t size,
+                                                     uint64_t address,
+                                                     uint64_t alignment,
+                                                     uint64_t flags,
+                                                     BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
+                                            BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
+                                       hsa_amd_pointer_info_t* info,
+                                       void* (*alloc)(size_t),
+                                       uint32_t* num_agents_accessible,
+                                       hsa_agent_t** accessible);
+hsa_status_t asan_hsa_init();
+
+}  // namespace __asan
+
+#endif  // SANITIZER_AMDHSA
+
+#endif  // ASAN_HSA_LINUX_H
diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp
index 16e827c635f21..469a5fea91e33 100644
--- a/compiler-rt/lib/asan/asan_interceptors.cpp
+++ b/compiler-rt/lib/asan/asan_interceptors.cpp
@@ -14,6 +14,7 @@
 #include "asan_interceptors.h"
 
 #include "asan_allocator.h"
+#include "asan_hsa_linux.h"
 #include "asan_internal.h"
 #include "asan_mapping.h"
 #include "asan_poisoning.h"
@@ -895,166 +896,6 @@ DEFINE_REAL(int, vfork, )
 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork, )
 #  endif
 
-#  if SANITIZER_AMDGPU
-void ENSURE_HSA_INITED();
-
-INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_allocate,
-            hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
-            void** ptr) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  GET_STACK_TRACE_MALLOC;
-  return asan_hsa_amd_memory_pool_allocate(memory_pool, size, flags, ptr,
-                                           &stack);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_free, void* ptr) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  GET_STACK_TRACE_FREE;
-  return asan_hsa_amd_memory_pool_free(ptr, &stack);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
-            const hsa_agent_t* agents, const uint32_t* flags, const void* ptr) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  GET_STACK_TRACE_FREE;
-  return asan_hsa_amd_agents_allow_access(num_agents, agents, flags, ptr,
-                                          &stack);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_memory_copy, void* dst, const void* src,
-            size_t size) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  if (flags()->replace_intrin) {
-    if (dst != src) {
-      CHECK_RANGES_OVERLAP("hsa_memory_copy", dst, size, src, size);
-    }
-    ASAN_READ_RANGE(nullptr, src, size);
-    ASAN_WRITE_RANGE(nullptr, dst, size);
-  }
-  return REAL(hsa_memory_copy)(dst, src, size);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy, void* dst,
-            hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
-            size_t size, uint32_t num_dep_signals,
-            const hsa_signal_t* dep_signals, hsa_signal_t completion_signal) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  if (flags()->replace_intrin) {
-    if (dst != src) {
-      CHECK_RANGES_OVERLAP("hsa_amd_memory_async_copy", dst, size, src, size);
-    }
-    ASAN_READ_RANGE(nullptr, src, size);
-    ASAN_WRITE_RANGE(nullptr, dst, size);
-  }
-  return REAL(hsa_amd_memory_async_copy)(dst, dst_agent, src, src_agent, size,
-                                         num_dep_signals, dep_signals,
-                                         completion_signal);
-}
-
-#    if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
-INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void* dst,
-            hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
-            size_t size, uint32_t num_dep_signals,
-            const hsa_signal_t* dep_signals, hsa_signal_t completion_signal,
-            hsa_amd_sdma_engine_id_t engine_id, bool force_copy_on_sdma) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  if (flags()->replace_intrin) {
-    if (dst != src) {
-      CHECK_RANGES_OVERLAP("hsa_amd_memory_async_copy_on_engine", dst, size,
-                           src, size);
-    }
-    ASAN_READ_RANGE(nullptr, src, size);
-    ASAN_WRITE_RANGE(nullptr, dst, size);
-  }
-  return REAL(hsa_amd_memory_async_copy_on_engine)(
-      dst, dst_agent, src, src_agent, size, num_dep_signals, dep_signals,
-      completion_signal, engine_id, force_copy_on_sdma);
-}
-#    endif
-
-INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
-            hsa_amd_ipc_memory_t* handle) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  return asan_hsa_amd_ipc_memory_create(ptr, len, handle);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_attach,
-            const hsa_amd_ipc_memory_t* handle, size_t len, uint32_t num_agents,
-            const hsa_agent_t* mapping_agents, void** mapped_ptr) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  return asan_hsa_amd_ipc_memory_attach(handle, len, num_agents, mapping_agents,
-                                        mapped_ptr);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_detach, void* mapped_ptr) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  return asan_hsa_amd_ipc_memory_detach(mapped_ptr);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_vmem_address_reserve_align, void** ptr,
-            size_t size, uint64_t address, uint64_t alignment, uint64_t flags) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  GET_STACK_TRACE_MALLOC;
-  return asan_hsa_amd_vmem_address_reserve_align(ptr, size, address, alignment,
-                                                 flags, &stack);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_vmem_address_free, void* ptr, size_t size) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  GET_STACK_TRACE_FREE;
-  return asan_hsa_amd_vmem_address_free(ptr, size, &stack);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
-            hsa_amd_pointer_info_t* info, void* (*alloc)(size_t),
-            uint32_t* num_agents_accessible, hsa_agent_t** accessible) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  return asan_hsa_amd_pointer_info(ptr, info, alloc, num_agents_accessible,
-                                   accessible);
-}
-
-INTERCEPTOR(hsa_status_t, hsa_init) {
-  AsanInitFromRtl();
-  ENSURE_HSA_INITED();
-  return asan_hsa_init();
-}
-
-void InitializeAmdgpuInterceptors() {
-  ASAN_INTERCEPT_FUNC(hsa_init);
-  ASAN_INTERCEPT_FUNC(hsa_memory_copy);
-  ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_allocate);
-  ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_free);
-  ASAN_INTERCEPT_FUNC(hsa_amd_agents_allow_access);
-  ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy);
-#    if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
-  ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy_on_engine);
-#    endif
-  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_create);
-  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_attach);
-  ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_detach);
-  ASAN_INTERCEPT_FUNC(hsa_amd_vmem_address_reserve_align);
-  ASAN_INTERCEPT_FUNC(hsa_amd_vmem_address_free);
-  ASAN_INTERCEPT_FUNC(hsa_amd_pointer_info);
-}
-
-void ENSURE_HSA_INITED() {
-  if (!REAL(hsa_init))
-    InitializeAmdgpuInterceptors();
-}
-#  endif
-
 // ---------------------- InitializeAsanInterceptors ---------------- {{{1
 namespace __asan {
 void InitializeAsanInterceptors() {
@@ -1171,7 +1012,10 @@ void InitializeAsanInterceptors() {
   ASAN_INTERCEPT_FUNC(vfork);
 #  endif
 
-#  if SANITIZER_AMDGPU
+#  if SANITIZER_AMDHSA
+  // HSA/ROCr interceptors are split out of this TU to keep the host interceptor
+  // surface clean. Initialize them here so the REAL() slots exist before first
+  // HSA API call (the first such call is often hsa_init()).
   InitializeAmdgpuInterceptors();
 #  endif
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index 726ef132d0e40..17f254a59155b 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -24,19 +24,8 @@
 #include "sanitizer_procmaps.h"
 #include "sanitizer_type_traits.h"
 
-#if SANITIZER_AMDGPU
-#  if defined(__has_include)
-#    if __has_include("hsa.h")
-#      include "hsa.h"
-#      include "hsa_ext_amd.h"
-#    elif __has_include("hsa/hsa.h")
-#      include "hsa/hsa.h"
-#      include "hsa/hsa_ext_amd.h"
-#    endif
-#  else
-#    include "hsa/hsa.h"
-#    include "hsa/hsa_ext_amd.h"
-#  endif
+#if SANITIZER_AMDHSA
+#  include "sanitizer_common/sanitizer_hsa.h"
 #endif
 
 namespace __sanitizer {
@@ -88,7 +77,7 @@ struct NoOpMapUnmapCallback {
 #include "sanitizer_allocator_local_cache.h"
 #include "sanitizer_allocator_secondary.h"
 #include "sanitizer_allocator_device.h"
-#if SANITIZER_AMDGPU
+#if SANITIZER_AMDHSA
 #  include "sanitizer_allocator_amdgpu.h"
 #endif
 #include "sanitizer_allocator_combined.h"
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 068b8911de3de..995afafc82183 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -9,7 +9,7 @@
 // Part of the Sanitizer Allocator.
 //
 //===----------------------------------------------------------------------===//
-#if SANITIZER_AMDGPU
+#if SANITIZER_AMDHSA
 #  include <dlfcn.h>  // For dlsym
 
 #  include "sanitizer_allocator.h"
@@ -300,4 +300,4 @@ void VmemGpuReserveTracker::MarkFreed(uptr ptr, uptr size) {
 }
 
 }  // namespace __sanitizer
-#endif  // SANITIZER_AMDGPU
+#endif  // SANITIZER_AMDHSA
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index 398af140c8235..babaadad697be 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -13,7 +13,7 @@
 #  error This file must be included inside sanitizer_allocator.h
 #endif
 
-#if SANITIZER_AMDGPU
+#if SANITIZER_AMDHSA
 
 static const DeviceAllocationType DAT_AMDGPU =
     static_cast<DeviceAllocationType>(1);
@@ -99,4 +99,4 @@ template <class MapUnmapCallback>
 using DefaultDeviceAllocator =
     DeviceAllocatorT<MapUnmapCallback, AmdgpuDeviceAllocator>;
 
-#endif  // SANITIZER_AMDGPU
+#endif  // SANITIZER_AMDHSA
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index 00d8a5385ce8d..c78a449cb319e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -390,7 +390,7 @@ class DeviceAllocatorT {
   mutable StaticSpinMutex mutex_;
 };
 
-#if !SANITIZER_AMDGPU
+#if !SANITIZER_AMDHSA
 template <class MapUnmapCallback>
 using DefaultDeviceAllocator =
     DeviceAllocatorT<MapUnmapCallback, NoOpDeviceAllocator>;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index d3ded56bed58c..b33a1a85ba3fe 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -1096,7 +1096,7 @@ const s32 kReleaseToOSIntervalNever = -1;
 // checks (e.g. RTLD_DEEPBIND on Linux).
 void OnDlOpen(const char* filename, int flag);
 
-#if SANITIZER_AMDGPU
+#if SANITIZER_AMDHSA
 void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag);
 #else
 inline void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag) {}
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_hsa.h b/compiler-rt/lib/sanitizer_common/sanitizer_hsa.h
new file mode 100644
index 0000000000000..5a9d81182dfc6
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_hsa.h
@@ -0,0 +1,19 @@
+//===-- sanitizer_hsa.h ----------------------------------------- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Umbrella include for in-tree HSA stub headers (host AMDHSA sanitizer builds).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_HSA_H_
+#define SANITIZER_HSA_H_
+
+#include "sanitizer_common/sanitizer_hsa/hsa.h"
+#include "sanitizer_common/sanitizer_hsa/hsa_ext_amd.h"
+
+#endif  // SANITIZER_HSA_H_
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa.h b/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa.h
new file mode 100644
index 0000000000000..f052ddb579a78
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa.h
@@ -0,0 +1,49 @@
+//===-- sanitizer_hsa/hsa.h ------------------------------------- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Minimal HSA API declarations used by compiler-rt host sanitizers.
+// Matches ROCr layout/ABI; does not require ROCm headers at build time.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_HSA_HSA_H_
+#define SANITIZER_HSA_HSA_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  HSA_STATUS_SUCCESS = 0x0,
+  HSA_STATUS_INFO_BREAK = 0x1,
+  HSA_STATUS_ERROR = 0x1000,
+  HSA_STATUS_ERROR_INVALID_ARGUMENT = 0x1001,
+  HSA_STATUS_ERROR_NOT_INITIALIZED = 0x100B,
+  HSA_STATUS_ERROR_OUT_OF_RESOURCES = 0x100C,
+  HSA_STATUS_ERROR_INVALID_RUNTIME_STATE = 0x1025,
+} hsa_status_t;
+
+typedef struct hsa_agent_s {
+  uint64_t handle;
+} hsa_agent_t;
+
+typedef struct hsa_signal_s {
+  uint64_t handle;
+} hsa_signal_t;
+
+hsa_status_t hsa_init(void);
+hsa_status_t hsa_memory_copy(void* dst, const void* src, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // SANITIZER_HSA_HSA_H_
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa_ext_amd.h b/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa_ext_amd.h
new file mode 100644
index 0000000000000..9a335762fcf89
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa_ext_amd.h
@@ -0,0 +1,141 @@
+//===-- sanitizer_hsa/hsa_ext_amd.h ----------------------------- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Minimal AMD HSA extension declarations used by compiler-rt host sanitizers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_HSA_HSA_EXT_AMD_H_
+#define SANITIZER_HSA_HSA_EXT_AMD_H_
+
+#include "hsa.h"
+
+#ifndef __cplusplus
+#  include <stdbool.h>
+#endif
+
+#define HSA_AMD_INTERFACE_VERSION_MAJOR 1
+#define HSA_AMD_INTERFACE_VERSION_MINOR 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct hsa_amd_memory_pool_s {
+  uint64_t handle;
+} hsa_amd_memory_pool_t;
+
+typedef enum {
+  HSA_EXT_POINTER_TYPE_UNKNOWN = 0,
+  HSA_EXT_POINTER_TYPE_HSA = 1,
+  HSA_EXT_POINTER_TYPE_LOCKED = 2,
+  HSA_EXT_POINTER_TYPE_GRAPHICS = 3,
+  HSA_EXT_POINTER_TYPE_IPC = 4,
+  HSA_EXT_POINTER_TYPE_RESERVED_ADDR = 5,
+  HSA_EXT_POINTER_TYPE_HSA_VMEM = 6,
+} hsa_amd_pointer_type_t;
+
+typedef struct hsa_amd_pointer_info_s {
+  uint32_t size;
+  hsa_amd_pointer_type_t type;
+  void* agentBaseAddress;
+  void* hostBaseAddress;
+  size_t sizeInBytes;
+} hsa_amd_pointer_info_t;
+
+typedef struct hsa_amd_ipc_memory_s {
+  uint32_t handle[8];
+} hsa_amd_ipc_memory_t;
+
+typedef enum hsa_amd_sdma_engine_id {
+  HSA_AMD_SDMA_ENGINE_0 = 0x1,
+  HSA_AMD_SDMA_ENGINE_1 = 0x2,
+  HSA_AMD_SDMA_ENGINE_2 = 0x4,
+  HSA_AMD_SDMA_ENGINE_3 = 0x8,
+  HSA_AMD_SDMA_ENGINE_4 = 0x10,
+  HSA_AMD_SDMA_ENGINE_5 = 0x20,
+  HSA_AMD_SDMA_ENGINE_6 = 0x40,
+  HSA_AMD_SDMA_ENGINE_7 = 0x80,
+  HSA_AMD_SDMA_ENGINE_8 = 0x100,
+  HSA_AMD_SDMA_ENGINE_9 = 0x200,
+  HSA_AMD_SDMA_ENGINE_10 = 0x400,
+  HSA_AMD_SDMA_ENGINE_11 = 0x800,
+  HSA_AMD_SDMA_ENGINE_12 = 0x1000,
+  HSA_AMD_SDMA_ENGINE_13 = 0x2000,
+  HSA_AMD_SDMA_ENGINE_14 = 0x4000,
+  HSA_AMD_SDMA_ENGINE_15 = 0x8000,
+} hsa_amd_sdma_engine_id_t;
+
+typedef enum hsa_amd_vmem_address_reserve_flag_s {
+  HSA_AMD_VMEM_ADDRESS_NO_REGISTER = (1UL << 0),
+} hsa_amd_vmem_address_reserve_flag_t;
+
+typedef enum hsa_amd_event_type_s {
+  HSA_AMD_GPU_MEMORY_FAULT_EVENT = 0,
+  HSA_AMD_GPU_MEMORY_ERROR_EVENT = 1,
+  HSA_AMD_SYSTEM_SHUTDOWN_EVENT = 2,
+} hsa_amd_event_type_t;
+
+typedef struct hsa_amd_gpu_memory_fault_info_s {
+  hsa_agent_t agent;
+  uint64_t virtual_address;
+  uint32_t fault_reason_mask;
+} hsa_amd_gpu_memory_fault_info_t;
+
+typedef struct hsa_amd_event_s {
+  hsa_amd_event_type_t event_type;
+  union {
+    hsa_amd_gpu_memory_fault_info_t memory_fault;
+  };
+} hsa_amd_event_t;
+
+typedef hsa_status_t (*hsa_amd_system_event_callback_t)(
+    const hsa_amd_event_t* event, void* data);
+
+hsa_status_t hsa_amd_memory_pool_allocate(hsa_amd_memory_pool_t memory_pool,
+                                          size_t size, uint32_t flags,
+                                          void** ptr);
+hsa_status_t hsa_amd_memory_pool_free(void* ptr);
+hsa_status_t hsa_amd_agents_allow_access(uint32_t num_agents,
+                                         const hsa_agent_t* agents,
+                                         const uint32_t* flags,
+                                         const void* ptr);
+hsa_status_t hsa_amd_memory_async_copy(void* dst, hsa_agent_t dst_agent,
+                                       const void* src, hsa_agent_t src_agent,
+                                       size_t size, uint32_t num_dep_signals,
+                                       const hsa_signal_t* dep_signals,
+                                       hsa_signal_t completion_signal);
+hsa_status_t hsa_amd_memory_async_copy_on_engine(
+    void* dst, hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
+    size_t size, uint32_t num_dep_signals, const hsa_signal_t* dep_signals,
+    hsa_signal_t completion_signal, hsa_amd_sdma_engine_id_t engine_id,
+    bool force_copy_on_sdma);
+hsa_status_t hsa_amd_ipc_memory_create(void* ptr, size_t len,
+                                       hsa_amd_ipc_memory_t* handle);
+hsa_status_t hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
+                                       size_t len, uint32_t num_agents,
+                                       const hsa_agent_t* mapping_agents,
+                                       void** mapped_ptr);
+hsa_status_t hsa_amd_ipc_memory_detach(void* mapped_ptr);
+hsa_status_t hsa_amd_vmem_address_reserve_align(void** va, size_t size,
+                                                uint64_t address,
+                                                uint64_t alignment,
+                                                uint64_t flags);
+hsa_status_t hsa_amd_vmem_address_free(void* va, size_t size);
+hsa_status_t hsa_amd_pointer_info(const void* ptr, hsa_amd_pointer_info_t* info,
+                                  void* (*alloc)(size_t),
+                                  uint32_t* num_agents_accessible,
+                                  hsa_agent_t** accessible);
+hsa_status_t hsa_amd_register_system_event_handler(
+    hsa_amd_system_event_callback_t callback, void* data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // SANITIZER_HSA_HSA_EXT_AMD_H_
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index 88d109dffd74a..455f2f33ab486 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -2901,7 +2901,7 @@ void OnDlOpen(const char* filename, int flag) {
 #  endif
 }
 
-#  if SANITIZER_AMDGPU
+#  if SANITIZER_AMDHSA
 void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag) {
   if (filename &&
       (internal_strstr(filename, "libamdhip64.so") ||
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
index 6b5ffd0af9595..2fb689916d62d 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
@@ -315,12 +315,16 @@
 #  define SANITIZER_ALPHA 0
 #endif
 
-#if SANITIZER_AMDGPU || defined(__AMDGPU__)
+#if defined(__AMDGPU__)
 #  define SANITIZER_AMDGPU 1
 #else
 #  define SANITIZER_AMDGPU 0
 #endif
 
+#ifndef SANITIZER_AMDHSA
+#  define SANITIZER_AMDHSA 0
+#endif
+
 #if defined(__NVPTX__)
 #  define SANITIZER_NVPTX 1
 #else
diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
index b8c58041d31a4..ee1a08b878e77 100644
--- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
@@ -80,11 +80,10 @@ set(SANITIZER_TEST_CFLAGS_COMMON
   -Wno-gnu-zero-variadic-macro-arguments
   )
 
-# Do not pass -DSANITIZER_AMDGPU=1 to all unittests: that would pull <hsa.h>
-# and AMDGPU-only types into every TU without ROCm include paths. When
-# SANITIZER_AMDGPU is enabled, skip the NolibcMain subprocess test instead
-# (see NOT SANITIZER_AMDGPU targets below and sanitizer_nolibc_test.cpp).
-if(SANITIZER_AMDGPU)
+# Host HSA support (SANITIZER_AMDHSA) does not affect unittest compile flags.
+# When SANITIZER_AMDHSA is enabled, skip the NolibcMain subprocess test instead
+# (see NOT SANITIZER_AMDHSA targets below and sanitizer_nolibc_test.cpp).
+if(SANITIZER_AMDHSA)
   list(APPEND SANITIZER_TEST_CFLAGS_COMMON
        -DCOMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST=1)
 endif()
@@ -193,11 +192,11 @@ macro(add_sanitizer_tests_for_arch arch)
     LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS_COMMON} ${TARGET_LINK_FLAGS} ${extra_flags})
 
   if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64"
-     AND NOT SANITIZER_AMDGPU)
+     AND NOT SANITIZER_AMDHSA)
     # Test that the libc-independent part of sanitizer_common is indeed
     # independent of libc, by linking this binary without libc (here) and
     # executing it (unit test in sanitizer_nolibc_test.cpp). Omitted when
-    # SANITIZER_AMDGPU: AMDGPU runtime code needs libdl / libcdep and cannot
+    # SANITIZER_AMDHSA: AMDGPU runtime code needs libdl / libcdep and cannot
     # link with -nostdlib.
     get_target_flags_for_arch(${arch} TARGET_FLAGS)
     clang_compile(sanitizer_nolibc_test_main.${arch}.o
@@ -224,7 +223,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
                              $<TARGET_OBJECTS:RTSanitizerCommonLibc.osx>
                              $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.osx>)
   else()
-    if(CAN_TARGET_x86_64 AND NOT SANITIZER_AMDGPU)
+    if(CAN_TARGET_x86_64 AND NOT SANITIZER_AMDHSA)
       add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64"
                                $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
                                $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.x86_64>)
diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
index b17cc731aaa0a..9f44263dc65ad 100644
--- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
+++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
@@ -19,7 +19,7 @@
 
 extern const char *argv0;
 
-// When SANITIZER_AMDGPU is enabled, CMake defines this macro and does not build
+// When SANITIZER_AMDHSA is enabled, CMake defines this macro and does not build
 // Sanitizer-*-Test-Nolibc (see tests/CMakeLists.txt).
 #if SANITIZER_LINUX && defined(__x86_64__) && \
     !defined(COMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST)
diff --git a/compiler-rt/test/CMakeLists.txt b/compiler-rt/test/CMakeLists.txt
index 0b28a532f8e22..c885e1fb0e849 100644
--- a/compiler-rt/test/CMakeLists.txt
+++ b/compiler-rt/test/CMakeLists.txt
@@ -16,7 +16,7 @@ pythonize_bool(COMPILER_RT_HAS_AARCH64_SME)
 
 pythonize_bool(COMPILER_RT_HAS_NO_DEFAULT_CONFIG_FLAG)
 
-pythonize_bool(SANITIZER_AMDGPU)
+pythonize_bool(SANITIZER_AMDHSA)
 
 if(LLVM_TREE_AVAILABLE OR NOT COMPILER_RT_STANDALONE_BUILD)
   set(COMPILER_RT_BUILT_WITH_LLVM TRUE)
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
index fc2aa6def57e4..daa89c52a2a9c 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
@@ -1,7 +1,7 @@
 # Link against ROCm's HSA runtime. Tests under TestCases/AMDGPU run only when
 # lit finds a ROCm install (see lit.local.cfg.py): $ROCM_PATH or /opt/rocm,
 # with include/hsa/hsa.h and libhsa-runtime64. Compiler-rt must be built with
-# SANITIZER_AMDGPU enabled. The suite uses the dynamic ASan runtime only.
+# SANITIZER_AMDHSA enabled. The suite uses the dynamic ASan runtime only.
 
 import glob
 import os
diff --git a/compiler-rt/test/lit.common.configured.in b/compiler-rt/test/lit.common.configured.in
index 0dde837fa52df..186041654cce5 100644
--- a/compiler-rt/test/lit.common.configured.in
+++ b/compiler-rt/test/lit.common.configured.in
@@ -26,7 +26,7 @@ set_default("clang", "@COMPILER_RT_RESOLVED_TEST_COMPILER@")
 set_default("compiler_id", "@COMPILER_RT_TEST_COMPILER_ID@")
 set_default("python_executable", "@Python3_EXECUTABLE@")
 set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@)
-set_default("sanitizer_amdgpu", @SANITIZER_AMDGPU_PYBOOL@)
+set_default("sanitizer_amdgpu", @SANITIZER_AMDHSA_PYBOOL@)
 set_default("compiler_rt_intercept_libdispatch", @COMPILER_RT_INTERCEPT_LIBDISPATCH_PYBOOL@)
 set_default("compiler_rt_output_dir", "@COMPILER_RT_RESOLVED_OUTPUT_DIR@")
 set_default("compiler_rt_bindir", "@COMPILER_RT_RESOLVED_EXEC_OUTPUT_DIR@")

>From 5b65ec35a11fa82398be464de1e9434bc02e010b Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Wed, 3 Jun 2026 13:55:51 +0530
Subject: [PATCH 30/46] Fix rebase conflict.

Rebase #PR 200748 changes.
Rebase #PR 201081 changes.
---
 compiler-rt/CMakeLists.txt                           | 8 +++-----
 compiler-rt/lib/sanitizer_common/sanitizer_common.h  | 6 ------
 compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp | 6 ++----
 3 files changed, 5 insertions(+), 15 deletions(-)

diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index f8b2c0d746e3a..fe6f1941018f6 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -227,6 +227,9 @@ endif()
 
 option(SANITIZER_ALLOW_CXXABI "Allow use of C++ ABI details in ubsan" ON)
 
+option(SANITIZER_AMDHSA
+  "Build host ASan with ROCm HSA API interceptors" OFF)
+
 set(SANITIZER_CAN_USE_CXXABI OFF)
 if (cxxabi_supported AND SANITIZER_ALLOW_CXXABI)
   set(SANITIZER_CAN_USE_CXXABI ON)
@@ -561,11 +564,6 @@ elseif(COMPILER_RT_HAS_G_FLAG)
   list(APPEND SANITIZER_COMMON_CFLAGS -g)
 endif()
 
-if(SANITIZER_AMDGPU AND NOT SANITIZER_AMDHSA)
-  set(SANITIZER_AMDHSA TRUE CACHE BOOL
-      "Enable ROCm HSA API interception in ASan" FORCE)
-endif()
-
 if(SANITIZER_AMDHSA)
   list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDHSA=1)
 endif()
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
index b33a1a85ba3fe..6f76d10a28cf6 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h
@@ -1096,12 +1096,6 @@ const s32 kReleaseToOSIntervalNever = -1;
 // checks (e.g. RTLD_DEEPBIND on Linux).
 void OnDlOpen(const char* filename, int flag);
 
-#if SANITIZER_AMDHSA
-void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag);
-#else
-inline void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag) {}
-#endif
-
 // Returns the requested amount of random data (up to 256 bytes) that can then
 // be used to seed a PRNG. Defaults to blocking like the underlying syscall.
 bool GetRandom(void *buffer, uptr length, bool blocking = true);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index 455f2f33ab486..1722ecb90220e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -2899,10 +2899,8 @@ void OnDlOpen(const char* filename, int flag) {
     Die();
   }
 #  endif
-}
 
-#  if SANITIZER_AMDHSA
-void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag) {
+#  ifdef SANITIZER_AMDHSA
   if (filename &&
       (internal_strstr(filename, "libamdhip64.so") ||
        internal_strstr(filename, "libhsa-runtime64.so") ||
@@ -2916,8 +2914,8 @@ void PatchHsaRuntimeDlopenFlag(const char* filename, int& flag) {
           filename);
     }
   }
-}
 #  endif
+}
 
 uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
                               uptr *largest_gap_found,

>From a54ac318c27ca8e9d1607afbadc109e07b87fbd4 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 8 Jun 2026 12:01:52 +0530
Subject: [PATCH 31/46] [compiler-rt][asan][AMDGPU] Bind ROCr via dlopen handle
 instead of RTLD_NEXT.

Load hsa_amd_* entry points with dlopen(RTLD_NOLOAD) and dlsym on the
ROCr handle after REAL(hsa_init), avoiding RTLD_NEXT which resolves ASan
interceptors and can recurse into the device allocator. Defer binding
with Init(false) during __asan_init and call Init(true) from
asan_hsa_init; guard Allocate when symbols are not loaded yet.

Remove the OnDlOpen RTLD_GLOBAL shim for HSA/HIP/OpenCL now that symbols
are resolved from libhsa-runtime64.so directly.

Fix VmemGpuReserveTracker singleton init for standalone runtimes (no
__cxa_guard_* / lazy EnsureInited).
---
 compiler-rt/lib/asan/asan_allocator.cpp       |   2 +-
 .../sanitizer_allocator_amdgpu.cpp            | 118 ++++++++++++++----
 .../sanitizer_allocator_amdgpu.h              |   8 +-
 .../lib/sanitizer_common/sanitizer_linux.cpp  |  16 ---
 4 files changed, 102 insertions(+), 42 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index cabec24177a66..2765398d123f0 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1786,7 +1786,7 @@ hsa_status_t asan_hsa_init() {
       __sanitizer::AmdgpuDeviceAllocator::ClearRuntimeShutdownState();
     // Load HSA entry points once the runtime is up; device allocator may stay
     // disabled, but interceptors and RegisterSystemEventHandlers need them.
-    if (__sanitizer::AmdgpuDeviceAllocator::Init())
+    if (__sanitizer::AmdgpuDeviceAllocator::Init(/*allow_dlopen=*/true))
       __sanitizer::AmdgpuDeviceAllocator::RegisterSystemEventHandlers();
   }
   return status;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 995afafc82183..057f8f85b3c5e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #if SANITIZER_AMDHSA
-#  include <dlfcn.h>  // For dlsym
+#  include <dlfcn.h>  // For dlopen, dlsym
 
 #  include "sanitizer_allocator.h"
 #  include "sanitizer_atomic.h"
@@ -38,18 +38,24 @@ struct HsaFunctions {
 
 static HsaFunctions hsa_amd;
 
+// ROCr handle
+static void* hsa_runtime_handle = nullptr;
+// Mutex to protect the ROCr handle
+static StaticSpinMutex hsa_runtime_init_mu;
+
+// Default ROCr library path
+static const char kDefaultHsaRuntimePath[] = "libhsa-runtime64.so";
+
 // Always align to page boundary to match current ROCr behavior
 static const size_t kPageSize_ = 4096;
 
 static atomic_uint8_t amdgpu_runtime_shutdown{0};
 static atomic_uint8_t amdgpu_event_registered{0};
+static atomic_uint8_t hsa_amd_symbols_loaded{0};
 
-#  define LOAD_HSA_FUNC_WITH_ERROR_CHECK(func, name, success)         \
-    func = (decltype(func))dlsym(RTLD_NEXT, name);                    \
-    if (!func) {                                                      \
-      VReport(2, "Amdgpu Init: Failed to load " #name " function\n"); \
-      success = false;                                                \
-    }
+static bool HsaSymbolsLoaded() {
+  return atomic_load(&hsa_amd_symbols_loaded, memory_order_acquire) != 0;
+}
 
 bool AmdgpuDeviceAllocator::IsRuntimeShutdown() {
   return static_cast<bool>(
@@ -89,25 +95,70 @@ void AmdgpuDeviceAllocator::NoteDeviceAllocatorFailure(
   }
 }
 
-bool AmdgpuDeviceAllocator::Init() {
+// Init(false): no-op during __asan_init (no dlopen, no dlsym).
+// Init(true): from asan_hsa_init() after REAL(hsa_init); ROCr may be up while
+// this still fails (hsa_init can succeed with an unloaded device backend).
+// Bind hsa_amd_* via dlopen(RTLD_NOLOAD) + dlsym(handle) — not REAL() here
+// and not RTLD_NEXT/DEFAULT (those resolve ASan interceptors and recurse).
+bool AmdgpuDeviceAllocator::Init(bool allow_dlopen) {
+  SpinMutexLock l(&hsa_runtime_init_mu);
+  if (HsaSymbolsLoaded())
+    return true;
+
+  // Never dlopen during __asan_init.
+  if (!allow_dlopen)
+    return false;
+
+  if (!hsa_runtime_handle) {
+    const char* path = GetEnv("SANITIZER_HSA_RUNTIME_PATH");
+    if (!path || !path[0])
+      path = kDefaultHsaRuntimePath;
+    // Prefer first `RTLD_NOLOAD` when ROCr is already mapped(after successful
+    // REAL(hsa_init)). If RTLD_NOLOAD fails, try `RTLD_NOW` (when ROCr is not
+    // loaded).
+    hsa_runtime_handle = dlopen(path, RTLD_LAZY | RTLD_NOLOAD);
+    if (!hsa_runtime_handle)
+      hsa_runtime_handle = dlopen(path, RTLD_NOW);
+    if (!hsa_runtime_handle) {
+      const char* err = dlerror();
+      VReport(2, "Amdgpu Init: dlopen(%s) failed: %s\n", path,
+              err ? err : "unknown error");
+      return false;
+    }
+  }
+
+  struct HsaSymbolEntry {
+    const char* name;
+    void** slot;
+  };
+  // HSA Symbol Entries Table
+  const HsaSymbolEntry kSymbols[] = {
+      {"hsa_amd_memory_pool_allocate", (void**)&hsa_amd.memory_pool_allocate},
+      {"hsa_amd_memory_pool_free", (void**)&hsa_amd.memory_pool_free},
+      {"hsa_amd_pointer_info", (void**)&hsa_amd.pointer_info},
+      {"hsa_amd_vmem_address_reserve_align",
+       (void**)&hsa_amd.vmem_address_reserve_align},
+      {"hsa_amd_vmem_address_free", (void**)&hsa_amd.vmem_address_free},
+      {"hsa_amd_register_system_event_handler",
+       (void**)&hsa_amd.register_system_event_handler},
+  };
+
   bool success = true;
-  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.memory_pool_allocate,
-                                 "hsa_amd_memory_pool_allocate", success);
-  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.memory_pool_free,
-                                 "hsa_amd_memory_pool_free", success);
-  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.pointer_info, "hsa_amd_pointer_info",
-                                 success);
-  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.vmem_address_reserve_align,
-                                 "hsa_amd_vmem_address_reserve_align", success);
-  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.vmem_address_free,
-                                 "hsa_amd_vmem_address_free", success);
-  LOAD_HSA_FUNC_WITH_ERROR_CHECK(hsa_amd.register_system_event_handler,
-                                 "hsa_amd_register_system_event_handler",
-                                 success);
+  for (uptr i = 0; i < ARRAY_SIZE(kSymbols); ++i) {
+    void* sym = dlsym(hsa_runtime_handle, kSymbols[i].name);
+    if (!sym) {
+      VReport(2, "Amdgpu Init: Failed to load %s from ROCr handle\n",
+              kSymbols[i].name);
+      success = false;
+    }
+    *kSymbols[i].slot = sym;
+  }
   if (!success) {
+    internal_memset(&hsa_amd, 0, sizeof(hsa_amd));
     VReport(1, "Amdgpu Init: Failed to load AMDGPU runtime functions\n");
     return false;
   }
+  atomic_store(&hsa_amd_symbols_loaded, 1, memory_order_release);
   return true;
 }
 
@@ -126,6 +177,11 @@ void* AmdgpuDeviceAllocator::Allocate(uptr size, uptr alignment,
     return nullptr;
   }
 
+  if (!HsaSymbolsLoaded()) {
+    aa_info->EnsureFailureStatus(HSA_STATUS_ERROR_NOT_INITIALIZED);
+    return nullptr;
+  }
+
   if (!aa_info->memory_pool.handle) {
     aa_info->status = hsa_amd.vmem_address_reserve_align(
         &aa_info->ptr, size, aa_info->address, aa_info->alignment,
@@ -258,12 +314,26 @@ void AmdgpuDeviceAllocator::RegisterSystemEventHandlers() {
 
 uptr AmdgpuDeviceAllocator::GetPageSize() { return kPageSize_; }
 
+// File-scope singleton: function-local static would emit __cxa_guard_* calls
+// that are unavailable when this TU is linked into standalone runtimes.
+static VmemGpuReserveTracker vmem_gpu_reserve_tracker;
+
 VmemGpuReserveTracker& VmemGpuReserveTracker::Get() {
-  static VmemGpuReserveTracker tracker;
-  return tracker;
+  return vmem_gpu_reserve_tracker;
+}
+
+void VmemGpuReserveTracker::EnsureInited() {
+  if (atomic_load(&inited_, memory_order_acquire))
+    return;
+  SpinMutexLock l(&mu_);
+  if (atomic_load(&inited_, memory_order_relaxed))
+    return;
+  reservations_.Initialize(0);
+  atomic_store(&inited_, 1, memory_order_release);
 }
 
 void VmemGpuReserveTracker::OnReserve(uptr ptr, uptr size) {
+  EnsureInited();
   SpinMutexLock l(&mu_);
   VmemGpuReservation entry;
   entry.ptr = ptr;
@@ -274,6 +344,7 @@ void VmemGpuReserveTracker::OnReserve(uptr ptr, uptr size) {
 
 VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::CheckFree(uptr ptr,
                                                                    uptr size) {
+  EnsureInited();
   SpinMutexLock l(&mu_);
   for (uptr i = 0; i < reservations_.size(); ++i) {
     const VmemGpuReservation& r = reservations_[i];
@@ -289,6 +360,7 @@ VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::CheckFree(uptr ptr,
 }
 
 void VmemGpuReserveTracker::MarkFreed(uptr ptr, uptr size) {
+  EnsureInited();
   SpinMutexLock l(&mu_);
   for (uptr i = 0; i < reservations_.size(); ++i) {
     VmemGpuReservation& r = reservations_[i];
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index babaadad697be..dba657175b6cb 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -20,7 +20,7 @@ static const DeviceAllocationType DAT_AMDGPU =
 
 class AmdgpuDeviceAllocator {
  public:
-  static bool Init();
+  static bool Init(bool allow_dlopen = false);
   static void* Allocate(uptr size, uptr alignment,
                         DeviceAllocationInfo* da_info);
   static void Deallocate(void* p);
@@ -64,8 +64,11 @@ class VmemGpuReserveTracker {
     bool freed;
   };
 
+  void EnsureInited();
+
   StaticSpinMutex mu_;
-  InternalMmapVector<VmemGpuReservation> reservations_;
+  InternalMmapVectorNoCtor<VmemGpuReservation> reservations_;
+  atomic_uint8_t inited_;
 };
 
 // True when ROCr reserves host-accessible VA (e.g. HIP managed / SVM).
@@ -84,6 +87,7 @@ struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
     if (status == HSA_STATUS_SUCCESS)
       status = err;
   }
+
   hsa_status_t status;
   void* alloc_func;
   hsa_amd_memory_pool_t memory_pool;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index 1722ecb90220e..6f0259f31dbf5 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -2899,22 +2899,6 @@ void OnDlOpen(const char* filename, int flag) {
     Die();
   }
 #  endif
-
-#  ifdef SANITIZER_AMDHSA
-  if (filename &&
-      (internal_strstr(filename, "libamdhip64.so") ||
-       internal_strstr(filename, "libhsa-runtime64.so") ||
-       internal_strstr(filename, "libamdocl64.so")) &&
-      !(flag & RTLD_GLOBAL)) {
-    flag |= RTLD_GLOBAL;
-    if (Verbosity() >= 2) {
-      Printf(
-          "RTLD_GLOBAL flag on dlopen call forced on for %s due to AMDGPU "
-          "device sanitizer runtime requirements.\n",
-          filename);
-    }
-  }
-#  endif
 }
 
 uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,

>From 449ee4d7997341f28d48960d842bfdb0bda832de Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Mon, 8 Jun 2026 19:32:21 +0530
Subject: [PATCH 32/46] [compiler-rt][asan][HSA] Move HSA allocation wrappers
 into `asan_hsa_allocator.cpp`

Split the AMDGPU HSA allocation wrappers out of asan_allocator.cpp into
asan_hsa_allocator.cpp/.h so interceptor setup (asan_hsa_linux.cpp)
stays separate from ROCr mapping logic. Expose minimal AsanHsa* hooks
from the allocator so the new TU can still translate between
ROCr-visible bases and ASan user pointers without duplicating allocator
internals.
---
 compiler-rt/lib/asan/CMakeLists.txt         |   2 +
 compiler-rt/lib/asan/asan_allocator.cpp     | 325 +++-----------------
 compiler-rt/lib/asan/asan_allocator.h       |  14 +
 compiler-rt/lib/asan/asan_hsa_allocator.cpp | 304 ++++++++++++++++++
 compiler-rt/lib/asan/asan_hsa_allocator.h   |  60 ++++
 compiler-rt/lib/asan/asan_hsa_linux.h       |  36 +--
 6 files changed, 420 insertions(+), 321 deletions(-)
 create mode 100644 compiler-rt/lib/asan/asan_hsa_allocator.cpp
 create mode 100644 compiler-rt/lib/asan/asan_hsa_allocator.h

diff --git a/compiler-rt/lib/asan/CMakeLists.txt b/compiler-rt/lib/asan/CMakeLists.txt
index 2e36e6a07904c..2076c488bfba7 100644
--- a/compiler-rt/lib/asan/CMakeLists.txt
+++ b/compiler-rt/lib/asan/CMakeLists.txt
@@ -12,6 +12,7 @@ set(ASAN_SOURCES
   asan_fuchsia.cpp
   asan_globals.cpp
   asan_globals_win.cpp
+  asan_hsa_allocator.cpp
   asan_hsa_linux.cpp
   asan_interceptors.cpp
   asan_interceptors_memintrinsics.cpp
@@ -81,6 +82,7 @@ SET(ASAN_HEADERS
   asan_fake_stack.h
   asan_flags.h
   asan_flags.inc
+  asan_hsa_allocator.h
   asan_hsa_linux.h
   asan_init_version.h
   asan_interceptors.h
diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 2765398d123f0..547037c283fd1 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1483,313 +1483,66 @@ int __asan_update_allocation_context(void* addr) {
 
 #if SANITIZER_AMDHSA
 
-// Pull in the in-tree HSA type stubs for the wrapper implementations below.
-#  include "sanitizer_common/sanitizer_hsa.h"
-
 namespace __asan {
 
-DECLARE_REAL(hsa_status_t, hsa_init);
-DECLARE_REAL(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
-             const hsa_agent_t* agents, const uint32_t* flags, const void* ptr)
-DECLARE_REAL(hsa_status_t, hsa_amd_memory_pool_allocate,
-             hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
-             void** ptr)
-DECLARE_REAL(hsa_status_t, hsa_amd_memory_pool_free, void* ptr)
-DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
-             hsa_amd_ipc_memory_t* handle)
-DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_attach,
-             const hsa_amd_ipc_memory_t* handle, size_t len,
-             uint32_t num_agents, const hsa_agent_t* mapping_agents,
-             void** mapped_ptr)
-DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_detach, void* mapped_ptr)
-DECLARE_REAL(hsa_status_t, hsa_amd_vmem_address_reserve_align, void** ptr,
-             size_t size, uint64_t address, uint64_t alignment, uint64_t flags)
-DECLARE_REAL(hsa_status_t, hsa_amd_vmem_address_free, void* ptr, size_t size)
-DECLARE_REAL(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
-             hsa_amd_pointer_info_t* info, void* (*alloc)(size_t),
-             uint32_t* num_agents_accessible, hsa_agent_t** accessible)
-DECLARE_REAL(hsa_status_t, hsa_amd_register_system_event_handler,
-             hsa_amd_system_event_callback_t, void*)
-
-// HSA allocation wrappers live in this TU (not `asan_hsa_linux.cpp`) because
-// they need access to ASan allocator internals (e.g. `instance`,
-// `get_allocator()`, `AsanChunk` layout) to translate between ROCr-visible
-// mappings and ASan user pointers / metadata.
-//
-// Always align to page boundary to match current ROCr behavior.
-static const size_t kPageSize_ = 4096;
-
-hsa_status_t asan_hsa_amd_memory_pool_allocate(
-    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
-    BufferedStackTrace* stack) {
-  AmdgpuAllocationInfo aa_info;
-  aa_info.alloc_func =
-      reinterpret_cast<void*>((uptr)&__asan::asan_hsa_amd_memory_pool_allocate);
-  aa_info.memory_pool = memory_pool;
-  aa_info.size = size;
-  aa_info.flags = flags;
-  aa_info.ptr = nullptr;
-  SetErrnoOnNull(*ptr = instance.Allocate(size, kPageSize_, stack, FROM_MALLOC,
-                                          false, &aa_info));
-  return aa_info.status;
-}
-
-hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
-                                           BufferedStackTrace* stack) {
-  void* p = get_allocator().GetBlockBegin(ptr);
-  if (p) {
-    instance.Deallocate(ptr, 0, 0, stack, FROM_MALLOC);
-    return HSA_STATUS_SUCCESS;
-  }
-  return REAL(hsa_amd_memory_pool_free)(ptr);
+void* AsanHsaAllocate(uptr size, uptr alignment, BufferedStackTrace* stack,
+                      DeviceAllocationInfo* da_info) {
+  return instance.Allocate(size, alignment, stack, FROM_MALLOC, false, da_info);
 }
 
-hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
-                                              const hsa_agent_t* agents,
-                                              const uint32_t* flags,
-                                              const void* ptr,
-                                              BufferedStackTrace* stack) {
-  (void)stack;
-  void* p = get_allocator().GetBlockBegin(ptr);
-  return REAL(hsa_amd_agents_allow_access)(num_agents, agents, flags,
-                                           p ? p : ptr);
+void AsanHsaDeallocate(void* ptr, BufferedStackTrace* stack) {
+  instance.Deallocate(ptr, 0, 0, stack, FROM_MALLOC);
+}
+
+void* AsanHsaGetBlockBegin(const void* ptr) {
+  return get_allocator().GetBlockBegin(ptr);
 }
 
-#  if SANITIZER_CAN_USE_ALLOCATOR64
-static struct __asan::AP64<LocalAddressSpaceView> AP_;
-#  else
-static struct __asan::AP32<LocalAddressSpaceView> AP_;
-#  endif
+uptr AsanHsaGetActuallyAllocatedSize(void* ptr) {
+  return get_allocator().GetActuallyAllocatedSize(ptr);
+}
 
-hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
-                                            hsa_amd_ipc_memory_t* handle) {
+bool AsanHsaTranslateIpcCreate(void* ptr, size_t len, void** out_ptr,
+                               size_t* out_len) {
+  static const uptr kHsaPageSize = 4096;
   void* ptr_ = get_allocator().GetBlockBegin(ptr);
   AsanChunk* m = ptr_
                      ? instance.GetAsanChunkByAddr(reinterpret_cast<uptr>(ptr_))
                      : nullptr;
-  if (ptr_ && m) {
-    static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
-    uptr p = reinterpret_cast<uptr>(ptr);
-    uptr p_ = reinterpret_cast<uptr>(ptr_);
-    if (p == p_ + kPageSize_ && len == m->UsedSize()) {
-      size_t len_ = get_allocator().GetActuallyAllocatedSize(ptr_);
-      return REAL(hsa_amd_ipc_memory_create)(ptr_, len_, handle);
-    }
-  }
-  return REAL(hsa_amd_ipc_memory_create)(ptr, len, handle);
-}
-
-hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
-                                            size_t len, uint32_t num_agents,
-                                            const hsa_agent_t* mapping_agents,
-                                            void** mapped_ptr) {
-  static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
-  size_t len_ = len + kPageSize_;
-  hsa_status_t status = REAL(hsa_amd_ipc_memory_attach)(
-      handle, len_, num_agents, mapping_agents, mapped_ptr);
-  if (status == HSA_STATUS_SUCCESS && mapped_ptr) {
-    uptr mapped_base = reinterpret_cast<uptr>(*mapped_ptr);
-    uptr user_beg = mapped_base + kPageSize_;
-    uptr tail_beg = RoundUpTo(user_beg + len, ASAN_SHADOW_GRANULARITY);
-    uptr mapped_end = mapped_base + kPageSize_ + RoundUpTo(len, kPageSize_);
-
-    PoisonShadow(mapped_base, kPageSize_, kAsanHeapLeftRedzoneMagic);
-
-    if (mapped_end > tail_beg)
-      PoisonShadow(tail_beg, mapped_end - tail_beg, kAsanHeapLeftRedzoneMagic);
-
-    uptr size_rounded_down = RoundDownTo(len, ASAN_SHADOW_GRANULARITY);
-    if (size_rounded_down)
-      PoisonShadow(user_beg, size_rounded_down, 0);
-
-    if (len != size_rounded_down && CanPoisonMemory()) {
-      u8* shadow = (u8*)MemToShadow(user_beg + size_rounded_down);
-      *shadow = flags()->poison_partial
-                    ? static_cast<u8>(len & (ASAN_SHADOW_GRANULARITY - 1))
-                    : 0;
-    }
-
-    *mapped_ptr = reinterpret_cast<void*>(user_beg);
-  }
-  return status;
-}
-
-hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr) {
-  static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
-  uptr mapped_base = reinterpret_cast<uptr>(mapped_ptr) - kPageSize_;
-
-  // Snapshot mapping size before detach: ROCr may return an error while the
-  // import is still live (e.g. thunk_bo path if hsaKmtMemoryVaUnmap fails).
-  // Only clear ASan shadow after a successful detach so redzones stay valid
-  // if teardown did not complete.
-  hsa_amd_pointer_info_t info;
-  info.size = sizeof(hsa_amd_pointer_info_t);
-  uptr mapped_sz = 0;
-  if (REAL(hsa_amd_pointer_info)(reinterpret_cast<void*>(mapped_base), &info,
-                                 nullptr, nullptr,
-                                 nullptr) == HSA_STATUS_SUCCESS)
-    mapped_sz = static_cast<uptr>(info.sizeInBytes);
-
-  const hsa_status_t status =
-      REAL(hsa_amd_ipc_memory_detach)(reinterpret_cast<void*>(mapped_base));
-  if (status == HSA_STATUS_SUCCESS && mapped_sz) {
-    PoisonShadow(mapped_base, mapped_sz, 0);
-    FlushUnneededASanShadowMemory(mapped_base, mapped_sz);
-  }
-  return status;
-}
-
-hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
-    void** ptr, size_t size, uint64_t address, uint64_t alignment,
-    uint64_t flags, BufferedStackTrace* stack) {
-  // Bypass the tracking for a fixed address since it cannot be supported.
-  // Reasons:
-  //  1. Address may not meet the alignment/page-size requirement.
-  //  2. Requested range overlaps an existing reserved/mapped range.
-  //  3. Insufficient VA space to honor that exact placement.
-  if (address)
-    return REAL(hsa_amd_vmem_address_reserve_align)(ptr, size, address,
-                                                    alignment, flags);
-
-  if (alignment < kPageSize_)
-    alignment = kPageSize_;
-
-  if (UNLIKELY(!IsPowerOfTwo(alignment))) {
-    errno = errno_EINVAL;
-    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
-  }
-
-  if (!__sanitizer::AmdgpuVmemReserveUsesHostMapping(flags)) {
-    const hsa_status_t status = REAL(hsa_amd_vmem_address_reserve_align)(
-        ptr, size, address, alignment, flags);
-    if (status == HSA_STATUS_SUCCESS && ptr && *ptr)
-      __sanitizer::VmemGpuReserveTracker::Get().OnReserve(
-          reinterpret_cast<uptr>(*ptr), size);
-    return status;
-  }
-
-  AmdgpuAllocationInfo aa_info;
-  aa_info.alloc_func = reinterpret_cast<void*>(
-      (uptr)&__asan::asan_hsa_amd_vmem_address_reserve_align);
-  aa_info.memory_pool = {0};
-  aa_info.size = size;
-  aa_info.flags64 = flags;
-  aa_info.address = 0;
-  aa_info.alignment = alignment;
-  aa_info.ptr = nullptr;
-  SetErrnoOnNull(*ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC,
-                                          false, &aa_info));
-
-  return aa_info.status;
+  if (!ptr_ || !m)
+    return false;
+  uptr p = reinterpret_cast<uptr>(ptr);
+  uptr p_ = reinterpret_cast<uptr>(ptr_);
+  if (p != p_ + kHsaPageSize || len != m->UsedSize())
+    return false;
+  *out_ptr = ptr_;
+  *out_len = get_allocator().GetActuallyAllocatedSize(ptr_);
+  return true;
 }
 
-hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
-                                            BufferedStackTrace* stack) {
-  if (UNLIKELY(!IsAligned(reinterpret_cast<uptr>(ptr), kPageSize_))) {
-    errno = errno_EINVAL;
-    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
-  }
-  if (size == 0) {
-    errno = errno_EINVAL;
-    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
-  }
-
+bool AsanHsaIsVmemFreeValid(void* ptr, uptr size) {
   void* p = get_allocator().GetBlockBegin(ptr);
-  if (p) {
-    // Match ROCr: require the same va/size as reserve before freeing metadata.
-    uptr ptr_ = reinterpret_cast<uptr>(ptr);
-    AsanChunk* m = reinterpret_cast<AsanChunk*>(ptr_ - kChunkHeaderSize);
-    if (m->Beg() != ptr_ || size != m->UsedSize()) {
-      errno = errno_EINVAL;
-      return HSA_STATUS_ERROR_INVALID_ARGUMENT;
-    }
-    instance.Deallocate(ptr, 0, 0, stack, FROM_MALLOC);
-    return HSA_STATUS_SUCCESS;
-  }
+  if (!p)
+    return false;
+  uptr ptr_ = reinterpret_cast<uptr>(ptr);
+  AsanChunk* m = reinterpret_cast<AsanChunk*>(ptr_ - kChunkHeaderSize);
+  return m->Beg() == ptr_ && size == m->UsedSize();
+}
 
-  // GPU-only free: only supported when host-accessible VA (NO_REGISTER) is
-  // used.
-  using VmemGpuReserveTracker = __sanitizer::VmemGpuReserveTracker;
-  const uptr ptr_uptr = reinterpret_cast<uptr>(ptr);
-  switch (VmemGpuReserveTracker::Get().CheckFree(ptr_uptr, size)) {
-    case VmemGpuReserveTracker::kNotTracked:
-      break;
-    case VmemGpuReserveTracker::kFirstFree: {
-      const hsa_status_t status = REAL(hsa_amd_vmem_address_free)(ptr, size);
-      if (status == HSA_STATUS_SUCCESS)
-        VmemGpuReserveTracker::Get().MarkFreed(ptr_uptr, size);
-      return status;
-    }
-    case VmemGpuReserveTracker::kSizeMismatch:
-      errno = errno_EINVAL;
-      return HSA_STATUS_ERROR_INVALID_ARGUMENT;
-    case VmemGpuReserveTracker::kDoubleFree:
-      ReportDoubleFree(ptr_uptr, stack);
-      return HSA_STATUS_SUCCESS;
-  }
-  // Passthrough: untracked vmem frees (eg: fixed-address reservations) go
-  // straight to ROCr.
-  return REAL(hsa_amd_vmem_address_free)(ptr, size);
-}
-
-hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
-                                       hsa_amd_pointer_info_t* info,
-                                       void* (*alloc)(size_t),
-                                       uint32_t* num_agents_accessible,
-                                       hsa_agent_t** accessible) {
-  // Device/pool mappings are keyed by the ROCr reservation base (map_beg), not
-  // the user pointer returned from intercepted allocate/reserve.
+bool AsanHsaGetLiveMappingInfo(const void* ptr, void** map_base,
+                               uptr* used_size, uptr* offset) {
   void* hsa_map_base = get_allocator().GetBlockBegin(ptr);
   if (!hsa_map_base)
-    // Not tracked by ASan; query ROCr on the caller's pointer unchanged.
-    return REAL(hsa_amd_pointer_info)(ptr, info, alloc, num_agents_accessible,
-                                      accessible);
-
+    return false;
   uptr user = reinterpret_cast<uptr>(ptr);
   AsanChunk* m = reinterpret_cast<AsanChunk*>(user - kChunkHeaderSize);
   if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED ||
       m->Beg() != user)
-    // Inside a tracked mapping but not the live user base (redzone, interior,
-    // or freed); do not apply the user-visible pointer_info adjustment.
-    return REAL(hsa_amd_pointer_info)(ptr, info, alloc, num_agents_accessible,
-                                      accessible);
-
-  hsa_status_t status = REAL(hsa_amd_pointer_info)(
-      hsa_map_base, info, alloc, num_agents_accessible, accessible);
-  if (status == HSA_STATUS_SUCCESS && info) {
-    static_assert(AP_.kMetadataSize == 0, "Expression below requires this");
-    // User VA may be above hsa_map_base when VMem API's(i.e
-    // hsa_amd_vmem_address_reserve_align())  uses alignment > page size (device
-    // allocator padding and ASan redzones/headers).
-    const uptr offset = user - reinterpret_cast<uptr>(hsa_map_base);
-    // hostBaseAddress must reflect the user-visible reservation base (ROCr may
-    // report os_addr below an aligned user VA for NO_REGISTER reserves).
-    if (info->hostBaseAddress)
-      info->hostBaseAddress = reinterpret_cast<void*>(
-          reinterpret_cast<uptr>(info->hostBaseAddress) + offset);
-    // agentBaseAddress is NULL for unmapped RESERVED_ADDR (no GPU backing).
-    // Do not turn NULL + offset into a fake agent address.
-    if (info->agentBaseAddress)
-      info->agentBaseAddress = reinterpret_cast<void*>(
-          reinterpret_cast<uptr>(info->agentBaseAddress) + offset);
-    info->sizeInBytes = m->UsedSize();
-  }
-  return status;
-}
-
-hsa_status_t asan_hsa_init() {
-  hsa_status_t status = REAL(hsa_init)();
-  if (status == HSA_STATUS_SUCCESS) {
-    // Only clear state when recovering from a prior shutdown (avoids clearing
-    // amdgpu_event_registered on every refcount bump and re-registering).
-    if (__sanitizer::AmdgpuDeviceAllocator::IsRuntimeShutdown())
-      __sanitizer::AmdgpuDeviceAllocator::ClearRuntimeShutdownState();
-    // Load HSA entry points once the runtime is up; device allocator may stay
-    // disabled, but interceptors and RegisterSystemEventHandlers need them.
-    if (__sanitizer::AmdgpuDeviceAllocator::Init(/*allow_dlopen=*/true))
-      __sanitizer::AmdgpuDeviceAllocator::RegisterSystemEventHandlers();
-  }
-  return status;
+    return false;
+  *map_base = hsa_map_base;
+  *used_size = m->UsedSize();
+  *offset = user - reinterpret_cast<uptr>(hsa_map_base);
+  return true;
 }
 
 }  // namespace __asan
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index 9a8601f80430e..0ce2befc389d7 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -330,6 +330,20 @@ void asan_mz_force_unlock();
 void PrintInternalAllocatorStats();
 void AsanSoftRssLimitExceededCallback(bool exceeded);
 
+#if SANITIZER_AMDHSA
+// Hooks for asan_hsa_amd* wrappers.
+void* AsanHsaAllocate(uptr size, uptr alignment, BufferedStackTrace* stack,
+                      DeviceAllocationInfo* da_info);
+void AsanHsaDeallocate(void* ptr, BufferedStackTrace* stack);
+void* AsanHsaGetBlockBegin(const void* ptr);
+uptr AsanHsaGetActuallyAllocatedSize(void* ptr);
+bool AsanHsaTranslateIpcCreate(void* ptr, size_t len, void** out_ptr,
+                               size_t* out_len);
+bool AsanHsaIsVmemFreeValid(void* ptr, uptr size);
+bool AsanHsaGetLiveMappingInfo(const void* ptr, void** map_base,
+                               uptr* used_size, uptr* offset);
+#endif  // SANITIZER_AMDHSA
+
 }  // namespace __asan
 
 #endif  // ASAN_ALLOCATOR_H
diff --git a/compiler-rt/lib/asan/asan_hsa_allocator.cpp b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
new file mode 100644
index 0000000000000..149321b1a60d4
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
@@ -0,0 +1,304 @@
+//===-- asan_hsa_allocator.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
+//
+//===----------------------------------------------------------------------===//
+//
+// AMDGPU HSA allocation wrappers for AddressSanitizer (SANITIZER_AMDHSA).
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+
+#if SANITIZER_AMDHSA
+
+#  include "asan_allocator.h"
+#  include "asan_hsa_allocator.h"
+#  include "asan_interceptors.h"
+#  include "asan_internal.h"
+#  include "asan_mapping.h"
+#  include "asan_poisoning.h"
+#  include "asan_report.h"
+#  include "sanitizer_common/sanitizer_allocator_checks.h"
+#  include "sanitizer_common/sanitizer_errno.h"
+#  include "sanitizer_common/sanitizer_hsa.h"
+
+namespace __asan {
+
+DECLARE_REAL(hsa_status_t, hsa_init);
+DECLARE_REAL(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
+             const hsa_agent_t* agents, const uint32_t* flags, const void* ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_memory_pool_allocate,
+             hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
+             void** ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_memory_pool_free, void* ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
+             hsa_amd_ipc_memory_t* handle)
+DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_attach,
+             const hsa_amd_ipc_memory_t* handle, size_t len,
+             uint32_t num_agents, const hsa_agent_t* mapping_agents,
+             void** mapped_ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_ipc_memory_detach, void* mapped_ptr)
+DECLARE_REAL(hsa_status_t, hsa_amd_vmem_address_reserve_align, void** ptr,
+             size_t size, uint64_t address, uint64_t alignment, uint64_t flags)
+DECLARE_REAL(hsa_status_t, hsa_amd_vmem_address_free, void* ptr, size_t size)
+DECLARE_REAL(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
+             hsa_amd_pointer_info_t* info, void* (*alloc)(size_t),
+             uint32_t* num_agents_accessible, hsa_agent_t** accessible)
+
+// Always align to page boundary to match current ROCr behavior.
+static const size_t kPageSize_ = 4096;
+
+#  if SANITIZER_CAN_USE_ALLOCATOR64
+static_assert(AP64<LocalAddressSpaceView>::kMetadataSize == 0,
+              "HSA IPC/VMem wrappers require zero allocator metadata");
+#  else
+static_assert(AP32<LocalAddressSpaceView>::kMetadataSize == 0,
+              "HSA IPC/VMem wrappers require zero allocator metadata");
+#  endif
+
+hsa_status_t asan_hsa_amd_memory_pool_allocate(
+    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
+    BufferedStackTrace* stack) {
+  AmdgpuAllocationInfo aa_info;
+  aa_info.alloc_func =
+      reinterpret_cast<void*>((uptr)&__asan::asan_hsa_amd_memory_pool_allocate);
+  aa_info.memory_pool = memory_pool;
+  aa_info.size = size;
+  aa_info.flags = flags;
+  aa_info.ptr = nullptr;
+  SetErrnoOnNull(*ptr = AsanHsaAllocate(size, kPageSize_, stack, &aa_info));
+  return aa_info.status;
+}
+
+hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
+                                           BufferedStackTrace* stack) {
+  void* p = AsanHsaGetBlockBegin(ptr);
+  if (p) {
+    AsanHsaDeallocate(ptr, stack);
+    return HSA_STATUS_SUCCESS;
+  }
+  return REAL(hsa_amd_memory_pool_free)(ptr);
+}
+
+hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
+                                              const hsa_agent_t* agents,
+                                              const uint32_t* flags,
+                                              const void* ptr,
+                                              BufferedStackTrace* stack) {
+  (void)stack;
+  void* p = AsanHsaGetBlockBegin(ptr);
+  return REAL(hsa_amd_agents_allow_access)(num_agents, agents, flags,
+                                           p ? p : ptr);
+}
+
+hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
+                                            hsa_amd_ipc_memory_t* handle) {
+  void* ptr_;
+  size_t len_;
+  if (AsanHsaTranslateIpcCreate(ptr, len, &ptr_, &len_))
+    return REAL(hsa_amd_ipc_memory_create)(ptr_, len_, handle);
+  return REAL(hsa_amd_ipc_memory_create)(ptr, len, handle);
+}
+
+hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
+                                            size_t len, uint32_t num_agents,
+                                            const hsa_agent_t* mapping_agents,
+                                            void** mapped_ptr) {
+  size_t len_ = len + kPageSize_;
+  hsa_status_t status = REAL(hsa_amd_ipc_memory_attach)(
+      handle, len_, num_agents, mapping_agents, mapped_ptr);
+  if (status == HSA_STATUS_SUCCESS && mapped_ptr) {
+    uptr mapped_base = reinterpret_cast<uptr>(*mapped_ptr);
+    uptr user_beg = mapped_base + kPageSize_;
+    uptr tail_beg = RoundUpTo(user_beg + len, ASAN_SHADOW_GRANULARITY);
+    uptr mapped_end = mapped_base + kPageSize_ + RoundUpTo(len, kPageSize_);
+
+    PoisonShadow(mapped_base, kPageSize_, kAsanHeapLeftRedzoneMagic);
+
+    if (mapped_end > tail_beg)
+      PoisonShadow(tail_beg, mapped_end - tail_beg, kAsanHeapLeftRedzoneMagic);
+
+    uptr size_rounded_down = RoundDownTo(len, ASAN_SHADOW_GRANULARITY);
+    if (size_rounded_down)
+      PoisonShadow(user_beg, size_rounded_down, 0);
+
+    if (len != size_rounded_down && CanPoisonMemory()) {
+      u8* shadow = (u8*)MemToShadow(user_beg + size_rounded_down);
+      *shadow = flags()->poison_partial
+                    ? static_cast<u8>(len & (ASAN_SHADOW_GRANULARITY - 1))
+                    : 0;
+    }
+
+    *mapped_ptr = reinterpret_cast<void*>(user_beg);
+  }
+  return status;
+}
+
+hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr) {
+  uptr mapped_base = reinterpret_cast<uptr>(mapped_ptr) - kPageSize_;
+
+  // Snapshot mapping size before detach: ROCr may return an error while the
+  // import is still live (e.g. thunk_bo path if hsaKmtMemoryVaUnmap fails).
+  // Only clear ASan shadow after a successful detach so redzones stay valid
+  // if teardown did not complete.
+  hsa_amd_pointer_info_t info;
+  info.size = sizeof(hsa_amd_pointer_info_t);
+  uptr mapped_sz = 0;
+  if (REAL(hsa_amd_pointer_info)(reinterpret_cast<void*>(mapped_base), &info,
+                                 nullptr, nullptr,
+                                 nullptr) == HSA_STATUS_SUCCESS)
+    mapped_sz = static_cast<uptr>(info.sizeInBytes);
+
+  const hsa_status_t status =
+      REAL(hsa_amd_ipc_memory_detach)(reinterpret_cast<void*>(mapped_base));
+  if (status == HSA_STATUS_SUCCESS && mapped_sz) {
+    PoisonShadow(mapped_base, mapped_sz, 0);
+    FlushUnneededASanShadowMemory(mapped_base, mapped_sz);
+  }
+  return status;
+}
+
+hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
+    void** ptr, size_t size, uint64_t address, uint64_t alignment,
+    uint64_t flags, BufferedStackTrace* stack) {
+  // Bypass the tracking for a fixed address since it cannot be supported.
+  // Reasons:
+  //  1. Address may not meet the alignment/page-size requirement.
+  //  2. Requested range overlaps an existing reserved/mapped range.
+  //  3. Insufficient VA space to honor that exact placement.
+  if (address)
+    return REAL(hsa_amd_vmem_address_reserve_align)(ptr, size, address,
+                                                    alignment, flags);
+
+  if (alignment < kPageSize_)
+    alignment = kPageSize_;
+
+  if (UNLIKELY(!IsPowerOfTwo(alignment))) {
+    errno = errno_EINVAL;
+    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+  }
+
+  if (!__sanitizer::AmdgpuVmemReserveUsesHostMapping(flags)) {
+    const hsa_status_t status = REAL(hsa_amd_vmem_address_reserve_align)(
+        ptr, size, address, alignment, flags);
+    if (status == HSA_STATUS_SUCCESS && ptr && *ptr)
+      __sanitizer::VmemGpuReserveTracker::Get().OnReserve(
+          reinterpret_cast<uptr>(*ptr), size);
+    return status;
+  }
+
+  AmdgpuAllocationInfo aa_info;
+  aa_info.alloc_func = reinterpret_cast<void*>(
+      (uptr)&__asan::asan_hsa_amd_vmem_address_reserve_align);
+  aa_info.memory_pool = {0};
+  aa_info.size = size;
+  aa_info.flags64 = flags;
+  aa_info.address = 0;
+  aa_info.alignment = alignment;
+  aa_info.ptr = nullptr;
+  SetErrnoOnNull(*ptr = AsanHsaAllocate(size, alignment, stack, &aa_info));
+
+  return aa_info.status;
+}
+
+hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
+                                            BufferedStackTrace* stack) {
+  if (UNLIKELY(!IsAligned(reinterpret_cast<uptr>(ptr), kPageSize_))) {
+    errno = errno_EINVAL;
+    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+  }
+  if (size == 0) {
+    errno = errno_EINVAL;
+    return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+  }
+
+  if (AsanHsaGetBlockBegin(ptr)) {
+    if (!AsanHsaIsVmemFreeValid(ptr, size)) {
+      errno = errno_EINVAL;
+      return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+    }
+    AsanHsaDeallocate(ptr, stack);
+    return HSA_STATUS_SUCCESS;
+  }
+
+  // GPU-only free: only supported when host-accessible VA (NO_REGISTER) is
+  // used.
+  using VmemGpuReserveTracker = __sanitizer::VmemGpuReserveTracker;
+  const uptr ptr_uptr = reinterpret_cast<uptr>(ptr);
+  switch (VmemGpuReserveTracker::Get().CheckFree(ptr_uptr, size)) {
+    case VmemGpuReserveTracker::kNotTracked:
+      break;
+    case VmemGpuReserveTracker::kFirstFree: {
+      const hsa_status_t status = REAL(hsa_amd_vmem_address_free)(ptr, size);
+      if (status == HSA_STATUS_SUCCESS)
+        VmemGpuReserveTracker::Get().MarkFreed(ptr_uptr, size);
+      return status;
+    }
+    case VmemGpuReserveTracker::kSizeMismatch:
+      errno = errno_EINVAL;
+      return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+    case VmemGpuReserveTracker::kDoubleFree:
+      ReportDoubleFree(ptr_uptr, stack);
+      return HSA_STATUS_SUCCESS;
+  }
+  // Passthrough: untracked vmem frees (eg: fixed-address reservations) go
+  // straight to ROCr.
+  return REAL(hsa_amd_vmem_address_free)(ptr, size);
+}
+
+hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
+                                       hsa_amd_pointer_info_t* info,
+                                       void* (*alloc)(size_t),
+                                       uint32_t* num_agents_accessible,
+                                       hsa_agent_t** accessible) {
+  // Device/pool mappings are keyed by the ROCr reservation base (map_beg), not
+  // the user pointer returned from intercepted allocate/reserve.
+  void* hsa_map_base;
+  uptr used_size;
+  uptr offset;
+  if (!AsanHsaGetLiveMappingInfo(ptr, &hsa_map_base, &used_size, &offset))
+    return REAL(hsa_amd_pointer_info)(ptr, info, alloc, num_agents_accessible,
+                                      accessible);
+
+  hsa_status_t status = REAL(hsa_amd_pointer_info)(
+      hsa_map_base, info, alloc, num_agents_accessible, accessible);
+  if (status == HSA_STATUS_SUCCESS && info) {
+    // User VA may be above hsa_map_base when VMem API's(i.e
+    // hsa_amd_vmem_address_reserve_align())  uses alignment > page size (device
+    // allocator padding and ASan redzones/headers).
+    // hostBaseAddress must reflect the user-visible reservation base (ROCr may
+    // report os_addr below an aligned user VA for NO_REGISTER reserves).
+    if (info->hostBaseAddress)
+      info->hostBaseAddress = reinterpret_cast<void*>(
+          reinterpret_cast<uptr>(info->hostBaseAddress) + offset);
+    // agentBaseAddress is NULL for unmapped RESERVED_ADDR (no GPU backing).
+    // Do not turn NULL + offset into a fake agent address.
+    if (info->agentBaseAddress)
+      info->agentBaseAddress = reinterpret_cast<void*>(
+          reinterpret_cast<uptr>(info->agentBaseAddress) + offset);
+    info->sizeInBytes = used_size;
+  }
+  return status;
+}
+
+hsa_status_t asan_hsa_init() {
+  hsa_status_t status = REAL(hsa_init)();
+  if (status == HSA_STATUS_SUCCESS) {
+    // Only clear state when recovering from a prior shutdown (avoids clearing
+    // amdgpu_event_registered on every refcount bump and re-registering).
+    if (__sanitizer::AmdgpuDeviceAllocator::IsRuntimeShutdown())
+      __sanitizer::AmdgpuDeviceAllocator::ClearRuntimeShutdownState();
+    // Load HSA entry points once the runtime is up; device allocator may stay
+    // disabled, but interceptors and RegisterSystemEventHandlers need them.
+    if (__sanitizer::AmdgpuDeviceAllocator::Init(/*allow_dlopen=*/true))
+      __sanitizer::AmdgpuDeviceAllocator::RegisterSystemEventHandlers();
+  }
+  return status;
+}
+
+}  // namespace __asan
+
+#endif  // SANITIZER_AMDHSA
diff --git a/compiler-rt/lib/asan/asan_hsa_allocator.h b/compiler-rt/lib/asan/asan_hsa_allocator.h
new file mode 100644
index 0000000000000..7d5b7f4c174f2
--- /dev/null
+++ b/compiler-rt/lib/asan/asan_hsa_allocator.h
@@ -0,0 +1,60 @@
+//===-- asan_hsa_allocator.h ---------------------------------- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// AMDGPU HSA allocation wrappers for AddressSanitizer (SANITIZER_AMDHSA).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_HSA_ALLOCATOR_H
+#define ASAN_HSA_ALLOCATOR_H
+
+#include "sanitizer_common/sanitizer_platform.h"
+
+#if SANITIZER_AMDHSA
+
+#  include "asan_stack.h"
+#  include "sanitizer_common/sanitizer_hsa.h"
+
+namespace __asan {
+
+hsa_status_t asan_hsa_amd_memory_pool_allocate(
+    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
+    BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
+                                           BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
+                                              const hsa_agent_t* agents,
+                                              const uint32_t* flags,
+                                              const void* ptr,
+                                              BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
+                                            hsa_amd_ipc_memory_t* handle);
+hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
+                                            size_t len, uint32_t num_agents,
+                                            const hsa_agent_t* mapping_agents,
+                                            void** mapped_ptr);
+hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr);
+hsa_status_t asan_hsa_amd_vmem_address_reserve_align(void** ptr, size_t size,
+                                                     uint64_t address,
+                                                     uint64_t alignment,
+                                                     uint64_t flags,
+                                                     BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
+                                            BufferedStackTrace* stack);
+hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
+                                       hsa_amd_pointer_info_t* info,
+                                       void* (*alloc)(size_t),
+                                       uint32_t* num_agents_accessible,
+                                       hsa_agent_t** accessible);
+hsa_status_t asan_hsa_init();
+
+}  // namespace __asan
+
+#endif  // SANITIZER_AMDHSA
+
+#endif  // ASAN_HSA_ALLOCATOR_H
diff --git a/compiler-rt/lib/asan/asan_hsa_linux.h b/compiler-rt/lib/asan/asan_hsa_linux.h
index ca788a8f4bcc5..0daebc45e3473 100644
--- a/compiler-rt/lib/asan/asan_hsa_linux.h
+++ b/compiler-rt/lib/asan/asan_hsa_linux.h
@@ -13,48 +13,14 @@
 #ifndef ASAN_HSA_LINUX_H
 #define ASAN_HSA_LINUX_H
 
-#include "sanitizer_common/sanitizer_platform.h"
+#include "asan_hsa_allocator.h"
 
 #if SANITIZER_AMDHSA
 
-#  include "asan_stack.h"
-#  include "sanitizer_common/sanitizer_hsa.h"
-
 namespace __asan {
 
 void InitializeAmdgpuInterceptors();
 
-hsa_status_t asan_hsa_amd_memory_pool_allocate(
-    hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
-    BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_memory_pool_free(void* ptr,
-                                           BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_agents_allow_access(uint32_t num_agents,
-                                              const hsa_agent_t* agents,
-                                              const uint32_t* flags,
-                                              const void* ptr,
-                                              BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_ipc_memory_create(void* ptr, size_t len,
-                                            hsa_amd_ipc_memory_t* handle);
-hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
-                                            size_t len, uint32_t num_agents,
-                                            const hsa_agent_t* mapping_agents,
-                                            void** mapped_ptr);
-hsa_status_t asan_hsa_amd_ipc_memory_detach(void* mapped_ptr);
-hsa_status_t asan_hsa_amd_vmem_address_reserve_align(void** ptr, size_t size,
-                                                     uint64_t address,
-                                                     uint64_t alignment,
-                                                     uint64_t flags,
-                                                     BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
-                                            BufferedStackTrace* stack);
-hsa_status_t asan_hsa_amd_pointer_info(const void* ptr,
-                                       hsa_amd_pointer_info_t* info,
-                                       void* (*alloc)(size_t),
-                                       uint32_t* num_agents_accessible,
-                                       hsa_agent_t** accessible);
-hsa_status_t asan_hsa_init();
-
 }  // namespace __asan
 
 #endif  // SANITIZER_AMDHSA

>From 3495f2785f81af75787215d36a4d6b72f61dc464 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Thu, 11 Jun 2026 17:06:21 +0530
Subject: [PATCH 33/46] [compiler-rt][asan][AMDGPU] Make CombinedAllocator's
 device leg a template parameter

Expose the device heap as CombinedAllocator's third template argument
(default DeviceAllocatorT with NoOpDeviceAllocator). ASan AMDGPU selects
AmdgpuDeviceAllocatorT<Primary> from asan_allocator.h instead of pulling
amdgpu typedefs through sanitizer_allocator.h.

Drop the Init enable boolean; DeviceBackend::kEnableDeviceBackend gates
DeviceAllocatorT::Init. Key DeviceAllocatorT off PrimaryAllocator and
rename the plugged static API parameter to DeviceBackend for clarity.
---
 compiler-rt/lib/asan/asan_allocator.cpp       |  8 ++-
 compiler-rt/lib/asan/asan_allocator.h         | 12 +++++
 .../sanitizer_common/sanitizer_allocator.h    |  7 ++-
 .../sanitizer_allocator_amdgpu.cpp            |  2 +
 .../sanitizer_allocator_amdgpu.h              | 12 +++--
 .../sanitizer_allocator_combined.h            | 23 ++++-----
 .../sanitizer_allocator_device.h              | 49 ++++++++++---------
 7 files changed, 67 insertions(+), 46 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 547037c283fd1..b7b37db867058 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -393,11 +393,9 @@ struct Allocator {
 
   void InitLinkerInitialized(const AllocatorOptions& options) {
     SetAllocatorMayReturnNull(options.may_return_null);
-    // Device-backed allocations use CombinedAllocator's device path. On
-    // SANITIZER_AMDHSA builds, enable it so InitMemFuncs() /
-    // AmdgpuDeviceAllocator::Init() run at startup.
-    allocator.InitLinkerInitialized(options.release_to_os_interval_ms, 0,
-                                    SANITIZER_AMDHSA);
+    // Device tier init is driven by DeviceBackend::kEnableDeviceBackend inside
+    // CombinedAllocator::InitLinkerInitialized (no separate ASan field).
+    allocator.InitLinkerInitialized(options.release_to_os_interval_ms);
     SharedInitCode(options);
     max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
                                        ? common_flags()->max_allocation_size_mb
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index 0ce2befc389d7..265536e3b270b 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -18,6 +18,11 @@
 #include "asan_interceptors.h"
 #include "asan_internal.h"
 #include "sanitizer_common/sanitizer_allocator.h"
+#if SANITIZER_AMDHSA
+namespace __sanitizer {
+#  include "sanitizer_common/sanitizer_allocator_amdgpu.h"
+}  // namespace __sanitizer
+#endif
 #include "sanitizer_common/sanitizer_list.h"
 #include "sanitizer_common/sanitizer_platform.h"
 
@@ -269,7 +274,14 @@ static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
 
 template <typename AddressSpaceView>
 using AsanAllocatorASVT =
+#if SANITIZER_AMDHSA
+    CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>,
+                      DefaultLargeMmapAllocatorPtrArray,
+                      __sanitizer::AmdgpuDeviceAllocatorT<
+                          PrimaryAllocatorASVT<AddressSpaceView>>>;
+#else
     CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>>;
+#endif
 using AsanAllocator = AsanAllocatorASVT<LocalAddressSpaceView>;
 using AllocatorCache = AsanAllocator::AllocatorCache;
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index 17f254a59155b..04dfa426be54d 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -69,7 +69,9 @@ struct NoOpMapUnmapCallback {
 
 // clang-format off
 // Include order is load-bearing (MemoryMapper, AllocatorStats, secondary
-// typedefs, then DeviceAllocatorT, then CombinedAllocator). Do not sort.
+// typedefs, then DeviceAllocatorT, then CombinedAllocator). Select device 
+// tier via CombinedAllocator's third template from asan_allocator.h.
+// Do not sort.
 #include "sanitizer_allocator_size_class_map.h"
 #include "sanitizer_allocator_stats.h"
 #include "sanitizer_allocator_primary64.h"
@@ -77,9 +79,6 @@ struct NoOpMapUnmapCallback {
 #include "sanitizer_allocator_local_cache.h"
 #include "sanitizer_allocator_secondary.h"
 #include "sanitizer_allocator_device.h"
-#if SANITIZER_AMDHSA
-#  include "sanitizer_allocator_amdgpu.h"
-#endif
 #include "sanitizer_allocator_combined.h"
 // clang-format on
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 057f8f85b3c5e..d3ccf27c986df 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -16,6 +16,8 @@
 #  include "sanitizer_atomic.h"
 
 namespace __sanitizer {
+#  include "sanitizer_allocator_amdgpu.h"
+
 struct HsaFunctions {
   // -------------- Memory Allocate/Deallocate Functions ----------------
   hsa_status_t (*memory_pool_allocate)(hsa_amd_memory_pool_t memory_pool,
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index dba657175b6cb..9f82753a2393a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#  error This file must be included inside sanitizer_allocator.h
+#  error This file must be included after sanitizer_allocator.h
 #endif
 
 #if SANITIZER_AMDHSA
@@ -20,6 +20,8 @@ static const DeviceAllocationType DAT_AMDGPU =
 
 class AmdgpuDeviceAllocator {
  public:
+  static constexpr bool kEnableDeviceBackend = true;
+
   static bool Init(bool allow_dlopen = false);
   static void* Allocate(uptr size, uptr alignment,
                         DeviceAllocationInfo* da_info);
@@ -99,8 +101,10 @@ struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
   void* ptr;
 };
 
-template <class MapUnmapCallback>
-using DefaultDeviceAllocator =
-    DeviceAllocatorT<MapUnmapCallback, AmdgpuDeviceAllocator>;
+// AMDGPU device heap for CombinedAllocator's third template parameter:
+// DeviceAllocatorT<Primary, AmdgpuDeviceAllocator>.
+template <class PrimaryAllocator>
+using AmdgpuDeviceAllocatorT =
+    DeviceAllocatorT<PrimaryAllocator, AmdgpuDeviceAllocator>;
 
 #endif  // SANITIZER_AMDHSA
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
index fc2a2e5b50c8f..0222a4324a434 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -13,14 +13,18 @@
 #error This file must be included inside sanitizer_allocator.h
 #endif
 
-// This class implements a complete memory allocator by using two
-// internal allocators:
+// This class implements a complete memory allocator by using two internal
+// allocators:
 // PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
 //  When allocating 2^x bytes it should return 2^x aligned chunk.
 // PrimaryAllocator is used via a local AllocatorCache.
 // SecondaryAllocator can allocate anything, but is not efficient.
+// DeviceAllocator as optional device heap tier: implemented as
+// DeviceAllocatorT<PrimaryAllocator, DeviceBackend>. Default:
+// DefaultDeviceAllocator<PrimaryAllocator>.
 template <class PrimaryAllocator,
-          class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray>
+          class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray,
+          class DeviceAllocator = DefaultDeviceAllocator<PrimaryAllocator>>
 class CombinedAllocator {
  public:
   using AllocatorCache = typename PrimaryAllocator::AllocatorCache;
@@ -28,22 +32,19 @@ class CombinedAllocator {
       LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback,
                          LargeMmapAllocatorPtrArray,
                          typename PrimaryAllocator::AddressSpaceView>;
-  using DeviceAllocator =
-      DefaultDeviceAllocator<typename PrimaryAllocator::MapUnmapCallback>;
 
-  void InitLinkerInitialized(s32 release_to_os_interval_ms, uptr heap_start = 0,
-                             bool enable_device_allocator = false) {
+  void InitLinkerInitialized(s32 release_to_os_interval_ms,
+                             uptr heap_start = 0) {
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.InitLinkerInitialized();
-    device_.Init(enable_device_allocator, primary_.kMetadataSize);
+    device_.Init(primary_.kMetadataSize);
   }
 
-  void Init(s32 release_to_os_interval_ms, uptr heap_start = 0,
-            bool enable_device_allocator = false) {
+  void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) {
     stats_.Init();
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.Init();
-    device_.Init(enable_device_allocator, primary_.kMetadataSize);
+    device_.Init(primary_.kMetadataSize);
   }
 
   void* Allocate(AllocatorCache* cache, uptr size, uptr alignment,
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index c78a449cb319e..63ebd13d44ce8 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -13,9 +13,9 @@
 #  error This file must be included inside sanitizer_allocator.h
 #endif
 
-// Vendor-neutral device heap book-keeping. Runtime access (allocate, pointer
-// queries, deallocate etc.) is provided by a DeviceAllocator template
-// parameter.
+// DeviceAllocatorT: device-mapped heap bookkeeping. Map/unmap uses the
+// primary's MapUnmapCallback. Alloc/free/pointer queries use DeviceBackend
+// (NoOpDeviceAllocator, AmdgpuDeviceAllocator, ...).
 
 enum DeviceAllocationType {
   DAT_UNKNOWN = 0,
@@ -39,6 +39,10 @@ struct DevicePointerInfo {
 };
 
 struct NoOpDeviceAllocator {
+  // When kEnableDeviceBackend is false, DeviceAllocatorT::Init skips device
+  // book-keeping (no ptr_array setup or InitMemFuncs at allocator init).
+  static constexpr bool kEnableDeviceBackend = false;
+
   static bool Init() { return false; }
 
   static void* Allocate(uptr size, uptr alignment,
@@ -68,17 +72,17 @@ struct NoOpDeviceAllocator {
   }
 };
 
-template <class MapUnmapCallback = NoOpMapUnmapCallback,
-          class DeviceAllocator = NoOpDeviceAllocator>
+template <class PrimaryAllocator, class DeviceBackend = NoOpDeviceAllocator>
 class DeviceAllocatorT {
  public:
   using PtrArrayT = DefaultLargeMmapAllocatorPtrArray;
+  using MapUnmapCallback = typename PrimaryAllocator::MapUnmapCallback;
 
-  void Init(bool enable, uptr kMetadataSize) {
+  void Init(uptr kMetadataSize) {
     internal_memset(this, 0, sizeof(*this));
-    enabled_ = enable;
-    if (!enable)
+    if (!DeviceBackend::kEnableDeviceBackend)
       return;
+    enabled_ = true;
     kMetadataSize_ = kMetadataSize;
     chunks_ = reinterpret_cast<uptr*>(ptr_array_.Init());
     InitMemFuncs();
@@ -88,7 +92,7 @@ class DeviceAllocatorT {
                  DeviceAllocationInfo* da_info) {
     if (!da_info || !InitMemFuncs()) {
       if (da_info)
-        DeviceAllocator::NoteDeviceAllocatorFailure(
+        DeviceBackend::NoteDeviceAllocatorFailure(
             da_info, DEV_ALLOC_FAILURE_NOT_INITIALIZED);
       return nullptr;
     }
@@ -107,13 +111,13 @@ class DeviceAllocatorT {
           "WARNING: %s: DeviceAllocator allocation overflow: "
           "0x%zx bytes with 0x%zx alignment requested\n",
           SanitizerToolName, map_size, alignment);
-      DeviceAllocator::NoteDeviceAllocatorFailure(
+      DeviceBackend::NoteDeviceAllocatorFailure(
           da_info, DEV_ALLOC_FAILURE_OUT_OF_RESOURCES);
       return nullptr;
     }
-    void* ptr = DeviceAllocator::Allocate(map_size, alignment, da_info);
+    void* ptr = DeviceBackend::Allocate(map_size, alignment, da_info);
     if (!ptr) {
-      DeviceAllocator::NoteDeviceAllocatorFailure(
+      DeviceBackend::NoteDeviceAllocatorFailure(
           da_info, DEV_ALLOC_FAILURE_OUT_OF_RESOURCES);
       return nullptr;
     }
@@ -168,7 +172,7 @@ class DeviceAllocatorT {
       stat->Sub(AllocatorStatMapped, h->map_size);
     }
     MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
-    DeviceAllocator::Deallocate(p);
+    DeviceBackend::Deallocate(p);
   }
 
   uptr TotalMemoryUsed() {
@@ -331,10 +335,10 @@ class DeviceAllocatorT {
     if (!enabled_ || mem_funcs_inited_ || mem_funcs_init_count_ >= 2) {
       return mem_funcs_inited_;
     }
-    mem_funcs_inited_ = DeviceAllocator::Init();
+    mem_funcs_inited_ = DeviceBackend::Init();
     mem_funcs_init_count_++;
     if (mem_funcs_inited_)
-      page_size_ = DeviceAllocator::GetPageSize();
+      page_size_ = DeviceBackend::GetPageSize();
     return mem_funcs_inited_;
   }
 
@@ -342,7 +346,7 @@ class DeviceAllocatorT {
 
   Header* GetHeaderAnyPointer(uptr p, Header* h) const {
     CHECK(IsAligned(p, page_size_));
-    return DeviceAllocator::GetPointerInfo(p, h) ? h : nullptr;
+    return DeviceBackend::GetPointerInfo(p, h) ? h : nullptr;
   }
 
   Header* GetHeader(uptr chunk, Header* h) const {
@@ -350,12 +354,12 @@ class DeviceAllocatorT {
     // is unloaded, GetPointerInfo() will fail. For such case, we can still
     // return a valid value for map_beg, map_size will be limited to one page
     if (LIKELY(!dev_runtime_unloaded_)) {
-      if (DeviceAllocator::GetPointerInfo(chunk, h))
+      if (DeviceBackend::GetPointerInfo(chunk, h))
         return h;
       // If GetPointerInfo() fails, we don't assume the runtime is unloaded yet.
       // We just return a conservative single-page header. Here mark/check the
       // runtime shutdown state
-      dev_runtime_unloaded_ = DeviceAllocator::IsRuntimeShutdown();
+      dev_runtime_unloaded_ = DeviceBackend::IsRuntimeShutdown();
     }
     // If we reach here, device runtime is unloaded.
     // Fallback: conservative single-page header
@@ -390,8 +394,9 @@ class DeviceAllocatorT {
   mutable StaticSpinMutex mutex_;
 };
 
-#if !SANITIZER_AMDHSA
-template <class MapUnmapCallback>
+// Default CombinedAllocator device leg: DeviceAllocatorT<PrimaryAllocator,
+// NoOpDeviceAllocator> (same MapUnmapCallback as primary; no device
+// allocations).
+template <class PrimaryAllocator>
 using DefaultDeviceAllocator =
-    DeviceAllocatorT<MapUnmapCallback, NoOpDeviceAllocator>;
-#endif
+    DeviceAllocatorT<PrimaryAllocator, NoOpDeviceAllocator>;

>From d7e3e8300ee44f32a1c1a6b3fa6e89535400959b Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Thu, 11 Jun 2026 17:18:49 +0530
Subject: [PATCH 34/46] Rename sanitizer_hsa folder to hsa.

---
 .../lib/sanitizer_common/{sanitizer_hsa => hsa}/hsa.h     | 8 ++++----
 .../sanitizer_common/{sanitizer_hsa => hsa}/hsa_ext_amd.h | 8 ++++----
 compiler-rt/lib/sanitizer_common/sanitizer_hsa.h          | 4 ++--
 3 files changed, 10 insertions(+), 10 deletions(-)
 rename compiler-rt/lib/sanitizer_common/{sanitizer_hsa => hsa}/hsa.h (87%)
 rename compiler-rt/lib/sanitizer_common/{sanitizer_hsa => hsa}/hsa_ext_amd.h (96%)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa.h b/compiler-rt/lib/sanitizer_common/hsa/hsa.h
similarity index 87%
rename from compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa.h
rename to compiler-rt/lib/sanitizer_common/hsa/hsa.h
index f052ddb579a78..1f08b1802d3f5 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa.h
+++ b/compiler-rt/lib/sanitizer_common/hsa/hsa.h
@@ -1,4 +1,4 @@
-//===-- sanitizer_hsa/hsa.h ------------------------------------- C++ -*-===//
+//===-- hsa/hsa.h ------------------------------------- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -11,8 +11,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef SANITIZER_HSA_HSA_H_
-#define SANITIZER_HSA_HSA_H_
+#ifndef HSA_H_
+#define HSA_H_
 
 #include <stddef.h>
 #include <stdint.h>
@@ -46,4 +46,4 @@ hsa_status_t hsa_memory_copy(void* dst, const void* src, size_t size);
 }
 #endif
 
-#endif  // SANITIZER_HSA_HSA_H_
+#endif  // HSA_H_
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa_ext_amd.h b/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
similarity index 96%
rename from compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa_ext_amd.h
rename to compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
index 9a335762fcf89..53ab5a2766e68 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_hsa/hsa_ext_amd.h
+++ b/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
@@ -1,4 +1,4 @@
-//===-- sanitizer_hsa/hsa_ext_amd.h ----------------------------- C++ -*-===//
+//===-- hsa/hsa_ext_amd.h ----------------------------- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -10,8 +10,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef SANITIZER_HSA_HSA_EXT_AMD_H_
-#define SANITIZER_HSA_HSA_EXT_AMD_H_
+#ifndef HSA_EXT_AMD_H_
+#define HSA_EXT_AMD_H_
 
 #include "hsa.h"
 
@@ -138,4 +138,4 @@ hsa_status_t hsa_amd_register_system_event_handler(
 }
 #endif
 
-#endif  // SANITIZER_HSA_HSA_EXT_AMD_H_
+#endif  // HSA_EXT_AMD_H_
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_hsa.h b/compiler-rt/lib/sanitizer_common/sanitizer_hsa.h
index 5a9d81182dfc6..38dcad4d0e6d4 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_hsa.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_hsa.h
@@ -13,7 +13,7 @@
 #ifndef SANITIZER_HSA_H_
 #define SANITIZER_HSA_H_
 
-#include "sanitizer_common/sanitizer_hsa/hsa.h"
-#include "sanitizer_common/sanitizer_hsa/hsa_ext_amd.h"
+#include "sanitizer_common/hsa/hsa.h"
+#include "sanitizer_common/hsa/hsa_ext_amd.h"
 
 #endif  // SANITIZER_HSA_H_

>From 7456edd8f402a61f3dc77c0f34fd1199f8d33c9c Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 16 Jun 2026 12:37:56 +0530
Subject: [PATCH 35/46] [compiler-rt][asan][AMDGPU] Decouple device allocator
 tier from CombinedAllocator

The AMDGPU/HSA device heap tier was grafted directly into the shared
CombinedAllocator (extra template parameter, device_ member, and device
dispatch in every method), forcing device-specific code into a class
used by every sanitizer (TSan/MSan/LSan/HWASan/...). Move it out so the
shared base stays host-only.

Allocator changes:
- Revert CombinedAllocator to host-only (primary + secondary): drop the
  device template parameter, the device_ member, and all device
  dispatch.  The generic GetMetaDataFastLocked helper (used by
  hwasan/lsan) is kept.

- Add DeviceCombinedAllocator, a wrapper that *composes* an unmodified
  host allocator with a DeviceAllocatorT and re-exposes the same public
  surface, dispatching to the device tier on da_info (allocate) or
  PointerIsMine (everything else). It is parameterized on the host
  allocator type, so it is independent of CombinedAllocator's
  definition.

- DeviceAllocatorT::Init() now sources kMetadataSize from its
  PrimaryAllocator template argument instead of receiving it as a
  parameter, so the wrapper needs no PrimaryAllocator coupling.

- ASan selects DeviceCombinedAllocator<CombinedAllocator<Primary>,
  AmdgpuDeviceAllocatorT<Primary>> under SANITIZER_AMDHSA, and a small
  AllocateBlock() helper gates the da_info argument so non-AMDHSA builds
  use the unchanged three argument call `CombinedAllocator::Allocate`.

Build / tests:
- Move sanitizer_allocator_amdgpu.cpp from
  SANITIZER_SOURCES_NOTERMINATION to SANITIZER_LIBCDEP_SOURCES: it uses
  dlopen/dlsym and belongs in the libc-dependent archive, not the
  libc-independent core.
- With the core dl-free again, restore the sanitizer_common NolibcMain
  test unconditionally: revert the SANITIZER_AMDHSA skip and the
  COMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST gate.

Also intercept hsa_memory_free (routed to asan_hsa_amd_memory_pool_free)
so device frees through that entry point are tracked.
---
 compiler-rt/lib/asan/asan_allocator.cpp       |  18 +-
 compiler-rt/lib/asan/asan_allocator.h         |   8 +-
 compiler-rt/lib/asan/asan_hsa_linux.cpp       |   8 +
 .../lib/sanitizer_common/CMakeLists.txt       |   3 +-
 .../sanitizer_common/sanitizer_allocator.h    |   7 +-
 .../sanitizer_allocator_combined.h            |  70 ++-----
 .../sanitizer_allocator_combined_device.h     | 195 ++++++++++++++++++
 .../sanitizer_allocator_device.h              |  14 +-
 .../lib/sanitizer_common/tests/CMakeLists.txt |  17 +-
 .../tests/sanitizer_nolibc_test.cpp           |   5 +-
 10 files changed, 258 insertions(+), 87 deletions(-)
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index b7b37db867058..dee06b646e4c6 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -537,6 +537,20 @@ struct Allocator {
     return true;
   }
 
+  // Route the raw block allocation to the underlying allocator. The device
+  // heap tier (da_info) only exists for SANITIZER_AMDHSA, where `allocator` is
+  // a DeviceCombinedAllocator; other builds use a plain CombinedAllocator whose
+  // Allocate() has no device parameter.
+  void* AllocateBlock(AllocatorCache* cache, uptr needed_size,
+                      DeviceAllocationInfo* da_info) {
+#if SANITIZER_AMDHSA
+    return allocator.Allocate(cache, needed_size, 8, da_info);
+#else
+    (void)da_info;
+    return allocator.Allocate(cache, needed_size, 8);
+#endif
+  }
+
   // -------------------- Allocation/Deallocation routines ---------------
   // may_return_null tells AllocateImpl() whether OOM should produce a nullptr
   // (true) or a fatal Report*+Die() (false).
@@ -597,11 +611,11 @@ struct Allocator {
     void* allocated;
     if (t) {
       AllocatorCache* cache = GetAllocatorCache(&t->malloc_storage());
-      allocated = allocator.Allocate(cache, needed_size, 8, da_info);
+      allocated = AllocateBlock(cache, needed_size, da_info);
     } else {
       SpinMutexLock l(&fallback_mutex);
       AllocatorCache* cache = &fallback_allocator_cache;
-      allocated = allocator.Allocate(cache, needed_size, 8, da_info);
+      allocated = AllocateBlock(cache, needed_size, da_info);
     }
     if (UNLIKELY(!allocated)) {
       SetAllocatorOutOfMemory();
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index 265536e3b270b..3cb6b3b9a11f0 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -275,10 +275,10 @@ static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
 template <typename AddressSpaceView>
 using AsanAllocatorASVT =
 #if SANITIZER_AMDHSA
-    CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>,
-                      DefaultLargeMmapAllocatorPtrArray,
-                      __sanitizer::AmdgpuDeviceAllocatorT<
-                          PrimaryAllocatorASVT<AddressSpaceView>>>;
+    DeviceCombinedAllocator<
+        CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>>,
+        __sanitizer::AmdgpuDeviceAllocatorT<
+            PrimaryAllocatorASVT<AddressSpaceView>>>;
 #else
     CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>>;
 #endif
diff --git a/compiler-rt/lib/asan/asan_hsa_linux.cpp b/compiler-rt/lib/asan/asan_hsa_linux.cpp
index a6ea062a23595..1c60fde946990 100644
--- a/compiler-rt/lib/asan/asan_hsa_linux.cpp
+++ b/compiler-rt/lib/asan/asan_hsa_linux.cpp
@@ -35,6 +35,7 @@ DEFINE_REAL(hsa_status_t, hsa_amd_memory_pool_allocate,
             hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags,
             void** ptr)
 DEFINE_REAL(hsa_status_t, hsa_amd_memory_pool_free, void* ptr)
+DEFINE_REAL(hsa_status_t, hsa_memory_free, void* ptr)
 DEFINE_REAL(hsa_status_t, hsa_amd_memory_async_copy, void*, hsa_agent_t,
             const void*, hsa_agent_t, size_t, uint32_t, const hsa_signal_t*,
             hsa_signal_t)
@@ -83,6 +84,13 @@ INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_free, void* ptr) {
   return asan_hsa_amd_memory_pool_free(ptr, &stack);
 }
 
+INTERCEPTOR(hsa_status_t, hsa_memory_free, void* ptr) {
+  AsanInitFromRtl();
+  ENSURE_HSA_INITED();
+  GET_STACK_TRACE_FREE;
+  return asan_hsa_amd_memory_pool_free(ptr, &stack);
+}
+
 INTERCEPTOR(hsa_status_t, hsa_amd_agents_allow_access, uint32_t num_agents,
             const hsa_agent_t* agents, const uint32_t* flags, const void* ptr) {
   AsanInitFromRtl();
diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
index 5e13b5b18cc56..8db07e2cde7d7 100644
--- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
@@ -3,7 +3,6 @@
 
 set(SANITIZER_SOURCES_NOTERMINATION
   sanitizer_allocator.cpp
-  sanitizer_allocator_amdgpu.cpp
   sanitizer_common.cpp
   sanitizer_deadlock_detector1.cpp
   sanitizer_deadlock_detector2.cpp
@@ -62,6 +61,7 @@ set(SANITIZER_NOLIBC_SOURCES
 
 set(SANITIZER_LIBCDEP_SOURCES
   sanitizer_common_libcdep.cpp
+  sanitizer_allocator_amdgpu.cpp
   sanitizer_allocator_checks.cpp
   sanitizer_dl.cpp
   sanitizer_linux_libcdep.cpp
@@ -114,6 +114,7 @@ set(SANITIZER_IMPL_HEADERS
   sanitizer_allocator.h
   sanitizer_allocator_checks.h
   sanitizer_allocator_combined.h
+  sanitizer_allocator_combined_device.h
   sanitizer_allocator_dlsym.h
   sanitizer_allocator_interface.h
   sanitizer_allocator_internal.h
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index 04dfa426be54d..3d7e1876a3c69 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -69,8 +69,10 @@ struct NoOpMapUnmapCallback {
 
 // clang-format off
 // Include order is load-bearing (MemoryMapper, AllocatorStats, secondary
-// typedefs, then DeviceAllocatorT, then CombinedAllocator). Select device 
-// tier via CombinedAllocator's third template from asan_allocator.h.
+// typedefs, then DeviceAllocatorT, then CombinedAllocator). The base
+// CombinedAllocator is host-only (primary + secondary); an optional device heap
+// tier is layered on top via DeviceCombinedAllocator (opt-in from
+// asan_allocator.h), so the shared base stays untouched.
 // Do not sort.
 #include "sanitizer_allocator_size_class_map.h"
 #include "sanitizer_allocator_stats.h"
@@ -80,6 +82,7 @@ struct NoOpMapUnmapCallback {
 #include "sanitizer_allocator_secondary.h"
 #include "sanitizer_allocator_device.h"
 #include "sanitizer_allocator_combined.h"
+#include "sanitizer_allocator_combined_device.h"
 // clang-format on
 
 bool IsRssLimitExceeded();
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
index 0222a4324a434..49d1f252b8e9c 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -13,18 +13,14 @@
 #error This file must be included inside sanitizer_allocator.h
 #endif
 
-// This class implements a complete memory allocator by using two internal
-// allocators:
+// This class implements a complete memory allocator by using two
+// internal allocators:
 // PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
 //  When allocating 2^x bytes it should return 2^x aligned chunk.
 // PrimaryAllocator is used via a local AllocatorCache.
 // SecondaryAllocator can allocate anything, but is not efficient.
-// DeviceAllocator as optional device heap tier: implemented as
-// DeviceAllocatorT<PrimaryAllocator, DeviceBackend>. Default:
-// DefaultDeviceAllocator<PrimaryAllocator>.
 template <class PrimaryAllocator,
-          class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray,
-          class DeviceAllocator = DefaultDeviceAllocator<PrimaryAllocator>>
+          class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray>
 class CombinedAllocator {
  public:
   using AllocatorCache = typename PrimaryAllocator::AllocatorCache;
@@ -37,18 +33,15 @@ class CombinedAllocator {
                              uptr heap_start = 0) {
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.InitLinkerInitialized();
-    device_.Init(primary_.kMetadataSize);
   }
 
   void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) {
     stats_.Init();
     primary_.Init(release_to_os_interval_ms, heap_start);
     secondary_.Init();
-    device_.Init(primary_.kMetadataSize);
   }
 
-  void* Allocate(AllocatorCache* cache, uptr size, uptr alignment,
-                 DeviceAllocationInfo* da_info = nullptr) {
+  void* Allocate(AllocatorCache* cache, uptr size, uptr alignment) {
     // Returning 0 on malloc(0) may break a lot of code.
     if (size == 0)
       size = 1;
@@ -72,9 +65,7 @@ class CombinedAllocator {
     // alignment without such requirement, and allocating 'size' would use
     // extraneous memory, so we employ 'original_size'.
     void* res;
-    if (da_info)
-      res = device_.Allocate(&stats_, original_size, alignment, da_info);
-    else if (primary_.CanAllocate(size, alignment))
+    if (primary_.CanAllocate(size, alignment))
       res = cache->Allocate(&primary_, primary_.ClassID(size));
     else
       res = secondary_.Allocate(&stats_, original_size, alignment);
@@ -99,10 +90,8 @@ class CombinedAllocator {
     if (!p) return;
     if (primary_.PointerIsMine(p))
       cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
-    else if (secondary_.PointerIsMine(p))
+    else
       secondary_.Deallocate(&stats_, p);
-    else if (device_.PointerIsMine(p))
-      device_.Deallocate(&stats_, p);
   }
 
   void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
@@ -126,11 +115,7 @@ class CombinedAllocator {
   bool PointerIsMine(const void *p) const {
     if (primary_.PointerIsMine(p))
       return true;
-    if (secondary_.PointerIsMine(p))
-      return true;
-    if (device_.PointerIsMine(p))
-      return true;
-    return false;
+    return secondary_.PointerIsMine(p);
   }
 
   bool FromPrimary(const void *p) const { return primary_.PointerIsMine(p); }
@@ -138,62 +123,42 @@ class CombinedAllocator {
   void *GetMetaData(const void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetMetaData(p);
-    if (secondary_.PointerIsMine(p))
-      return secondary_.GetMetaData(p);
-    if (device_.PointerIsMine(p))
-      return device_.GetMetaData(p);
-    return nullptr;
+    return secondary_.GetMetaData(p);
   }
 
   // Same as GetMetaData, but must be called with the allocator locked
-  // (via ForceLock). Uses GetBlockBeginFastLocked for secondary/device checks
-  // to avoid re-acquiring the mutex already held by the caller.
+  // (via ForceLock). Uses GetBlockBeginFastLocked for the secondary check to
+  // avoid re-acquiring the mutex already held by the caller.
   void* GetMetaDataFastLocked(const void* p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetMetaData(p);
     if (secondary_.GetBlockBeginFastLocked(p))
       return secondary_.GetMetaData(p);
-    if (device_.GetBlockBeginFastLocked(p))
-      return device_.GetMetaData(p);
     return nullptr;
   }
 
   void *GetBlockBegin(const void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);
-    if (secondary_.PointerIsMine(p))
-      return secondary_.GetBlockBegin(p);
-    if (device_.PointerIsMine(p))
-      return device_.GetBlockBegin(p);
-    return nullptr;
+    return secondary_.GetBlockBegin(p);
   }
 
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
-  void *GetBlockBeginFastLocked(const void *p) {
-    void* beg;
+  void* GetBlockBeginFastLocked(const void* p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);
-    if ((beg = secondary_.GetBlockBeginFastLocked(p)))
-      return beg;
-    if ((beg = device_.GetBlockBeginFastLocked(p)))
-      return beg;
-    return nullptr;
+    return secondary_.GetBlockBeginFastLocked(p);
   }
 
   uptr GetActuallyAllocatedSize(void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetActuallyAllocatedSize(p);
-    if (secondary_.PointerIsMine(p))
-      return secondary_.GetActuallyAllocatedSize(p);
-    if (device_.PointerIsMine(p))
-      return device_.GetActuallyAllocatedSize(p);
-    return 0;
+    return secondary_.GetActuallyAllocatedSize(p);
   }
 
   uptr TotalMemoryUsed() {
-    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed() +
-           device_.TotalMemoryUsed();
+    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
   }
 
   void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
@@ -217,13 +182,11 @@ class CombinedAllocator {
   void PrintStats() {
     primary_.PrintStats();
     secondary_.PrintStats();
-    device_.PrintStats();
   }
 
   // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
   // introspection API.
   void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
-    device_.ForceLock();
     primary_.ForceLock();
     secondary_.ForceLock();
   }
@@ -231,7 +194,6 @@ class CombinedAllocator {
   void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
     secondary_.ForceUnlock();
     primary_.ForceUnlock();
-    device_.ForceUnlock();
   }
 
   // Iterate over all existing chunks.
@@ -239,12 +201,10 @@ class CombinedAllocator {
   void ForEachChunk(ForEachChunkCallback callback, void *arg) {
     primary_.ForEachChunk(callback, arg);
     secondary_.ForEachChunk(callback, arg);
-    device_.ForEachChunk(callback, arg);
   }
 
  private:
   PrimaryAllocator primary_;
   SecondaryAllocator secondary_;
-  DeviceAllocator device_;
   AllocatorGlobalStats stats_;
 };
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
new file mode 100644
index 0000000000000..b1f1035381f78
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
@@ -0,0 +1,195 @@
+//===-- sanitizer_allocator_combined_device.h -------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Part of the Sanitizer Allocator.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_ALLOCATOR_H
+#  error This file must be included inside sanitizer_allocator.h
+#endif
+
+// DeviceCombinedAllocator adds an optional device-memory heap tier on top of an
+// existing host allocator, without touching the base class shared by every
+// sanitizer. It is parameterized purely on the host allocator type (typically a
+// CombinedAllocator of primary + secondary, but any allocator exposing the same
+// interface works) and a DeviceAllocatorT (device backend, e.g. AMDGPU/HSA). It
+// re-exposes the host allocator's public surface, dispatching to the device
+// tier when a DeviceAllocationInfo is supplied (allocate) or when the pointer
+// belongs to the device heap (everything else), and delegating to the host
+// allocator otherwise. The host and device address ranges are disjoint, so "try
+// host, then device" preserves the host allocator's dispatch semantics.
+//
+// Note: this composes (does not inherit from / modify) the host allocator, so
+// it is independent of CombinedAllocator's definition. It still contains a host
+// allocator because the front end (e.g. ASan) services host and device
+// allocations through a single object that shares chunk/redzone metadata.
+template <class HostAllocator, class DeviceAllocatorT>
+class DeviceCombinedAllocator {
+ public:
+  using AllocatorCache = typename HostAllocator::AllocatorCache;
+  using SecondaryAllocator = typename HostAllocator::SecondaryAllocator;
+
+  void InitLinkerInitialized(s32 release_to_os_interval_ms,
+                             uptr heap_start = 0) {
+    device_stats_.Init();
+    base_.InitLinkerInitialized(release_to_os_interval_ms, heap_start);
+    device_.Init();
+  }
+
+  void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) {
+    device_stats_.Init();
+    base_.Init(release_to_os_interval_ms, heap_start);
+    device_.Init();
+  }
+
+  void* Allocate(AllocatorCache* cache, uptr size, uptr alignment,
+                 DeviceAllocationInfo* da_info = nullptr) {
+    if (!da_info)
+      return base_.Allocate(cache, size, alignment);
+    // Device tier: mirror CombinedAllocator's malloc(0)/overflow guards. The
+    // device allocator handles its own alignment/page rounding, so 'size' is
+    // forwarded unrounded (CombinedAllocator's original_size semantics).
+    if (size == 0)
+      size = 1;
+    if (size + alignment < size) {
+      Report(
+          "WARNING: %s: DeviceCombinedAllocator allocation overflow: "
+          "0x%zx bytes with 0x%zx alignment requested\n",
+          SanitizerToolName, size, alignment);
+      return nullptr;
+    }
+    void* res = device_.Allocate(&device_stats_, size, alignment, da_info);
+    if (alignment > 8 && res)
+      CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
+    return res;
+  }
+
+  s32 ReleaseToOSIntervalMs() const { return base_.ReleaseToOSIntervalMs(); }
+
+  void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
+    base_.SetReleaseToOSIntervalMs(release_to_os_interval_ms);
+  }
+
+  void ForceReleaseToOS() { base_.ForceReleaseToOS(); }
+
+  void Deallocate(AllocatorCache* cache, void* p) {
+    if (!p)
+      return;
+    if (device_.PointerIsMine(p))
+      device_.Deallocate(&device_stats_, p);
+    else
+      base_.Deallocate(cache, p);
+  }
+
+  void* Reallocate(AllocatorCache* cache, void* p, uptr new_size,
+                   uptr alignment) {
+    if (!p)
+      return Allocate(cache, new_size, alignment);
+    if (!new_size) {
+      Deallocate(cache, p);
+      return nullptr;
+    }
+    CHECK(PointerIsMine(p));
+    uptr old_size = GetActuallyAllocatedSize(p);
+    uptr memcpy_size = Min(new_size, old_size);
+    void* new_p = Allocate(cache, new_size, alignment);
+    if (new_p)
+      internal_memcpy(new_p, p, memcpy_size);
+    Deallocate(cache, p);
+    return new_p;
+  }
+
+  bool PointerIsMine(const void* p) const {
+    return base_.PointerIsMine(p) || device_.PointerIsMine(p);
+  }
+
+  bool FromPrimary(const void* p) const { return base_.FromPrimary(p); }
+
+  void* GetMetaData(const void* p) {
+    if (device_.PointerIsMine(p))
+      return device_.GetMetaData(p);
+    return base_.GetMetaData(p);
+  }
+
+  // Same as GetMetaData, but must be called with the allocator locked
+  // (via ForceLock). Uses GetBlockBeginFastLocked for the device check to avoid
+  // re-acquiring a mutex already held by the caller.
+  void* GetMetaDataFastLocked(const void* p) {
+    if (device_.GetBlockBeginFastLocked(p))
+      return device_.GetMetaData(p);
+    return base_.GetMetaDataFastLocked(p);
+  }
+
+  void* GetBlockBegin(const void* p) {
+    if (void* beg = base_.GetBlockBegin(p))
+      return beg;
+    return device_.GetBlockBegin(p);
+  }
+
+  // This function does the same as GetBlockBegin, but is much faster.
+  // Must be called with the allocator locked.
+  void* GetBlockBeginFastLocked(const void* p) {
+    if (void* beg = base_.GetBlockBeginFastLocked(p))
+      return beg;
+    return device_.GetBlockBeginFastLocked(p);
+  }
+
+  uptr GetActuallyAllocatedSize(void* p) {
+    if (device_.PointerIsMine(p))
+      return device_.GetActuallyAllocatedSize(p);
+    return base_.GetActuallyAllocatedSize(p);
+  }
+
+  uptr TotalMemoryUsed() {
+    return base_.TotalMemoryUsed() + device_.TotalMemoryUsed();
+  }
+
+  void TestOnlyUnmap() { base_.TestOnlyUnmap(); }
+
+  void InitCache(AllocatorCache* cache) { base_.InitCache(cache); }
+
+  void DestroyCache(AllocatorCache* cache) { base_.DestroyCache(cache); }
+
+  void SwallowCache(AllocatorCache* cache) { base_.SwallowCache(cache); }
+
+  void GetStats(AllocatorStatCounters s) const {
+    base_.GetStats(s);
+    AllocatorStatCounters device_counters;
+    device_stats_.Get(device_counters);
+    for (int i = 0; i < AllocatorStatCount; i++) s[i] += device_counters[i];
+  }
+
+  void PrintStats() {
+    base_.PrintStats();
+    device_.PrintStats();
+  }
+
+  // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
+  // introspection API.
+  void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+    device_.ForceLock();
+    base_.ForceLock();
+  }
+
+  void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+    base_.ForceUnlock();
+    device_.ForceUnlock();
+  }
+
+  // Iterate over all existing chunks.
+  // The allocator must be locked when calling this function.
+  void ForEachChunk(ForEachChunkCallback callback, void* arg) {
+    base_.ForEachChunk(callback, arg);
+    device_.ForEachChunk(callback, arg);
+  }
+
+ private:
+  HostAllocator base_;
+  DeviceAllocatorT device_;
+  AllocatorGlobalStats device_stats_;
+};
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index 63ebd13d44ce8..cfcaa64c81564 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -78,12 +78,15 @@ class DeviceAllocatorT {
   using PtrArrayT = DefaultLargeMmapAllocatorPtrArray;
   using MapUnmapCallback = typename PrimaryAllocator::MapUnmapCallback;
 
-  void Init(uptr kMetadataSize) {
+  // Metadata layout mirrors the primary allocator so device-tier chunks carry
+  // the same per-chunk metadata size. Sourced from the primary template arg so
+  // callers (and any wrapper) need not thread it in.
+  void Init() {
     internal_memset(this, 0, sizeof(*this));
     if (!DeviceBackend::kEnableDeviceBackend)
       return;
     enabled_ = true;
-    kMetadataSize_ = kMetadataSize;
+    kMetadataSize_ = PrimaryAllocator::kMetadataSize;
     chunks_ = reinterpret_cast<uptr*>(ptr_array_.Init());
     InitMemFuncs();
   }
@@ -394,9 +397,10 @@ class DeviceAllocatorT {
   mutable StaticSpinMutex mutex_;
 };
 
-// Default CombinedAllocator device leg: DeviceAllocatorT<PrimaryAllocator,
-// NoOpDeviceAllocator> (same MapUnmapCallback as primary; no device
-// allocations).
+// Inert device tier: DeviceAllocatorT<PrimaryAllocator, NoOpDeviceAllocator>
+// (same MapUnmapCallback as primary; no device allocations). Usable as the
+// device backend for DeviceCombinedAllocator when no real device heap is
+// wired up.
 template <class PrimaryAllocator>
 using DefaultDeviceAllocator =
     DeviceAllocatorT<PrimaryAllocator, NoOpDeviceAllocator>;
diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
index ee1a08b878e77..55c7d665e639f 100644
--- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
@@ -80,14 +80,6 @@ set(SANITIZER_TEST_CFLAGS_COMMON
   -Wno-gnu-zero-variadic-macro-arguments
   )
 
-# Host HSA support (SANITIZER_AMDHSA) does not affect unittest compile flags.
-# When SANITIZER_AMDHSA is enabled, skip the NolibcMain subprocess test instead
-# (see NOT SANITIZER_AMDHSA targets below and sanitizer_nolibc_test.cpp).
-if(SANITIZER_AMDHSA)
-  list(APPEND SANITIZER_TEST_CFLAGS_COMMON
-       -DCOMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST=1)
-endif()
-
 set(SANITIZER_TEST_LINK_FLAGS_COMMON
   ${COMPILER_RT_UNITTEST_LINK_FLAGS}
   ${COMPILER_RT_UNWINDER_LINK_LIBS}
@@ -191,13 +183,10 @@ macro(add_sanitizer_tests_for_arch arch)
     CFLAGS  ${SANITIZER_TEST_CFLAGS_COMMON} ${extra_flags}
     LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS_COMMON} ${TARGET_LINK_FLAGS} ${extra_flags})
 
-  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64"
-     AND NOT SANITIZER_AMDHSA)
+  if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64")
     # Test that the libc-independent part of sanitizer_common is indeed
     # independent of libc, by linking this binary without libc (here) and
-    # executing it (unit test in sanitizer_nolibc_test.cpp). Omitted when
-    # SANITIZER_AMDHSA: AMDGPU runtime code needs libdl / libcdep and cannot
-    # link with -nostdlib.
+    # executing it (unit test in sanitizer_nolibc_test.cpp).
     get_target_flags_for_arch(${arch} TARGET_FLAGS)
     clang_compile(sanitizer_nolibc_test_main.${arch}.o
                   sanitizer_nolibc_test_main.cpp
@@ -223,7 +212,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
                              $<TARGET_OBJECTS:RTSanitizerCommonLibc.osx>
                              $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.osx>)
   else()
-    if(CAN_TARGET_x86_64 AND NOT SANITIZER_AMDHSA)
+    if(CAN_TARGET_x86_64)
       add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64"
                                $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
                                $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.x86_64>)
diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
index 9f44263dc65ad..41376ee094307 100644
--- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
+++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cpp
@@ -19,10 +19,7 @@
 
 extern const char *argv0;
 
-// When SANITIZER_AMDHSA is enabled, CMake defines this macro and does not build
-// Sanitizer-*-Test-Nolibc (see tests/CMakeLists.txt).
-#if SANITIZER_LINUX && defined(__x86_64__) && \
-    !defined(COMPILER_RT_SKIP_NOLIBC_SUBPROCESS_TEST)
+#if SANITIZER_LINUX && defined(__x86_64__)
 TEST(SanitizerCommon, NolibcMain) {
   std::string NolibcTestPath = argv0;
   NolibcTestPath += "-Nolibc";

>From f4419795bf1677bc7150b959621a6466e75c4f2e Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 16 Jun 2026 19:18:52 +0530
Subject: [PATCH 36/46] Update `sanitizer_internal_defs.h`

Remove unnecessary changes in sanitizer_internal_defs.h not related to
this PR objective.
---
 compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
index b0a61686aa8d4..c694897b6556b 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -40,7 +40,7 @@
 #  if SANITIZER_GO
 #    define SANITIZER_INTERFACE_ATTRIBUTE
 #    define SANITIZER_WEAK_ATTRIBUTE
-#  elif (SANITIZER_AMDGPU && defined(__AMDGPU__)) || SANITIZER_NVPTX
+#  elif SANITIZER_AMDGPU || SANITIZER_NVPTX
 #    define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("hidden")))
 #    define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
 #  else

>From bbde1388210cd1038e9b87f7fd5b9217fc2e5f9a Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 23 Jun 2026 10:37:27 +0530
Subject: [PATCH 37/46] [NFC] Use `Google` Style formatting explicitly.

---
 compiler-rt/lib/asan/asan_allocator.cpp       | 12 +--
 compiler-rt/lib/asan/asan_allocator.h         |  2 +-
 compiler-rt/lib/asan/asan_descriptions.cpp    |  3 +-
 compiler-rt/lib/asan/asan_hsa_allocator.cpp   | 32 ++++----
 compiler-rt/lib/asan/asan_hsa_allocator.h     |  4 +-
 compiler-rt/lib/asan/asan_hsa_linux.cpp       | 29 ++++---
 compiler-rt/lib/asan/asan_interceptors.cpp    |  4 +-
 .../lib/sanitizer_common/hsa/hsa_ext_amd.h    |  2 +-
 .../sanitizer_common/sanitizer_allocator.h    |  2 +-
 .../sanitizer_allocator_amdgpu.cpp            | 53 +++++--------
 .../sanitizer_allocator_amdgpu.h              |  5 +-
 .../sanitizer_allocator_combined.h            | 12 ++-
 .../sanitizer_allocator_combined_device.h     | 32 +++-----
 .../sanitizer_allocator_device.h              | 50 +++++-------
 .../lib/sanitizer_common/sanitizer_platform.h |  2 +-
 .../hsa_amd_ipc_memory_attach_heap_oob.cpp    | 30 ++++----
 .../AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp   | 29 ++++---
 .../hsa_amd_memory_async_copy_overlap.cpp     | 24 +++---
 ...a_amd_memory_pool_allocate_double_free.cpp | 20 +++--
 .../hsa_amd_pointer_info_memory_pool.cpp      | 19 ++---
 ...sa_amd_pointer_info_vmem_reserve_align.cpp | 21 +++--
 .../TestCases/AMDGPU/hsa_amd_test_helpers.h   | 76 ++++++++-----------
 ...vmem_address_reserve_align_double_free.cpp | 14 ++--
 .../AMDGPU/hsa_memory_copy_overlap.cpp        |  9 +--
 24 files changed, 204 insertions(+), 282 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index dee06b646e4c6..45ceb962ec89e 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1521,12 +1521,10 @@ bool AsanHsaTranslateIpcCreate(void* ptr, size_t len, void** out_ptr,
   AsanChunk* m = ptr_
                      ? instance.GetAsanChunkByAddr(reinterpret_cast<uptr>(ptr_))
                      : nullptr;
-  if (!ptr_ || !m)
-    return false;
+  if (!ptr_ || !m) return false;
   uptr p = reinterpret_cast<uptr>(ptr);
   uptr p_ = reinterpret_cast<uptr>(ptr_);
-  if (p != p_ + kHsaPageSize || len != m->UsedSize())
-    return false;
+  if (p != p_ + kHsaPageSize || len != m->UsedSize()) return false;
   *out_ptr = ptr_;
   *out_len = get_allocator().GetActuallyAllocatedSize(ptr_);
   return true;
@@ -1534,8 +1532,7 @@ bool AsanHsaTranslateIpcCreate(void* ptr, size_t len, void** out_ptr,
 
 bool AsanHsaIsVmemFreeValid(void* ptr, uptr size) {
   void* p = get_allocator().GetBlockBegin(ptr);
-  if (!p)
-    return false;
+  if (!p) return false;
   uptr ptr_ = reinterpret_cast<uptr>(ptr);
   AsanChunk* m = reinterpret_cast<AsanChunk*>(ptr_ - kChunkHeaderSize);
   return m->Beg() == ptr_ && size == m->UsedSize();
@@ -1544,8 +1541,7 @@ bool AsanHsaIsVmemFreeValid(void* ptr, uptr size) {
 bool AsanHsaGetLiveMappingInfo(const void* ptr, void** map_base,
                                uptr* used_size, uptr* offset) {
   void* hsa_map_base = get_allocator().GetBlockBegin(ptr);
-  if (!hsa_map_base)
-    return false;
+  if (!hsa_map_base) return false;
   uptr user = reinterpret_cast<uptr>(ptr);
   AsanChunk* m = reinterpret_cast<AsanChunk*>(user - kChunkHeaderSize);
   if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED ||
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index 3cb6b3b9a11f0..61d1b9e23becb 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -20,7 +20,7 @@
 #include "sanitizer_common/sanitizer_allocator.h"
 #if SANITIZER_AMDHSA
 namespace __sanitizer {
-#  include "sanitizer_common/sanitizer_allocator_amdgpu.h"
+#include "sanitizer_common/sanitizer_allocator_amdgpu.h"
 }  // namespace __sanitizer
 #endif
 #include "sanitizer_common/sanitizer_list.h"
diff --git a/compiler-rt/lib/asan/asan_descriptions.cpp b/compiler-rt/lib/asan/asan_descriptions.cpp
index e6e815591f4f6..71289f202d16d 100644
--- a/compiler-rt/lib/asan/asan_descriptions.cpp
+++ b/compiler-rt/lib/asan/asan_descriptions.cpp
@@ -418,8 +418,7 @@ void StackAddressDescription::Print() const {
 }
 
 void HeapAddressDescription::Print() const {
-  if (alloc_tid == kInvalidTid)
-    return;
+  if (alloc_tid == kInvalidTid) return;
 
   PrintHeapChunkAccess(addr, chunk_access);
 
diff --git a/compiler-rt/lib/asan/asan_hsa_allocator.cpp b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
index 149321b1a60d4..de5f8b9f82bc5 100644
--- a/compiler-rt/lib/asan/asan_hsa_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
@@ -14,16 +14,16 @@
 
 #if SANITIZER_AMDHSA
 
-#  include "asan_allocator.h"
-#  include "asan_hsa_allocator.h"
-#  include "asan_interceptors.h"
-#  include "asan_internal.h"
-#  include "asan_mapping.h"
-#  include "asan_poisoning.h"
-#  include "asan_report.h"
-#  include "sanitizer_common/sanitizer_allocator_checks.h"
-#  include "sanitizer_common/sanitizer_errno.h"
-#  include "sanitizer_common/sanitizer_hsa.h"
+#include "asan_allocator.h"
+#include "asan_hsa_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_hsa.h"
 
 namespace __asan {
 
@@ -51,13 +51,13 @@ DECLARE_REAL(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
 // Always align to page boundary to match current ROCr behavior.
 static const size_t kPageSize_ = 4096;
 
-#  if SANITIZER_CAN_USE_ALLOCATOR64
+#if SANITIZER_CAN_USE_ALLOCATOR64
 static_assert(AP64<LocalAddressSpaceView>::kMetadataSize == 0,
               "HSA IPC/VMem wrappers require zero allocator metadata");
-#  else
+#else
 static_assert(AP32<LocalAddressSpaceView>::kMetadataSize == 0,
               "HSA IPC/VMem wrappers require zero allocator metadata");
-#  endif
+#endif
 
 hsa_status_t asan_hsa_amd_memory_pool_allocate(
     hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
@@ -122,8 +122,7 @@ hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
       PoisonShadow(tail_beg, mapped_end - tail_beg, kAsanHeapLeftRedzoneMagic);
 
     uptr size_rounded_down = RoundDownTo(len, ASAN_SHADOW_GRANULARITY);
-    if (size_rounded_down)
-      PoisonShadow(user_beg, size_rounded_down, 0);
+    if (size_rounded_down) PoisonShadow(user_beg, size_rounded_down, 0);
 
     if (len != size_rounded_down && CanPoisonMemory()) {
       u8* shadow = (u8*)MemToShadow(user_beg + size_rounded_down);
@@ -173,8 +172,7 @@ hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
     return REAL(hsa_amd_vmem_address_reserve_align)(ptr, size, address,
                                                     alignment, flags);
 
-  if (alignment < kPageSize_)
-    alignment = kPageSize_;
+  if (alignment < kPageSize_) alignment = kPageSize_;
 
   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
     errno = errno_EINVAL;
diff --git a/compiler-rt/lib/asan/asan_hsa_allocator.h b/compiler-rt/lib/asan/asan_hsa_allocator.h
index 7d5b7f4c174f2..0a7f83dfeef64 100644
--- a/compiler-rt/lib/asan/asan_hsa_allocator.h
+++ b/compiler-rt/lib/asan/asan_hsa_allocator.h
@@ -17,8 +17,8 @@
 
 #if SANITIZER_AMDHSA
 
-#  include "asan_stack.h"
-#  include "sanitizer_common/sanitizer_hsa.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_hsa.h"
 
 namespace __asan {
 
diff --git a/compiler-rt/lib/asan/asan_hsa_linux.cpp b/compiler-rt/lib/asan/asan_hsa_linux.cpp
index 1c60fde946990..872aac96c6ab9 100644
--- a/compiler-rt/lib/asan/asan_hsa_linux.cpp
+++ b/compiler-rt/lib/asan/asan_hsa_linux.cpp
@@ -14,13 +14,13 @@
 
 #if SANITIZER_AMDHSA
 
-#  include "asan_hsa_linux.h"
-#  include "asan_interceptors.h"
-#  include "asan_interceptors_memintrinsics.h"
-#  include "asan_internal.h"
-#  include "asan_report.h"
-#  include "asan_stack.h"
-#  include "asan_suppressions.h"
+#include "asan_hsa_linux.h"
+#include "asan_interceptors.h"
+#include "asan_interceptors_memintrinsics.h"
+#include "asan_internal.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_suppressions.h"
 
 using namespace __asan;
 
@@ -39,11 +39,11 @@ DEFINE_REAL(hsa_status_t, hsa_memory_free, void* ptr)
 DEFINE_REAL(hsa_status_t, hsa_amd_memory_async_copy, void*, hsa_agent_t,
             const void*, hsa_agent_t, size_t, uint32_t, const hsa_signal_t*,
             hsa_signal_t)
-#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+#if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
 DEFINE_REAL(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void*,
             hsa_agent_t, const void*, hsa_agent_t, size_t, uint32_t,
             const hsa_signal_t*, hsa_signal_t, hsa_amd_sdma_engine_id_t, bool)
-#  endif
+#endif
 DEFINE_REAL(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
             hsa_amd_ipc_memory_t* handle)
 DEFINE_REAL(hsa_status_t, hsa_amd_ipc_memory_attach,
@@ -63,8 +63,7 @@ static void ENSURE_HSA_INITED() {
   // The HSA interceptors are initialized lazily: if the program calls into HSA
   // before ASan's global interceptor init runs, we still need the REAL() slots
   // to be populated so we can call through to ROCr.
-  if (!REAL(hsa_init))
-    InitializeAmdgpuInterceptors();
+  if (!REAL(hsa_init)) InitializeAmdgpuInterceptors();
 }
 
 INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_allocate,
@@ -132,7 +131,7 @@ INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy, void* dst,
                                          completion_signal);
 }
 
-#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+#if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
 INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void* dst,
             hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
             size_t size, uint32_t num_dep_signals,
@@ -152,7 +151,7 @@ INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void* dst,
       dst, dst_agent, src, src_agent, size, num_dep_signals, dep_signals,
       completion_signal, engine_id, force_copy_on_sdma);
 }
-#  endif
+#endif
 
 INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
             hsa_amd_ipc_memory_t* handle) {
@@ -214,9 +213,9 @@ void InitializeAmdgpuInterceptors() {
   ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_free);
   ASAN_INTERCEPT_FUNC(hsa_amd_agents_allow_access);
   ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy);
-#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+#if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
   ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy_on_engine);
-#  endif
+#endif
   ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_create);
   ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_attach);
   ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_detach);
diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp
index 469a5fea91e33..6b34e0af8ac5e 100644
--- a/compiler-rt/lib/asan/asan_interceptors.cpp
+++ b/compiler-rt/lib/asan/asan_interceptors.cpp
@@ -1012,12 +1012,12 @@ void InitializeAsanInterceptors() {
   ASAN_INTERCEPT_FUNC(vfork);
 #  endif
 
-#  if SANITIZER_AMDHSA
+#if SANITIZER_AMDHSA
   // HSA/ROCr interceptors are split out of this TU to keep the host interceptor
   // surface clean. Initialize them here so the REAL() slots exist before first
   // HSA API call (the first such call is often hsa_init()).
   InitializeAmdgpuInterceptors();
-#  endif
+#endif
 
   VReport(1, "AddressSanitizer: libc interceptors initialized\n");
 }
diff --git a/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h b/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
index 53ab5a2766e68..64287c59a7107 100644
--- a/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
+++ b/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
@@ -16,7 +16,7 @@
 #include "hsa.h"
 
 #ifndef __cplusplus
-#  include <stdbool.h>
+#include <stdbool.h>
 #endif
 
 #define HSA_AMD_INTERFACE_VERSION_MAJOR 1
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index 3d7e1876a3c69..ba0f97c11456b 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -25,7 +25,7 @@
 #include "sanitizer_type_traits.h"
 
 #if SANITIZER_AMDHSA
-#  include "sanitizer_common/sanitizer_hsa.h"
+#include "sanitizer_common/sanitizer_hsa.h"
 #endif
 
 namespace __sanitizer {
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index d3ccf27c986df..649049d8dac3e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -10,13 +10,13 @@
 //
 //===----------------------------------------------------------------------===//
 #if SANITIZER_AMDHSA
-#  include <dlfcn.h>  // For dlopen, dlsym
+#include <dlfcn.h>  // For dlopen, dlsym
 
-#  include "sanitizer_allocator.h"
-#  include "sanitizer_atomic.h"
+#include "sanitizer_allocator.h"
+#include "sanitizer_atomic.h"
 
 namespace __sanitizer {
-#  include "sanitizer_allocator_amdgpu.h"
+#include "sanitizer_allocator_amdgpu.h"
 
 struct HsaFunctions {
   // -------------- Memory Allocate/Deallocate Functions ----------------
@@ -83,8 +83,7 @@ void AmdgpuDeviceAllocator::ClearRuntimeShutdownState() {
 
 void AmdgpuDeviceAllocator::NoteDeviceAllocatorFailure(
     DeviceAllocationInfo* da_info, DeviceAllocFailure failure) {
-  if (!da_info || da_info->type_ != DAT_AMDGPU)
-    return;
+  if (!da_info || da_info->type_ != DAT_AMDGPU) return;
   AmdgpuAllocationInfo* aa_info =
       reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
   switch (failure) {
@@ -104,23 +103,19 @@ void AmdgpuDeviceAllocator::NoteDeviceAllocatorFailure(
 // and not RTLD_NEXT/DEFAULT (those resolve ASan interceptors and recurse).
 bool AmdgpuDeviceAllocator::Init(bool allow_dlopen) {
   SpinMutexLock l(&hsa_runtime_init_mu);
-  if (HsaSymbolsLoaded())
-    return true;
+  if (HsaSymbolsLoaded()) return true;
 
   // Never dlopen during __asan_init.
-  if (!allow_dlopen)
-    return false;
+  if (!allow_dlopen) return false;
 
   if (!hsa_runtime_handle) {
     const char* path = GetEnv("SANITIZER_HSA_RUNTIME_PATH");
-    if (!path || !path[0])
-      path = kDefaultHsaRuntimePath;
+    if (!path || !path[0]) path = kDefaultHsaRuntimePath;
     // Prefer first `RTLD_NOLOAD` when ROCr is already mapped(after successful
     // REAL(hsa_init)). If RTLD_NOLOAD fails, try `RTLD_NOW` (when ROCr is not
     // loaded).
     hsa_runtime_handle = dlopen(path, RTLD_LAZY | RTLD_NOLOAD);
-    if (!hsa_runtime_handle)
-      hsa_runtime_handle = dlopen(path, RTLD_NOW);
+    if (!hsa_runtime_handle) hsa_runtime_handle = dlopen(path, RTLD_NOW);
     if (!hsa_runtime_handle) {
       const char* err = dlerror();
       VReport(2, "Amdgpu Init: dlopen(%s) failed: %s\n", path,
@@ -192,8 +187,7 @@ void* AmdgpuDeviceAllocator::Allocate(uptr size, uptr alignment,
     aa_info->status = hsa_amd.memory_pool_allocate(
         aa_info->memory_pool, size, aa_info->flags, &aa_info->ptr);
   }
-  if (aa_info->status != HSA_STATUS_SUCCESS)
-    return nullptr;
+  if (aa_info->status != HSA_STATUS_SUCCESS) return nullptr;
 
   return aa_info->ptr;
 }
@@ -249,8 +243,7 @@ static uptr AmdgpuPointerInfoMapBase(uptr ptr,
 
 bool AmdgpuDeviceAllocator::GetPointerInfo(uptr ptr,
                                            DevicePointerInfo* ptr_info) {
-  if (!ptr_info)
-    return false;
+  if (!ptr_info) return false;
 
   // GetPointerInfo returns false after AMDGPU runtime shutdown
   if (UNLIKELY(IsRuntimeShutdown())) {
@@ -265,15 +258,13 @@ bool AmdgpuDeviceAllocator::GetPointerInfo(uptr ptr,
   hsa_status_t status =
       hsa_amd.pointer_info(reinterpret_cast<void*>(ptr), &info, 0, 0, 0);
 
-  if (status != HSA_STATUS_SUCCESS)
-    return false;
+  if (status != HSA_STATUS_SUCCESS) return false;
 
   if (info.type == HSA_EXT_POINTER_TYPE_UNKNOWN || !info.sizeInBytes)
     return false;
 
   const uptr map_beg = AmdgpuPointerInfoMapBase(ptr, info);
-  if (!map_beg)
-    return false;
+  if (!map_beg) return false;
 
   ptr_info->map_beg = map_beg;
   ptr_info->map_size = info.sizeInBytes;
@@ -291,8 +282,7 @@ void AmdgpuDeviceAllocator::RegisterSystemEventHandlers() {
     // Callback to detect and notify AMDGPU runtime shutdown
     hsa_amd_system_event_callback_t callback = [](const hsa_amd_event_t* event,
                                                   void* data) {
-      if (!event)
-        return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+      if (!event) return HSA_STATUS_ERROR_INVALID_ARGUMENT;
       if (event->event_type == HSA_AMD_SYSTEM_SHUTDOWN_EVENT)
         AmdgpuDeviceAllocator::NotifyRuntimeShutdown();
       return HSA_STATUS_SUCCESS;
@@ -325,11 +315,9 @@ VmemGpuReserveTracker& VmemGpuReserveTracker::Get() {
 }
 
 void VmemGpuReserveTracker::EnsureInited() {
-  if (atomic_load(&inited_, memory_order_acquire))
-    return;
+  if (atomic_load(&inited_, memory_order_acquire)) return;
   SpinMutexLock l(&mu_);
-  if (atomic_load(&inited_, memory_order_relaxed))
-    return;
+  if (atomic_load(&inited_, memory_order_relaxed)) return;
   reservations_.Initialize(0);
   atomic_store(&inited_, 1, memory_order_release);
 }
@@ -350,12 +338,9 @@ VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::CheckFree(uptr ptr,
   SpinMutexLock l(&mu_);
   for (uptr i = 0; i < reservations_.size(); ++i) {
     const VmemGpuReservation& r = reservations_[i];
-    if (r.ptr != ptr)
-      continue;
-    if (r.size != size)
-      return kSizeMismatch;
-    if (r.freed)
-      return kDoubleFree;
+    if (r.ptr != ptr) continue;
+    if (r.size != size) return kSizeMismatch;
+    if (r.freed) return kDoubleFree;
     return kFirstFree;
   }
   return kNotTracked;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index 9f82753a2393a..f5d315d4476bc 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#  error This file must be included after sanitizer_allocator.h
+#error This file must be included after sanitizer_allocator.h
 #endif
 
 #if SANITIZER_AMDHSA
@@ -86,8 +86,7 @@ struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
   // If allocation fails without an HSA API status, record one so callers never
   // see *ptr == nullptr with status still SUCCESS.
   void EnsureFailureStatus(hsa_status_t err) {
-    if (status == HSA_STATUS_SUCCESS)
-      status = err;
+    if (status == HSA_STATUS_SUCCESS) status = err;
   }
 
   hsa_status_t status;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
index 49d1f252b8e9c..51e676a87c0a3 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -41,7 +41,7 @@ class CombinedAllocator {
     secondary_.Init();
   }
 
-  void* Allocate(AllocatorCache* cache, uptr size, uptr alignment) {
+  void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
     // Returning 0 on malloc(0) may break a lot of code.
     if (size == 0)
       size = 1;
@@ -64,7 +64,7 @@ class CombinedAllocator {
     // using a non-fixed base address). The secondary takes care of the
     // alignment without such requirement, and allocating 'size' would use
     // extraneous memory, so we employ 'original_size'.
-    void* res;
+    void *res;
     if (primary_.CanAllocate(size, alignment))
       res = cache->Allocate(&primary_, primary_.ClassID(size));
     else
@@ -130,10 +130,8 @@ class CombinedAllocator {
   // (via ForceLock). Uses GetBlockBeginFastLocked for the secondary check to
   // avoid re-acquiring the mutex already held by the caller.
   void* GetMetaDataFastLocked(const void* p) {
-    if (primary_.PointerIsMine(p))
-      return primary_.GetMetaData(p);
-    if (secondary_.GetBlockBeginFastLocked(p))
-      return secondary_.GetMetaData(p);
+    if (primary_.PointerIsMine(p)) return primary_.GetMetaData(p);
+    if (secondary_.GetBlockBeginFastLocked(p)) return secondary_.GetMetaData(p);
     return nullptr;
   }
 
@@ -145,7 +143,7 @@ class CombinedAllocator {
 
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
-  void* GetBlockBeginFastLocked(const void* p) {
+  void *GetBlockBeginFastLocked(const void *p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);
     return secondary_.GetBlockBeginFastLocked(p);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
index b1f1035381f78..5dd12bd12648f 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#  error This file must be included inside sanitizer_allocator.h
+#error This file must be included inside sanitizer_allocator.h
 #endif
 
 // DeviceCombinedAllocator adds an optional device-memory heap tier on top of an
@@ -49,13 +49,11 @@ class DeviceCombinedAllocator {
 
   void* Allocate(AllocatorCache* cache, uptr size, uptr alignment,
                  DeviceAllocationInfo* da_info = nullptr) {
-    if (!da_info)
-      return base_.Allocate(cache, size, alignment);
+    if (!da_info) return base_.Allocate(cache, size, alignment);
     // Device tier: mirror CombinedAllocator's malloc(0)/overflow guards. The
     // device allocator handles its own alignment/page rounding, so 'size' is
     // forwarded unrounded (CombinedAllocator's original_size semantics).
-    if (size == 0)
-      size = 1;
+    if (size == 0) size = 1;
     if (size + alignment < size) {
       Report(
           "WARNING: %s: DeviceCombinedAllocator allocation overflow: "
@@ -78,8 +76,7 @@ class DeviceCombinedAllocator {
   void ForceReleaseToOS() { base_.ForceReleaseToOS(); }
 
   void Deallocate(AllocatorCache* cache, void* p) {
-    if (!p)
-      return;
+    if (!p) return;
     if (device_.PointerIsMine(p))
       device_.Deallocate(&device_stats_, p);
     else
@@ -88,8 +85,7 @@ class DeviceCombinedAllocator {
 
   void* Reallocate(AllocatorCache* cache, void* p, uptr new_size,
                    uptr alignment) {
-    if (!p)
-      return Allocate(cache, new_size, alignment);
+    if (!p) return Allocate(cache, new_size, alignment);
     if (!new_size) {
       Deallocate(cache, p);
       return nullptr;
@@ -98,8 +94,7 @@ class DeviceCombinedAllocator {
     uptr old_size = GetActuallyAllocatedSize(p);
     uptr memcpy_size = Min(new_size, old_size);
     void* new_p = Allocate(cache, new_size, alignment);
-    if (new_p)
-      internal_memcpy(new_p, p, memcpy_size);
+    if (new_p) internal_memcpy(new_p, p, memcpy_size);
     Deallocate(cache, p);
     return new_p;
   }
@@ -111,8 +106,7 @@ class DeviceCombinedAllocator {
   bool FromPrimary(const void* p) const { return base_.FromPrimary(p); }
 
   void* GetMetaData(const void* p) {
-    if (device_.PointerIsMine(p))
-      return device_.GetMetaData(p);
+    if (device_.PointerIsMine(p)) return device_.GetMetaData(p);
     return base_.GetMetaData(p);
   }
 
@@ -120,28 +114,24 @@ class DeviceCombinedAllocator {
   // (via ForceLock). Uses GetBlockBeginFastLocked for the device check to avoid
   // re-acquiring a mutex already held by the caller.
   void* GetMetaDataFastLocked(const void* p) {
-    if (device_.GetBlockBeginFastLocked(p))
-      return device_.GetMetaData(p);
+    if (device_.GetBlockBeginFastLocked(p)) return device_.GetMetaData(p);
     return base_.GetMetaDataFastLocked(p);
   }
 
   void* GetBlockBegin(const void* p) {
-    if (void* beg = base_.GetBlockBegin(p))
-      return beg;
+    if (void* beg = base_.GetBlockBegin(p)) return beg;
     return device_.GetBlockBegin(p);
   }
 
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
   void* GetBlockBeginFastLocked(const void* p) {
-    if (void* beg = base_.GetBlockBeginFastLocked(p))
-      return beg;
+    if (void* beg = base_.GetBlockBeginFastLocked(p)) return beg;
     return device_.GetBlockBeginFastLocked(p);
   }
 
   uptr GetActuallyAllocatedSize(void* p) {
-    if (device_.PointerIsMine(p))
-      return device_.GetActuallyAllocatedSize(p);
+    if (device_.PointerIsMine(p)) return device_.GetActuallyAllocatedSize(p);
     return base_.GetActuallyAllocatedSize(p);
   }
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index cfcaa64c81564..d1141434e7a42 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#  error This file must be included inside sanitizer_allocator.h
+#error This file must be included inside sanitizer_allocator.h
 #endif
 
 // DeviceAllocatorT: device-mapped heap bookkeeping. Map/unmap uses the
@@ -83,8 +83,7 @@ class DeviceAllocatorT {
   // callers (and any wrapper) need not thread it in.
   void Init() {
     internal_memset(this, 0, sizeof(*this));
-    if (!DeviceBackend::kEnableDeviceBackend)
-      return;
+    if (!DeviceBackend::kEnableDeviceBackend) return;
     enabled_ = true;
     kMetadataSize_ = PrimaryAllocator::kMetadataSize;
     chunks_ = reinterpret_cast<uptr*>(ptr_array_.Init());
@@ -106,8 +105,7 @@ class DeviceAllocatorT {
     }
     CHECK(IsPowerOfTwo(alignment));
     uptr map_size = RoundUpMapSize(size);
-    if (alignment > page_size_)
-      map_size += alignment;
+    if (alignment > page_size_) map_size += alignment;
     // Overflow.
     if (map_size < size) {
       Report(
@@ -161,8 +159,7 @@ class DeviceAllocatorT {
       uptr p_ = reinterpret_cast<uptr>(p);
       EnsureSortedChunks();  // Avoid doing the sort while iterating.
       for (idx = 0; idx < n_chunks_; idx++) {
-        if (chunks_[idx] >= p_)
-          break;
+        if (chunks_[idx] >= p_) break;
       }
       CHECK_EQ(chunks_[idx], p_);
       CHECK_LT(idx, n_chunks_);
@@ -211,21 +208,17 @@ class DeviceAllocatorT {
 
   void* GetBlockBegin(const void* ptr) const {
     Header header;
-    if (!mem_funcs_inited_)
-      return nullptr;
+    if (!mem_funcs_inited_) return nullptr;
     uptr p = reinterpret_cast<uptr>(ptr);
     SpinMutexLock l(&mutex_);
     uptr nearest_chunk = 0;
     // Cache-friendly linear search.
     for (uptr i = 0; i < n_chunks_; i++) {
       uptr ch = chunks_[i];
-      if (p < ch)
-        continue;  // p is at left to this chunk, skip it.
-      if (p - ch < p - nearest_chunk)
-        nearest_chunk = ch;
+      if (p < ch) continue;  // p is at left to this chunk, skip it.
+      if (p - ch < p - nearest_chunk) nearest_chunk = ch;
     }
-    if (!nearest_chunk)
-      return nullptr;
+    if (!nearest_chunk) return nullptr;
     if (p != nearest_chunk) {
       Header* h = GetHeader(nearest_chunk, &header);
       CHECK_GE(nearest_chunk, h->map_beg);
@@ -239,8 +232,7 @@ class DeviceAllocatorT {
   }
 
   void EnsureSortedChunks() {
-    if (chunks_sorted_)
-      return;
+    if (chunks_sorted_) return;
     Sort(reinterpret_cast<uptr*>(chunks_), n_chunks_);
     chunks_sorted_ = true;
   }
@@ -248,20 +240,17 @@ class DeviceAllocatorT {
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
   void* GetBlockBeginFastLocked(const void* ptr) {
-    if (!mem_funcs_inited_)
-      return nullptr;
+    if (!mem_funcs_inited_) return nullptr;
     mutex_.CheckLocked();
     uptr p = reinterpret_cast<uptr>(ptr);
     uptr n = n_chunks_;
-    if (!n)
-      return nullptr;
+    if (!n) return nullptr;
     EnsureSortedChunks();
     Header header, *h;
     h = GetHeader(chunks_[n - 1], &header);
     uptr min_mmap_ = chunks_[0];
     uptr max_mmap_ = chunks_[n - 1] + h->map_size;
-    if (p < min_mmap_)
-      return nullptr;
+    if (p < min_mmap_) return nullptr;
     if (p >= max_mmap_) {
       // TODO (bingma): If dev_runtime_unloaded_ = true, map_size is limited
       // to one page and we might miss a valid 'ptr'. If we hit cases where
@@ -283,15 +272,13 @@ class DeviceAllocatorT {
     if (beg < end) {
       CHECK_EQ(beg + 1, end);
       // There are 2 chunks left, choose one.
-      if (p >= chunks_[end])
-        beg = end;
+      if (p >= chunks_[end]) beg = end;
     }
 
     if (p != chunks_[beg]) {
       h = GetHeader(chunks_[beg], &header);
       CHECK_NE(h, nullptr);
-      if (p < h->map_beg)
-        return nullptr;
+      if (p < h->map_beg) return nullptr;
       if (h->map_beg + h->map_size <= p) {
         // TODO (bingma): See above TODO in this function
         return nullptr;
@@ -308,8 +295,7 @@ class DeviceAllocatorT {
         stats.currently_allocated >> 10, stats.max_allocated >> 20);
     for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) {
       uptr c = stats.by_size_log[i];
-      if (!c)
-        continue;
+      if (!c) continue;
       Printf("%zd:%zd; ", i, c);
     }
     Printf("\n");
@@ -340,8 +326,7 @@ class DeviceAllocatorT {
     }
     mem_funcs_inited_ = DeviceBackend::Init();
     mem_funcs_init_count_++;
-    if (mem_funcs_inited_)
-      page_size_ = DeviceBackend::GetPageSize();
+    if (mem_funcs_inited_) page_size_ = DeviceBackend::GetPageSize();
     return mem_funcs_inited_;
   }
 
@@ -357,8 +342,7 @@ class DeviceAllocatorT {
     // is unloaded, GetPointerInfo() will fail. For such case, we can still
     // return a valid value for map_beg, map_size will be limited to one page
     if (LIKELY(!dev_runtime_unloaded_)) {
-      if (DeviceBackend::GetPointerInfo(chunk, h))
-        return h;
+      if (DeviceBackend::GetPointerInfo(chunk, h)) return h;
       // If GetPointerInfo() fails, we don't assume the runtime is unloaded yet.
       // We just return a conservative single-page header. Here mark/check the
       // runtime shutdown state
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
index 2fb689916d62d..74b36ca7e8e9e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
@@ -322,7 +322,7 @@
 #endif
 
 #ifndef SANITIZER_AMDHSA
-#  define SANITIZER_AMDHSA 0
+#define SANITIZER_AMDHSA 0
 #endif
 
 #if defined(__NVPTX__)
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
index f39b86e6fe333..164544332b233 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // After hsa_amd_ipc_memory_attach, AddressSanitizer poisons a trailing redzone
@@ -15,23 +15,20 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
-#include "hsa_amd_test_helpers.h"
-
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <stdio.h>
 
+#include "hsa_amd_test_helpers.h"
+
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  if (hsa_amd_test_require_init()) return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
-    return 1;
+  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps)) return 1;
 
   constexpr size_t kBytes = 64;
-  void *mem = nullptr;
+  void* mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -46,7 +43,7 @@ int main() {
     return 1;
   }
 
-  void *mapped = nullptr;
+  void* mapped = nullptr;
   if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
                                 /*mapping_agents=*/nullptr,
                                 &mapped) != HSA_STATUS_SUCCESS ||
@@ -61,13 +58,15 @@ int main() {
   (void)hsa_iterate_agents(hsa_amd_test_pick_first_cpu_agent_cb, &cpu);
   if (cpu.agent.handle != 0) {
     /* Best-effort: allow the host CPU to access the imported range so the store
-       below is a normal fault checked by ASan, not an unmapped-device SIGSEGV. */
+       below is a normal fault checked by ASan, not an unmapped-device SIGSEGV.
+     */
     (void)hsa_amd_agents_allow_access(/*num_agents=*/1, &cpu.agent,
                                       /*flags=*/nullptr, mapped);
   }
 
-  auto *p = reinterpret_cast<volatile char *>(mapped);
-  // One byte past the 64-byte imported region; should land in ASan's tail redzone.
+  auto* p = reinterpret_cast<volatile char*>(mapped);
+  // One byte past the 64-byte imported region; should land in ASan's tail
+  // redzone.
   p[kBytes] = 1;
 
   fprintf(stderr, "expected heap-buffer-overflow after ipc attach\n");
@@ -78,4 +77,5 @@ int main() {
 
 // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
 // CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
-// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:71:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow
+// {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:71:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
index f84b1bbdde2a2..d4ebace6a9fa8 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
@@ -1,12 +1,12 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: %run %t 2>&1 | FileCheck %s
 //
-// Regression test for AddressSanitizer hsa_amd_ipc_memory_{create,attach,detach}:
-// same-process IPC round-trip on a pool allocation (unwrap on create, user pointer
-// adjustment and shadow on attach, base adjustment on detach).
-// hsa_amd_ipc_memory_create only supports coarse-grained GPU allocations; skip
-// fine-grained pools and non-GPU agents.
+// Regression test for AddressSanitizer
+// hsa_amd_ipc_memory_{create,attach,detach}: same-process IPC round-trip on a
+// pool allocation (unwrap on create, user pointer adjustment and shadow on
+// attach, base adjustment on detach). hsa_amd_ipc_memory_create only supports
+// coarse-grained GPU allocations; skip fine-grained pools and non-GPU agents.
 //
 // Coarse-grained device memory is often not mapped for CPU stores; do not
 // read/write *mapped from the host. Validate with hsa_amd_pointer_info instead.
@@ -14,23 +14,20 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
-#include "hsa_amd_test_helpers.h"
-
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <stdio.h>
 
+#include "hsa_amd_test_helpers.h"
+
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  if (hsa_amd_test_require_init()) return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
-    return 1;
+  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps)) return 1;
 
   constexpr size_t kBytes = 64;
-  void *mem = nullptr;
+  void* mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -45,7 +42,7 @@ int main() {
     return 1;
   }
 
-  void *mapped = nullptr;
+  void* mapped = nullptr;
   if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
                                 /*mapping_agents=*/nullptr,
                                 &mapped) != HSA_STATUS_SUCCESS ||
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
index c773b55b8e866..2b24e29e6c38d 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
@@ -1,30 +1,28 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
-// Regression test for the AddressSanitizer hsa_amd_memory_async_copy interceptor:
-// invalid overlapping ranges are diagnosed (same family of checks as memcpy).
+// Regression test for the AddressSanitizer hsa_amd_memory_async_copy
+// interceptor: invalid overlapping ranges are diagnosed (same family of checks
+// as memcpy).
 //
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
-#include "hsa_amd_test_helpers.h"
-
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "hsa_amd_test_helpers.h"
+
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  if (hsa_amd_test_require_init()) return 1;
 
   HsaAmdAgentPick pick;
   hsa_amd_test_agent_pick_init(&pick);
   hsa_status_t it = hsa_iterate_agents(hsa_amd_test_pick_first_agent_cb, &pick);
-  if (hsa_amd_test_iterate_agents_ok(it))
-    return 1;
+  if (hsa_amd_test_iterate_agents_ok(it)) return 1;
   if (pick.agent.handle == 0) {
     fprintf(stderr, "no HSA agent found\n");
     return 1;
@@ -39,8 +37,8 @@ int main() {
   }
 
   char buf[128];
-  char *dst = buf;
-  char *src = buf + 40;
+  char* dst = buf;
+  char* src = buf + 40;
   // Ranges [buf, buf+64) and [buf+40, buf+104) overlap; dst != src so the
   // interceptor runs CHECK_RANGES_OVERLAP before scheduling the async copy.
   (void)hsa_amd_memory_async_copy(dst, pick.agent, src, pick.agent, 64,
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index 82b21962b0948..b7483fc851f58 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_amd_memory_pool_allocate /
@@ -9,22 +9,19 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
-#include "hsa_amd_test_helpers.h"
-
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <stdio.h>
 
+#include "hsa_amd_test_helpers.h"
+
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  if (hsa_amd_test_require_init()) return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
-    return 1;
+  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps)) return 1;
 
-  void *mem = nullptr;
+  void* mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -40,4 +37,5 @@ int main() {
 }
 
 // CHECK: ERROR: AddressSanitizer: attempting double-free
-// CHECK: SUMMARY: AddressSanitizer: double-free {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:36:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: double-free
+// {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:36:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
index c6c54373210e5..006353941e8e7 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_amd_pointer_info interceptor on
@@ -9,22 +9,19 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
-#include "hsa_amd_test_helpers.h"
-
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <stdio.h>
 
+#include "hsa_amd_test_helpers.h"
+
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  if (hsa_amd_test_require_init()) return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
-    return 1;
+  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps)) return 1;
 
-  void *mem = nullptr;
+  void* mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -45,7 +42,7 @@ int main() {
   printf("pointer_info_pool sizeInBytes: %zu\n", info.sizeInBytes);
   printf("pointer_info_pool begin: %p\n", info.agentBaseAddress);
   printf("pointer_info_pool end: %p\n",
-         (void *)((uintptr_t)info.agentBaseAddress + info.sizeInBytes));
+         (void*)((uintptr_t)info.agentBaseAddress + info.sizeInBytes));
 
   if (hsa_amd_memory_pool_free(mem) != HSA_STATUS_SUCCESS) {
     fprintf(stderr, "hsa_amd_memory_pool_free failed\n");
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
index dceb8eca54d41..ffb406d4558c7 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: %run %t 2>&1 | FileCheck %s
 //
 // hsa_amd_pointer_info on vmem reserved via reserve_align() must report the
@@ -9,23 +9,22 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
-#include "hsa_amd_test_helpers.h"
-
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>
 
+#include "hsa_amd_test_helpers.h"
+
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  if (hsa_amd_test_require_init()) return 1;
 
-  const size_t kSize = 4096; //4KiB size
-  // Here alignment is larger than a page size, so the reserved address will be aligned to 64KiB.
-  const uint64_t kAlign = 65536; //64KiB alignment
-  void *mem = nullptr;
+  const size_t kSize = 4096;  // 4KiB size
+  // Here alignment is larger than a page size, so the reserved address will be
+  // aligned to 64KiB.
+  const uint64_t kAlign = 65536;  // 64KiB alignment
+  void* mem = nullptr;
   // Host-accessible vmem reserve via NO_REGISTER (host mmap, not KFD VA).
   if (hsa_amd_vmem_address_reserve_align(
           &mem, kSize, /*address=*/0, kAlign,
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
index 53b7f571fcbcf..1ae83d4efe28d 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
@@ -1,4 +1,5 @@
-//===-- hsa_amd_test_helpers.h - shared helpers for AMDGPU ASan HSA tests --===//
+//===-- hsa_amd_test_helpers.h - shared helpers for AMDGPU ASan HSA tests
+//--===//
 //
 // Common ROCm/HSA discovery and init helpers for hsa_amd*.cpp tests in this
 // directory. Intended only for compiler-rt lit tests.
@@ -10,7 +11,6 @@
 
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <stdio.h>
 
 inline int hsa_amd_test_require_init() {
@@ -35,10 +35,9 @@ struct HsaAmdPoolSearch {
   bool found;
 };
 
-inline hsa_status_t
-hsa_amd_test_find_runtime_alloc_pool_cb(hsa_amd_memory_pool_t pool,
-                                        void *data) {
-  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+inline hsa_status_t hsa_amd_test_find_runtime_alloc_pool_cb(
+    hsa_amd_memory_pool_t pool, void* data) {
+  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
   bool allow = false;
   if (hsa_amd_memory_pool_get_info(
           pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
@@ -50,28 +49,24 @@ hsa_amd_test_find_runtime_alloc_pool_cb(hsa_amd_memory_pool_t pool,
   return HSA_STATUS_INFO_BREAK;
 }
 
-inline hsa_status_t
-hsa_amd_test_find_agent_with_runtime_alloc_pool_cb(hsa_agent_t agent,
-                                                   void *data) {
+inline hsa_status_t hsa_amd_test_find_agent_with_runtime_alloc_pool_cb(
+    hsa_agent_t agent, void* data) {
   (void)agent;
-  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
   ps->found = false;
   hsa_status_t st = hsa_amd_agent_iterate_memory_pools(
       agent, hsa_amd_test_find_runtime_alloc_pool_cb, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
-    return st;
-  if (ps->found)
-    return HSA_STATUS_INFO_BREAK;
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK) return st;
+  if (ps->found) return HSA_STATUS_INFO_BREAK;
   return HSA_STATUS_SUCCESS;
 }
 
-inline int hsa_amd_test_find_first_runtime_alloc_pool(HsaAmdPoolSearch *ps) {
+inline int hsa_amd_test_find_first_runtime_alloc_pool(HsaAmdPoolSearch* ps) {
   ps->pool.handle = 0;
   ps->found = false;
   hsa_status_t it = hsa_iterate_agents(
       hsa_amd_test_find_agent_with_runtime_alloc_pool_cb, ps);
-  if (hsa_amd_test_iterate_agents_ok(it))
-    return 1;
+  if (hsa_amd_test_iterate_agents_ok(it)) return 1;
   if (!ps->found) {
     fprintf(stderr, "no runtime-alloc HSA memory pool found\n");
     return 1;
@@ -79,17 +74,15 @@ inline int hsa_amd_test_find_first_runtime_alloc_pool(HsaAmdPoolSearch *ps) {
   return 0;
 }
 
-inline hsa_status_t
-hsa_amd_test_find_coarse_gpu_ipc_pool_cb(hsa_amd_memory_pool_t pool,
-                                         void *data) {
-  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+inline hsa_status_t hsa_amd_test_find_coarse_gpu_ipc_pool_cb(
+    hsa_amd_memory_pool_t pool, void* data) {
+  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
 
   hsa_amd_segment_t segment = HSA_AMD_SEGMENT_PRIVATE;
   if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT,
                                    &segment) != HSA_STATUS_SUCCESS)
     return HSA_STATUS_SUCCESS;
-  if (segment != HSA_AMD_SEGMENT_GLOBAL)
-    return HSA_STATUS_SUCCESS;
+  if (segment != HSA_AMD_SEGMENT_GLOBAL) return HSA_STATUS_SUCCESS;
 
   uint32_t global_flags = 0;
   if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS,
@@ -110,33 +103,29 @@ hsa_amd_test_find_coarse_gpu_ipc_pool_cb(hsa_amd_memory_pool_t pool,
   return HSA_STATUS_INFO_BREAK;
 }
 
-inline hsa_status_t
-hsa_amd_test_find_gpu_agent_with_ipc_pool_cb(hsa_agent_t agent, void *data) {
+inline hsa_status_t hsa_amd_test_find_gpu_agent_with_ipc_pool_cb(
+    hsa_agent_t agent, void* data) {
   hsa_device_type_t dev = HSA_DEVICE_TYPE_CPU;
   if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
       HSA_STATUS_SUCCESS)
     return HSA_STATUS_SUCCESS;
-  if (dev != HSA_DEVICE_TYPE_GPU)
-    return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_GPU) return HSA_STATUS_SUCCESS;
 
-  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
+  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
   ps->found = false;
   hsa_status_t st = hsa_amd_agent_iterate_memory_pools(
       agent, hsa_amd_test_find_coarse_gpu_ipc_pool_cb, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
-    return st;
-  if (ps->found)
-    return HSA_STATUS_INFO_BREAK;
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK) return st;
+  if (ps->found) return HSA_STATUS_INFO_BREAK;
   return HSA_STATUS_SUCCESS;
 }
 
-inline int hsa_amd_test_find_first_coarse_gpu_ipc_pool(HsaAmdPoolSearch *ps) {
+inline int hsa_amd_test_find_first_coarse_gpu_ipc_pool(HsaAmdPoolSearch* ps) {
   ps->pool.handle = 0;
   ps->found = false;
   hsa_status_t it =
       hsa_iterate_agents(hsa_amd_test_find_gpu_agent_with_ipc_pool_cb, ps);
-  if (hsa_amd_test_iterate_agents_ok(it))
-    return 1;
+  if (hsa_amd_test_iterate_agents_ok(it)) return 1;
   if (!ps->found) {
     fprintf(stderr,
             "no coarse-grained GPU runtime-alloc HSA memory pool found\n");
@@ -149,19 +138,18 @@ struct HsaAmdCpuAgentPick {
   hsa_agent_t agent;
 };
 
-inline void hsa_amd_test_cpu_agent_pick_init(HsaAmdCpuAgentPick *out) {
+inline void hsa_amd_test_cpu_agent_pick_init(HsaAmdCpuAgentPick* out) {
   out->agent.handle = 0;
 }
 
 inline hsa_status_t hsa_amd_test_pick_first_cpu_agent_cb(hsa_agent_t agent,
-                                                         void *data) {
-  auto *out = static_cast<HsaAmdCpuAgentPick *>(data);
+                                                         void* data) {
+  auto* out = static_cast<HsaAmdCpuAgentPick*>(data);
   hsa_device_type_t dev = HSA_DEVICE_TYPE_GPU;
   if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
       HSA_STATUS_SUCCESS)
     return HSA_STATUS_SUCCESS;
-  if (dev != HSA_DEVICE_TYPE_CPU)
-    return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_CPU) return HSA_STATUS_SUCCESS;
   out->agent = agent;
   return HSA_STATUS_INFO_BREAK;
 }
@@ -170,15 +158,15 @@ struct HsaAmdAgentPick {
   hsa_agent_t agent;
 };
 
-inline void hsa_amd_test_agent_pick_init(HsaAmdAgentPick *out) {
+inline void hsa_amd_test_agent_pick_init(HsaAmdAgentPick* out) {
   out->agent.handle = 0;
 }
 
 inline hsa_status_t hsa_amd_test_pick_first_agent_cb(hsa_agent_t agent,
-                                                     void *data) {
-  auto *out = static_cast<HsaAmdAgentPick *>(data);
+                                                     void* data) {
+  auto* out = static_cast<HsaAmdAgentPick*>(data);
   out->agent = agent;
   return HSA_STATUS_INFO_BREAK;
 }
 
-#endif // COMPILER_RT_ASAN_AMDGPU_HSA_AMD_TEST_HELPERS_H
+#endif  // COMPILER_RT_ASAN_AMDGPU_HSA_AMD_TEST_HELPERS_H
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index 1828e6e930bbd..d2476784bf267 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
@@ -9,22 +9,20 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
-#include "hsa_amd_test_helpers.h"
-
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-
 #include <stdio.h>
 
+#include "hsa_amd_test_helpers.h"
+
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  if (hsa_amd_test_require_init()) return 1;
 
   // Size must be a non-zero multiple of the page size; address must be 0 so
   // the interceptor records the reservation for double-free diagnosis (see
   // asan_hsa_amd_vmem_address_reserve_align).
   const size_t kSize = 4096;
-  void *mem = nullptr;
+  void* mem = nullptr;
 
   // NOTE: To use `hipMallocManaged` way of reserving memory,
   // use either `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in flags.
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
index 51f90366d40b2..a74370e802f0d 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
-// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
+// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_memory_copy interceptor: invalid
@@ -9,7 +9,6 @@
 // UNSUPPORTED: android
 
 #include <hsa/hsa.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -20,8 +19,8 @@ int main() {
   }
 
   char buf[128];
-  char *dst = buf;
-  char *src = buf + 40;
+  char* dst = buf;
+  char* src = buf + 40;
   // Ranges [buf, buf+64) and [buf+40, buf+104) overlap; dst != src so the
   // interceptor runs CHECK_RANGES_OVERLAP.
   (void)hsa_memory_copy(dst, src, 64);

>From 2982c9d92106545a5ba2b80a5bd2ddaa182c4fce Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 23 Jun 2026 10:59:59 +0530
Subject: [PATCH 38/46] Revert "[NFC] Use `Google` Style formatting
 explicitly."

This reverts commit d44ec9346347e08df983653742ce8797af23da9c.
---
 compiler-rt/lib/asan/asan_allocator.cpp       | 12 ++-
 compiler-rt/lib/asan/asan_allocator.h         |  2 +-
 compiler-rt/lib/asan/asan_descriptions.cpp    |  3 +-
 compiler-rt/lib/asan/asan_hsa_allocator.cpp   | 32 ++++----
 compiler-rt/lib/asan/asan_hsa_allocator.h     |  4 +-
 compiler-rt/lib/asan/asan_hsa_linux.cpp       | 29 +++----
 compiler-rt/lib/asan/asan_interceptors.cpp    |  4 +-
 .../lib/sanitizer_common/hsa/hsa_ext_amd.h    |  2 +-
 .../sanitizer_common/sanitizer_allocator.h    |  2 +-
 .../sanitizer_allocator_amdgpu.cpp            | 53 ++++++++-----
 .../sanitizer_allocator_amdgpu.h              |  5 +-
 .../sanitizer_allocator_combined.h            | 12 +--
 .../sanitizer_allocator_combined_device.h     | 32 +++++---
 .../sanitizer_allocator_device.h              | 50 +++++++-----
 .../lib/sanitizer_common/sanitizer_platform.h |  2 +-
 .../hsa_amd_ipc_memory_attach_heap_oob.cpp    | 30 ++++----
 .../AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp   | 29 +++----
 .../hsa_amd_memory_async_copy_overlap.cpp     | 24 +++---
 ...a_amd_memory_pool_allocate_double_free.cpp | 20 ++---
 .../hsa_amd_pointer_info_memory_pool.cpp      | 19 +++--
 ...sa_amd_pointer_info_vmem_reserve_align.cpp | 21 ++---
 .../TestCases/AMDGPU/hsa_amd_test_helpers.h   | 76 +++++++++++--------
 ...vmem_address_reserve_align_double_free.cpp | 14 ++--
 .../AMDGPU/hsa_memory_copy_overlap.cpp        |  9 ++-
 24 files changed, 282 insertions(+), 204 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 45ceb962ec89e..dee06b646e4c6 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1521,10 +1521,12 @@ bool AsanHsaTranslateIpcCreate(void* ptr, size_t len, void** out_ptr,
   AsanChunk* m = ptr_
                      ? instance.GetAsanChunkByAddr(reinterpret_cast<uptr>(ptr_))
                      : nullptr;
-  if (!ptr_ || !m) return false;
+  if (!ptr_ || !m)
+    return false;
   uptr p = reinterpret_cast<uptr>(ptr);
   uptr p_ = reinterpret_cast<uptr>(ptr_);
-  if (p != p_ + kHsaPageSize || len != m->UsedSize()) return false;
+  if (p != p_ + kHsaPageSize || len != m->UsedSize())
+    return false;
   *out_ptr = ptr_;
   *out_len = get_allocator().GetActuallyAllocatedSize(ptr_);
   return true;
@@ -1532,7 +1534,8 @@ bool AsanHsaTranslateIpcCreate(void* ptr, size_t len, void** out_ptr,
 
 bool AsanHsaIsVmemFreeValid(void* ptr, uptr size) {
   void* p = get_allocator().GetBlockBegin(ptr);
-  if (!p) return false;
+  if (!p)
+    return false;
   uptr ptr_ = reinterpret_cast<uptr>(ptr);
   AsanChunk* m = reinterpret_cast<AsanChunk*>(ptr_ - kChunkHeaderSize);
   return m->Beg() == ptr_ && size == m->UsedSize();
@@ -1541,7 +1544,8 @@ bool AsanHsaIsVmemFreeValid(void* ptr, uptr size) {
 bool AsanHsaGetLiveMappingInfo(const void* ptr, void** map_base,
                                uptr* used_size, uptr* offset) {
   void* hsa_map_base = get_allocator().GetBlockBegin(ptr);
-  if (!hsa_map_base) return false;
+  if (!hsa_map_base)
+    return false;
   uptr user = reinterpret_cast<uptr>(ptr);
   AsanChunk* m = reinterpret_cast<AsanChunk*>(user - kChunkHeaderSize);
   if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED ||
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index 61d1b9e23becb..3cb6b3b9a11f0 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -20,7 +20,7 @@
 #include "sanitizer_common/sanitizer_allocator.h"
 #if SANITIZER_AMDHSA
 namespace __sanitizer {
-#include "sanitizer_common/sanitizer_allocator_amdgpu.h"
+#  include "sanitizer_common/sanitizer_allocator_amdgpu.h"
 }  // namespace __sanitizer
 #endif
 #include "sanitizer_common/sanitizer_list.h"
diff --git a/compiler-rt/lib/asan/asan_descriptions.cpp b/compiler-rt/lib/asan/asan_descriptions.cpp
index 71289f202d16d..e6e815591f4f6 100644
--- a/compiler-rt/lib/asan/asan_descriptions.cpp
+++ b/compiler-rt/lib/asan/asan_descriptions.cpp
@@ -418,7 +418,8 @@ void StackAddressDescription::Print() const {
 }
 
 void HeapAddressDescription::Print() const {
-  if (alloc_tid == kInvalidTid) return;
+  if (alloc_tid == kInvalidTid)
+    return;
 
   PrintHeapChunkAccess(addr, chunk_access);
 
diff --git a/compiler-rt/lib/asan/asan_hsa_allocator.cpp b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
index de5f8b9f82bc5..149321b1a60d4 100644
--- a/compiler-rt/lib/asan/asan_hsa_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
@@ -14,16 +14,16 @@
 
 #if SANITIZER_AMDHSA
 
-#include "asan_allocator.h"
-#include "asan_hsa_allocator.h"
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "sanitizer_common/sanitizer_allocator_checks.h"
-#include "sanitizer_common/sanitizer_errno.h"
-#include "sanitizer_common/sanitizer_hsa.h"
+#  include "asan_allocator.h"
+#  include "asan_hsa_allocator.h"
+#  include "asan_interceptors.h"
+#  include "asan_internal.h"
+#  include "asan_mapping.h"
+#  include "asan_poisoning.h"
+#  include "asan_report.h"
+#  include "sanitizer_common/sanitizer_allocator_checks.h"
+#  include "sanitizer_common/sanitizer_errno.h"
+#  include "sanitizer_common/sanitizer_hsa.h"
 
 namespace __asan {
 
@@ -51,13 +51,13 @@ DECLARE_REAL(hsa_status_t, hsa_amd_pointer_info, const void* ptr,
 // Always align to page boundary to match current ROCr behavior.
 static const size_t kPageSize_ = 4096;
 
-#if SANITIZER_CAN_USE_ALLOCATOR64
+#  if SANITIZER_CAN_USE_ALLOCATOR64
 static_assert(AP64<LocalAddressSpaceView>::kMetadataSize == 0,
               "HSA IPC/VMem wrappers require zero allocator metadata");
-#else
+#  else
 static_assert(AP32<LocalAddressSpaceView>::kMetadataSize == 0,
               "HSA IPC/VMem wrappers require zero allocator metadata");
-#endif
+#  endif
 
 hsa_status_t asan_hsa_amd_memory_pool_allocate(
     hsa_amd_memory_pool_t memory_pool, size_t size, uint32_t flags, void** ptr,
@@ -122,7 +122,8 @@ hsa_status_t asan_hsa_amd_ipc_memory_attach(const hsa_amd_ipc_memory_t* handle,
       PoisonShadow(tail_beg, mapped_end - tail_beg, kAsanHeapLeftRedzoneMagic);
 
     uptr size_rounded_down = RoundDownTo(len, ASAN_SHADOW_GRANULARITY);
-    if (size_rounded_down) PoisonShadow(user_beg, size_rounded_down, 0);
+    if (size_rounded_down)
+      PoisonShadow(user_beg, size_rounded_down, 0);
 
     if (len != size_rounded_down && CanPoisonMemory()) {
       u8* shadow = (u8*)MemToShadow(user_beg + size_rounded_down);
@@ -172,7 +173,8 @@ hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
     return REAL(hsa_amd_vmem_address_reserve_align)(ptr, size, address,
                                                     alignment, flags);
 
-  if (alignment < kPageSize_) alignment = kPageSize_;
+  if (alignment < kPageSize_)
+    alignment = kPageSize_;
 
   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
     errno = errno_EINVAL;
diff --git a/compiler-rt/lib/asan/asan_hsa_allocator.h b/compiler-rt/lib/asan/asan_hsa_allocator.h
index 0a7f83dfeef64..7d5b7f4c174f2 100644
--- a/compiler-rt/lib/asan/asan_hsa_allocator.h
+++ b/compiler-rt/lib/asan/asan_hsa_allocator.h
@@ -17,8 +17,8 @@
 
 #if SANITIZER_AMDHSA
 
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_hsa.h"
+#  include "asan_stack.h"
+#  include "sanitizer_common/sanitizer_hsa.h"
 
 namespace __asan {
 
diff --git a/compiler-rt/lib/asan/asan_hsa_linux.cpp b/compiler-rt/lib/asan/asan_hsa_linux.cpp
index 872aac96c6ab9..1c60fde946990 100644
--- a/compiler-rt/lib/asan/asan_hsa_linux.cpp
+++ b/compiler-rt/lib/asan/asan_hsa_linux.cpp
@@ -14,13 +14,13 @@
 
 #if SANITIZER_AMDHSA
 
-#include "asan_hsa_linux.h"
-#include "asan_interceptors.h"
-#include "asan_interceptors_memintrinsics.h"
-#include "asan_internal.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_suppressions.h"
+#  include "asan_hsa_linux.h"
+#  include "asan_interceptors.h"
+#  include "asan_interceptors_memintrinsics.h"
+#  include "asan_internal.h"
+#  include "asan_report.h"
+#  include "asan_stack.h"
+#  include "asan_suppressions.h"
 
 using namespace __asan;
 
@@ -39,11 +39,11 @@ DEFINE_REAL(hsa_status_t, hsa_memory_free, void* ptr)
 DEFINE_REAL(hsa_status_t, hsa_amd_memory_async_copy, void*, hsa_agent_t,
             const void*, hsa_agent_t, size_t, uint32_t, const hsa_signal_t*,
             hsa_signal_t)
-#if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
 DEFINE_REAL(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void*,
             hsa_agent_t, const void*, hsa_agent_t, size_t, uint32_t,
             const hsa_signal_t*, hsa_signal_t, hsa_amd_sdma_engine_id_t, bool)
-#endif
+#  endif
 DEFINE_REAL(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
             hsa_amd_ipc_memory_t* handle)
 DEFINE_REAL(hsa_status_t, hsa_amd_ipc_memory_attach,
@@ -63,7 +63,8 @@ static void ENSURE_HSA_INITED() {
   // The HSA interceptors are initialized lazily: if the program calls into HSA
   // before ASan's global interceptor init runs, we still need the REAL() slots
   // to be populated so we can call through to ROCr.
-  if (!REAL(hsa_init)) InitializeAmdgpuInterceptors();
+  if (!REAL(hsa_init))
+    InitializeAmdgpuInterceptors();
 }
 
 INTERCEPTOR(hsa_status_t, hsa_amd_memory_pool_allocate,
@@ -131,7 +132,7 @@ INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy, void* dst,
                                          completion_signal);
 }
 
-#if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
 INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void* dst,
             hsa_agent_t dst_agent, const void* src, hsa_agent_t src_agent,
             size_t size, uint32_t num_dep_signals,
@@ -151,7 +152,7 @@ INTERCEPTOR(hsa_status_t, hsa_amd_memory_async_copy_on_engine, void* dst,
       dst, dst_agent, src, src_agent, size, num_dep_signals, dep_signals,
       completion_signal, engine_id, force_copy_on_sdma);
 }
-#endif
+#  endif
 
 INTERCEPTOR(hsa_status_t, hsa_amd_ipc_memory_create, void* ptr, size_t len,
             hsa_amd_ipc_memory_t* handle) {
@@ -213,9 +214,9 @@ void InitializeAmdgpuInterceptors() {
   ASAN_INTERCEPT_FUNC(hsa_amd_memory_pool_free);
   ASAN_INTERCEPT_FUNC(hsa_amd_agents_allow_access);
   ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy);
-#if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
+#  if HSA_AMD_INTERFACE_VERSION_MINOR >= 1
   ASAN_INTERCEPT_FUNC(hsa_amd_memory_async_copy_on_engine);
-#endif
+#  endif
   ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_create);
   ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_attach);
   ASAN_INTERCEPT_FUNC(hsa_amd_ipc_memory_detach);
diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp
index 6b34e0af8ac5e..469a5fea91e33 100644
--- a/compiler-rt/lib/asan/asan_interceptors.cpp
+++ b/compiler-rt/lib/asan/asan_interceptors.cpp
@@ -1012,12 +1012,12 @@ void InitializeAsanInterceptors() {
   ASAN_INTERCEPT_FUNC(vfork);
 #  endif
 
-#if SANITIZER_AMDHSA
+#  if SANITIZER_AMDHSA
   // HSA/ROCr interceptors are split out of this TU to keep the host interceptor
   // surface clean. Initialize them here so the REAL() slots exist before first
   // HSA API call (the first such call is often hsa_init()).
   InitializeAmdgpuInterceptors();
-#endif
+#  endif
 
   VReport(1, "AddressSanitizer: libc interceptors initialized\n");
 }
diff --git a/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h b/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
index 64287c59a7107..53ab5a2766e68 100644
--- a/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
+++ b/compiler-rt/lib/sanitizer_common/hsa/hsa_ext_amd.h
@@ -16,7 +16,7 @@
 #include "hsa.h"
 
 #ifndef __cplusplus
-#include <stdbool.h>
+#  include <stdbool.h>
 #endif
 
 #define HSA_AMD_INTERFACE_VERSION_MAJOR 1
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index ba0f97c11456b..3d7e1876a3c69 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -25,7 +25,7 @@
 #include "sanitizer_type_traits.h"
 
 #if SANITIZER_AMDHSA
-#include "sanitizer_common/sanitizer_hsa.h"
+#  include "sanitizer_common/sanitizer_hsa.h"
 #endif
 
 namespace __sanitizer {
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index 649049d8dac3e..d3ccf27c986df 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -10,13 +10,13 @@
 //
 //===----------------------------------------------------------------------===//
 #if SANITIZER_AMDHSA
-#include <dlfcn.h>  // For dlopen, dlsym
+#  include <dlfcn.h>  // For dlopen, dlsym
 
-#include "sanitizer_allocator.h"
-#include "sanitizer_atomic.h"
+#  include "sanitizer_allocator.h"
+#  include "sanitizer_atomic.h"
 
 namespace __sanitizer {
-#include "sanitizer_allocator_amdgpu.h"
+#  include "sanitizer_allocator_amdgpu.h"
 
 struct HsaFunctions {
   // -------------- Memory Allocate/Deallocate Functions ----------------
@@ -83,7 +83,8 @@ void AmdgpuDeviceAllocator::ClearRuntimeShutdownState() {
 
 void AmdgpuDeviceAllocator::NoteDeviceAllocatorFailure(
     DeviceAllocationInfo* da_info, DeviceAllocFailure failure) {
-  if (!da_info || da_info->type_ != DAT_AMDGPU) return;
+  if (!da_info || da_info->type_ != DAT_AMDGPU)
+    return;
   AmdgpuAllocationInfo* aa_info =
       reinterpret_cast<AmdgpuAllocationInfo*>(da_info);
   switch (failure) {
@@ -103,19 +104,23 @@ void AmdgpuDeviceAllocator::NoteDeviceAllocatorFailure(
 // and not RTLD_NEXT/DEFAULT (those resolve ASan interceptors and recurse).
 bool AmdgpuDeviceAllocator::Init(bool allow_dlopen) {
   SpinMutexLock l(&hsa_runtime_init_mu);
-  if (HsaSymbolsLoaded()) return true;
+  if (HsaSymbolsLoaded())
+    return true;
 
   // Never dlopen during __asan_init.
-  if (!allow_dlopen) return false;
+  if (!allow_dlopen)
+    return false;
 
   if (!hsa_runtime_handle) {
     const char* path = GetEnv("SANITIZER_HSA_RUNTIME_PATH");
-    if (!path || !path[0]) path = kDefaultHsaRuntimePath;
+    if (!path || !path[0])
+      path = kDefaultHsaRuntimePath;
     // Prefer first `RTLD_NOLOAD` when ROCr is already mapped(after successful
     // REAL(hsa_init)). If RTLD_NOLOAD fails, try `RTLD_NOW` (when ROCr is not
     // loaded).
     hsa_runtime_handle = dlopen(path, RTLD_LAZY | RTLD_NOLOAD);
-    if (!hsa_runtime_handle) hsa_runtime_handle = dlopen(path, RTLD_NOW);
+    if (!hsa_runtime_handle)
+      hsa_runtime_handle = dlopen(path, RTLD_NOW);
     if (!hsa_runtime_handle) {
       const char* err = dlerror();
       VReport(2, "Amdgpu Init: dlopen(%s) failed: %s\n", path,
@@ -187,7 +192,8 @@ void* AmdgpuDeviceAllocator::Allocate(uptr size, uptr alignment,
     aa_info->status = hsa_amd.memory_pool_allocate(
         aa_info->memory_pool, size, aa_info->flags, &aa_info->ptr);
   }
-  if (aa_info->status != HSA_STATUS_SUCCESS) return nullptr;
+  if (aa_info->status != HSA_STATUS_SUCCESS)
+    return nullptr;
 
   return aa_info->ptr;
 }
@@ -243,7 +249,8 @@ static uptr AmdgpuPointerInfoMapBase(uptr ptr,
 
 bool AmdgpuDeviceAllocator::GetPointerInfo(uptr ptr,
                                            DevicePointerInfo* ptr_info) {
-  if (!ptr_info) return false;
+  if (!ptr_info)
+    return false;
 
   // GetPointerInfo returns false after AMDGPU runtime shutdown
   if (UNLIKELY(IsRuntimeShutdown())) {
@@ -258,13 +265,15 @@ bool AmdgpuDeviceAllocator::GetPointerInfo(uptr ptr,
   hsa_status_t status =
       hsa_amd.pointer_info(reinterpret_cast<void*>(ptr), &info, 0, 0, 0);
 
-  if (status != HSA_STATUS_SUCCESS) return false;
+  if (status != HSA_STATUS_SUCCESS)
+    return false;
 
   if (info.type == HSA_EXT_POINTER_TYPE_UNKNOWN || !info.sizeInBytes)
     return false;
 
   const uptr map_beg = AmdgpuPointerInfoMapBase(ptr, info);
-  if (!map_beg) return false;
+  if (!map_beg)
+    return false;
 
   ptr_info->map_beg = map_beg;
   ptr_info->map_size = info.sizeInBytes;
@@ -282,7 +291,8 @@ void AmdgpuDeviceAllocator::RegisterSystemEventHandlers() {
     // Callback to detect and notify AMDGPU runtime shutdown
     hsa_amd_system_event_callback_t callback = [](const hsa_amd_event_t* event,
                                                   void* data) {
-      if (!event) return HSA_STATUS_ERROR_INVALID_ARGUMENT;
+      if (!event)
+        return HSA_STATUS_ERROR_INVALID_ARGUMENT;
       if (event->event_type == HSA_AMD_SYSTEM_SHUTDOWN_EVENT)
         AmdgpuDeviceAllocator::NotifyRuntimeShutdown();
       return HSA_STATUS_SUCCESS;
@@ -315,9 +325,11 @@ VmemGpuReserveTracker& VmemGpuReserveTracker::Get() {
 }
 
 void VmemGpuReserveTracker::EnsureInited() {
-  if (atomic_load(&inited_, memory_order_acquire)) return;
+  if (atomic_load(&inited_, memory_order_acquire))
+    return;
   SpinMutexLock l(&mu_);
-  if (atomic_load(&inited_, memory_order_relaxed)) return;
+  if (atomic_load(&inited_, memory_order_relaxed))
+    return;
   reservations_.Initialize(0);
   atomic_store(&inited_, 1, memory_order_release);
 }
@@ -338,9 +350,12 @@ VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::CheckFree(uptr ptr,
   SpinMutexLock l(&mu_);
   for (uptr i = 0; i < reservations_.size(); ++i) {
     const VmemGpuReservation& r = reservations_[i];
-    if (r.ptr != ptr) continue;
-    if (r.size != size) return kSizeMismatch;
-    if (r.freed) return kDoubleFree;
+    if (r.ptr != ptr)
+      continue;
+    if (r.size != size)
+      return kSizeMismatch;
+    if (r.freed)
+      return kDoubleFree;
     return kFirstFree;
   }
   return kNotTracked;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index f5d315d4476bc..9f82753a2393a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#error This file must be included after sanitizer_allocator.h
+#  error This file must be included after sanitizer_allocator.h
 #endif
 
 #if SANITIZER_AMDHSA
@@ -86,7 +86,8 @@ struct AmdgpuAllocationInfo : public DeviceAllocationInfo {
   // If allocation fails without an HSA API status, record one so callers never
   // see *ptr == nullptr with status still SUCCESS.
   void EnsureFailureStatus(hsa_status_t err) {
-    if (status == HSA_STATUS_SUCCESS) status = err;
+    if (status == HSA_STATUS_SUCCESS)
+      status = err;
   }
 
   hsa_status_t status;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
index 51e676a87c0a3..49d1f252b8e9c 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -41,7 +41,7 @@ class CombinedAllocator {
     secondary_.Init();
   }
 
-  void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
+  void* Allocate(AllocatorCache* cache, uptr size, uptr alignment) {
     // Returning 0 on malloc(0) may break a lot of code.
     if (size == 0)
       size = 1;
@@ -64,7 +64,7 @@ class CombinedAllocator {
     // using a non-fixed base address). The secondary takes care of the
     // alignment without such requirement, and allocating 'size' would use
     // extraneous memory, so we employ 'original_size'.
-    void *res;
+    void* res;
     if (primary_.CanAllocate(size, alignment))
       res = cache->Allocate(&primary_, primary_.ClassID(size));
     else
@@ -130,8 +130,10 @@ class CombinedAllocator {
   // (via ForceLock). Uses GetBlockBeginFastLocked for the secondary check to
   // avoid re-acquiring the mutex already held by the caller.
   void* GetMetaDataFastLocked(const void* p) {
-    if (primary_.PointerIsMine(p)) return primary_.GetMetaData(p);
-    if (secondary_.GetBlockBeginFastLocked(p)) return secondary_.GetMetaData(p);
+    if (primary_.PointerIsMine(p))
+      return primary_.GetMetaData(p);
+    if (secondary_.GetBlockBeginFastLocked(p))
+      return secondary_.GetMetaData(p);
     return nullptr;
   }
 
@@ -143,7 +145,7 @@ class CombinedAllocator {
 
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
-  void *GetBlockBeginFastLocked(const void *p) {
+  void* GetBlockBeginFastLocked(const void* p) {
     if (primary_.PointerIsMine(p))
       return primary_.GetBlockBegin(p);
     return secondary_.GetBlockBeginFastLocked(p);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
index 5dd12bd12648f..b1f1035381f78 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined_device.h
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#error This file must be included inside sanitizer_allocator.h
+#  error This file must be included inside sanitizer_allocator.h
 #endif
 
 // DeviceCombinedAllocator adds an optional device-memory heap tier on top of an
@@ -49,11 +49,13 @@ class DeviceCombinedAllocator {
 
   void* Allocate(AllocatorCache* cache, uptr size, uptr alignment,
                  DeviceAllocationInfo* da_info = nullptr) {
-    if (!da_info) return base_.Allocate(cache, size, alignment);
+    if (!da_info)
+      return base_.Allocate(cache, size, alignment);
     // Device tier: mirror CombinedAllocator's malloc(0)/overflow guards. The
     // device allocator handles its own alignment/page rounding, so 'size' is
     // forwarded unrounded (CombinedAllocator's original_size semantics).
-    if (size == 0) size = 1;
+    if (size == 0)
+      size = 1;
     if (size + alignment < size) {
       Report(
           "WARNING: %s: DeviceCombinedAllocator allocation overflow: "
@@ -76,7 +78,8 @@ class DeviceCombinedAllocator {
   void ForceReleaseToOS() { base_.ForceReleaseToOS(); }
 
   void Deallocate(AllocatorCache* cache, void* p) {
-    if (!p) return;
+    if (!p)
+      return;
     if (device_.PointerIsMine(p))
       device_.Deallocate(&device_stats_, p);
     else
@@ -85,7 +88,8 @@ class DeviceCombinedAllocator {
 
   void* Reallocate(AllocatorCache* cache, void* p, uptr new_size,
                    uptr alignment) {
-    if (!p) return Allocate(cache, new_size, alignment);
+    if (!p)
+      return Allocate(cache, new_size, alignment);
     if (!new_size) {
       Deallocate(cache, p);
       return nullptr;
@@ -94,7 +98,8 @@ class DeviceCombinedAllocator {
     uptr old_size = GetActuallyAllocatedSize(p);
     uptr memcpy_size = Min(new_size, old_size);
     void* new_p = Allocate(cache, new_size, alignment);
-    if (new_p) internal_memcpy(new_p, p, memcpy_size);
+    if (new_p)
+      internal_memcpy(new_p, p, memcpy_size);
     Deallocate(cache, p);
     return new_p;
   }
@@ -106,7 +111,8 @@ class DeviceCombinedAllocator {
   bool FromPrimary(const void* p) const { return base_.FromPrimary(p); }
 
   void* GetMetaData(const void* p) {
-    if (device_.PointerIsMine(p)) return device_.GetMetaData(p);
+    if (device_.PointerIsMine(p))
+      return device_.GetMetaData(p);
     return base_.GetMetaData(p);
   }
 
@@ -114,24 +120,28 @@ class DeviceCombinedAllocator {
   // (via ForceLock). Uses GetBlockBeginFastLocked for the device check to avoid
   // re-acquiring a mutex already held by the caller.
   void* GetMetaDataFastLocked(const void* p) {
-    if (device_.GetBlockBeginFastLocked(p)) return device_.GetMetaData(p);
+    if (device_.GetBlockBeginFastLocked(p))
+      return device_.GetMetaData(p);
     return base_.GetMetaDataFastLocked(p);
   }
 
   void* GetBlockBegin(const void* p) {
-    if (void* beg = base_.GetBlockBegin(p)) return beg;
+    if (void* beg = base_.GetBlockBegin(p))
+      return beg;
     return device_.GetBlockBegin(p);
   }
 
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
   void* GetBlockBeginFastLocked(const void* p) {
-    if (void* beg = base_.GetBlockBeginFastLocked(p)) return beg;
+    if (void* beg = base_.GetBlockBeginFastLocked(p))
+      return beg;
     return device_.GetBlockBeginFastLocked(p);
   }
 
   uptr GetActuallyAllocatedSize(void* p) {
-    if (device_.PointerIsMine(p)) return device_.GetActuallyAllocatedSize(p);
+    if (device_.PointerIsMine(p))
+      return device_.GetActuallyAllocatedSize(p);
     return base_.GetActuallyAllocatedSize(p);
   }
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index d1141434e7a42..cfcaa64c81564 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 #ifndef SANITIZER_ALLOCATOR_H
-#error This file must be included inside sanitizer_allocator.h
+#  error This file must be included inside sanitizer_allocator.h
 #endif
 
 // DeviceAllocatorT: device-mapped heap bookkeeping. Map/unmap uses the
@@ -83,7 +83,8 @@ class DeviceAllocatorT {
   // callers (and any wrapper) need not thread it in.
   void Init() {
     internal_memset(this, 0, sizeof(*this));
-    if (!DeviceBackend::kEnableDeviceBackend) return;
+    if (!DeviceBackend::kEnableDeviceBackend)
+      return;
     enabled_ = true;
     kMetadataSize_ = PrimaryAllocator::kMetadataSize;
     chunks_ = reinterpret_cast<uptr*>(ptr_array_.Init());
@@ -105,7 +106,8 @@ class DeviceAllocatorT {
     }
     CHECK(IsPowerOfTwo(alignment));
     uptr map_size = RoundUpMapSize(size);
-    if (alignment > page_size_) map_size += alignment;
+    if (alignment > page_size_)
+      map_size += alignment;
     // Overflow.
     if (map_size < size) {
       Report(
@@ -159,7 +161,8 @@ class DeviceAllocatorT {
       uptr p_ = reinterpret_cast<uptr>(p);
       EnsureSortedChunks();  // Avoid doing the sort while iterating.
       for (idx = 0; idx < n_chunks_; idx++) {
-        if (chunks_[idx] >= p_) break;
+        if (chunks_[idx] >= p_)
+          break;
       }
       CHECK_EQ(chunks_[idx], p_);
       CHECK_LT(idx, n_chunks_);
@@ -208,17 +211,21 @@ class DeviceAllocatorT {
 
   void* GetBlockBegin(const void* ptr) const {
     Header header;
-    if (!mem_funcs_inited_) return nullptr;
+    if (!mem_funcs_inited_)
+      return nullptr;
     uptr p = reinterpret_cast<uptr>(ptr);
     SpinMutexLock l(&mutex_);
     uptr nearest_chunk = 0;
     // Cache-friendly linear search.
     for (uptr i = 0; i < n_chunks_; i++) {
       uptr ch = chunks_[i];
-      if (p < ch) continue;  // p is at left to this chunk, skip it.
-      if (p - ch < p - nearest_chunk) nearest_chunk = ch;
+      if (p < ch)
+        continue;  // p is at left to this chunk, skip it.
+      if (p - ch < p - nearest_chunk)
+        nearest_chunk = ch;
     }
-    if (!nearest_chunk) return nullptr;
+    if (!nearest_chunk)
+      return nullptr;
     if (p != nearest_chunk) {
       Header* h = GetHeader(nearest_chunk, &header);
       CHECK_GE(nearest_chunk, h->map_beg);
@@ -232,7 +239,8 @@ class DeviceAllocatorT {
   }
 
   void EnsureSortedChunks() {
-    if (chunks_sorted_) return;
+    if (chunks_sorted_)
+      return;
     Sort(reinterpret_cast<uptr*>(chunks_), n_chunks_);
     chunks_sorted_ = true;
   }
@@ -240,17 +248,20 @@ class DeviceAllocatorT {
   // This function does the same as GetBlockBegin, but is much faster.
   // Must be called with the allocator locked.
   void* GetBlockBeginFastLocked(const void* ptr) {
-    if (!mem_funcs_inited_) return nullptr;
+    if (!mem_funcs_inited_)
+      return nullptr;
     mutex_.CheckLocked();
     uptr p = reinterpret_cast<uptr>(ptr);
     uptr n = n_chunks_;
-    if (!n) return nullptr;
+    if (!n)
+      return nullptr;
     EnsureSortedChunks();
     Header header, *h;
     h = GetHeader(chunks_[n - 1], &header);
     uptr min_mmap_ = chunks_[0];
     uptr max_mmap_ = chunks_[n - 1] + h->map_size;
-    if (p < min_mmap_) return nullptr;
+    if (p < min_mmap_)
+      return nullptr;
     if (p >= max_mmap_) {
       // TODO (bingma): If dev_runtime_unloaded_ = true, map_size is limited
       // to one page and we might miss a valid 'ptr'. If we hit cases where
@@ -272,13 +283,15 @@ class DeviceAllocatorT {
     if (beg < end) {
       CHECK_EQ(beg + 1, end);
       // There are 2 chunks left, choose one.
-      if (p >= chunks_[end]) beg = end;
+      if (p >= chunks_[end])
+        beg = end;
     }
 
     if (p != chunks_[beg]) {
       h = GetHeader(chunks_[beg], &header);
       CHECK_NE(h, nullptr);
-      if (p < h->map_beg) return nullptr;
+      if (p < h->map_beg)
+        return nullptr;
       if (h->map_beg + h->map_size <= p) {
         // TODO (bingma): See above TODO in this function
         return nullptr;
@@ -295,7 +308,8 @@ class DeviceAllocatorT {
         stats.currently_allocated >> 10, stats.max_allocated >> 20);
     for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) {
       uptr c = stats.by_size_log[i];
-      if (!c) continue;
+      if (!c)
+        continue;
       Printf("%zd:%zd; ", i, c);
     }
     Printf("\n");
@@ -326,7 +340,8 @@ class DeviceAllocatorT {
     }
     mem_funcs_inited_ = DeviceBackend::Init();
     mem_funcs_init_count_++;
-    if (mem_funcs_inited_) page_size_ = DeviceBackend::GetPageSize();
+    if (mem_funcs_inited_)
+      page_size_ = DeviceBackend::GetPageSize();
     return mem_funcs_inited_;
   }
 
@@ -342,7 +357,8 @@ class DeviceAllocatorT {
     // is unloaded, GetPointerInfo() will fail. For such case, we can still
     // return a valid value for map_beg, map_size will be limited to one page
     if (LIKELY(!dev_runtime_unloaded_)) {
-      if (DeviceBackend::GetPointerInfo(chunk, h)) return h;
+      if (DeviceBackend::GetPointerInfo(chunk, h))
+        return h;
       // If GetPointerInfo() fails, we don't assume the runtime is unloaded yet.
       // We just return a conservative single-page header. Here mark/check the
       // runtime shutdown state
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
index 74b36ca7e8e9e..2fb689916d62d 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
@@ -322,7 +322,7 @@
 #endif
 
 #ifndef SANITIZER_AMDHSA
-#define SANITIZER_AMDHSA 0
+#  define SANITIZER_AMDHSA 0
 #endif
 
 #if defined(__NVPTX__)
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
index 164544332b233..f39b86e6fe333 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // After hsa_amd_ipc_memory_attach, AddressSanitizer poisons a trailing redzone
@@ -15,20 +15,23 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-#include <stdio.h>
 
-#include "hsa_amd_test_helpers.h"
+#include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init()) return 1;
+  if (hsa_amd_test_require_init())
+    return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps)) return 1;
+  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
+    return 1;
 
   constexpr size_t kBytes = 64;
-  void* mem = nullptr;
+  void *mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -43,7 +46,7 @@ int main() {
     return 1;
   }
 
-  void* mapped = nullptr;
+  void *mapped = nullptr;
   if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
                                 /*mapping_agents=*/nullptr,
                                 &mapped) != HSA_STATUS_SUCCESS ||
@@ -58,15 +61,13 @@ int main() {
   (void)hsa_iterate_agents(hsa_amd_test_pick_first_cpu_agent_cb, &cpu);
   if (cpu.agent.handle != 0) {
     /* Best-effort: allow the host CPU to access the imported range so the store
-       below is a normal fault checked by ASan, not an unmapped-device SIGSEGV.
-     */
+       below is a normal fault checked by ASan, not an unmapped-device SIGSEGV. */
     (void)hsa_amd_agents_allow_access(/*num_agents=*/1, &cpu.agent,
                                       /*flags=*/nullptr, mapped);
   }
 
-  auto* p = reinterpret_cast<volatile char*>(mapped);
-  // One byte past the 64-byte imported region; should land in ASan's tail
-  // redzone.
+  auto *p = reinterpret_cast<volatile char *>(mapped);
+  // One byte past the 64-byte imported region; should land in ASan's tail redzone.
   p[kBytes] = 1;
 
   fprintf(stderr, "expected heap-buffer-overflow after ipc attach\n");
@@ -77,5 +78,4 @@ int main() {
 
 // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
 // CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
-// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow
-// {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:71:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:71:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
index d4ebace6a9fa8..f84b1bbdde2a2 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
@@ -1,12 +1,12 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: %run %t 2>&1 | FileCheck %s
 //
-// Regression test for AddressSanitizer
-// hsa_amd_ipc_memory_{create,attach,detach}: same-process IPC round-trip on a
-// pool allocation (unwrap on create, user pointer adjustment and shadow on
-// attach, base adjustment on detach). hsa_amd_ipc_memory_create only supports
-// coarse-grained GPU allocations; skip fine-grained pools and non-GPU agents.
+// Regression test for AddressSanitizer hsa_amd_ipc_memory_{create,attach,detach}:
+// same-process IPC round-trip on a pool allocation (unwrap on create, user pointer
+// adjustment and shadow on attach, base adjustment on detach).
+// hsa_amd_ipc_memory_create only supports coarse-grained GPU allocations; skip
+// fine-grained pools and non-GPU agents.
 //
 // Coarse-grained device memory is often not mapped for CPU stores; do not
 // read/write *mapped from the host. Validate with hsa_amd_pointer_info instead.
@@ -14,20 +14,23 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-#include <stdio.h>
 
-#include "hsa_amd_test_helpers.h"
+#include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init()) return 1;
+  if (hsa_amd_test_require_init())
+    return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps)) return 1;
+  if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
+    return 1;
 
   constexpr size_t kBytes = 64;
-  void* mem = nullptr;
+  void *mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -42,7 +45,7 @@ int main() {
     return 1;
   }
 
-  void* mapped = nullptr;
+  void *mapped = nullptr;
   if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
                                 /*mapping_agents=*/nullptr,
                                 &mapped) != HSA_STATUS_SUCCESS ||
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
index 2b24e29e6c38d..c773b55b8e866 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
@@ -1,28 +1,30 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
-// Regression test for the AddressSanitizer hsa_amd_memory_async_copy
-// interceptor: invalid overlapping ranges are diagnosed (same family of checks
-// as memcpy).
+// Regression test for the AddressSanitizer hsa_amd_memory_async_copy interceptor:
+// invalid overlapping ranges are diagnosed (same family of checks as memcpy).
 //
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "hsa_amd_test_helpers.h"
-
 int main() {
-  if (hsa_amd_test_require_init()) return 1;
+  if (hsa_amd_test_require_init())
+    return 1;
 
   HsaAmdAgentPick pick;
   hsa_amd_test_agent_pick_init(&pick);
   hsa_status_t it = hsa_iterate_agents(hsa_amd_test_pick_first_agent_cb, &pick);
-  if (hsa_amd_test_iterate_agents_ok(it)) return 1;
+  if (hsa_amd_test_iterate_agents_ok(it))
+    return 1;
   if (pick.agent.handle == 0) {
     fprintf(stderr, "no HSA agent found\n");
     return 1;
@@ -37,8 +39,8 @@ int main() {
   }
 
   char buf[128];
-  char* dst = buf;
-  char* src = buf + 40;
+  char *dst = buf;
+  char *src = buf + 40;
   // Ranges [buf, buf+64) and [buf+40, buf+104) overlap; dst != src so the
   // interceptor runs CHECK_RANGES_OVERLAP before scheduling the async copy.
   (void)hsa_amd_memory_async_copy(dst, pick.agent, src, pick.agent, 64,
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index b7483fc851f58..82b21962b0948 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_amd_memory_pool_allocate /
@@ -9,19 +9,22 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-#include <stdio.h>
 
-#include "hsa_amd_test_helpers.h"
+#include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init()) return 1;
+  if (hsa_amd_test_require_init())
+    return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps)) return 1;
+  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
+    return 1;
 
-  void* mem = nullptr;
+  void *mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -37,5 +40,4 @@ int main() {
 }
 
 // CHECK: ERROR: AddressSanitizer: attempting double-free
-// CHECK: SUMMARY: AddressSanitizer: double-free
-// {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:36:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: double-free {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:36:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
index 006353941e8e7..c6c54373210e5 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_amd_pointer_info interceptor on
@@ -9,19 +9,22 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-#include <stdio.h>
 
-#include "hsa_amd_test_helpers.h"
+#include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init()) return 1;
+  if (hsa_amd_test_require_init())
+    return 1;
 
   HsaAmdPoolSearch ps;
-  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps)) return 1;
+  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
+    return 1;
 
-  void* mem = nullptr;
+  void *mem = nullptr;
   if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
           HSA_STATUS_SUCCESS ||
       !mem) {
@@ -42,7 +45,7 @@ int main() {
   printf("pointer_info_pool sizeInBytes: %zu\n", info.sizeInBytes);
   printf("pointer_info_pool begin: %p\n", info.agentBaseAddress);
   printf("pointer_info_pool end: %p\n",
-         (void*)((uintptr_t)info.agentBaseAddress + info.sizeInBytes));
+         (void *)((uintptr_t)info.agentBaseAddress + info.sizeInBytes));
 
   if (hsa_amd_memory_pool_free(mem) != HSA_STATUS_SUCCESS) {
     fprintf(stderr, "hsa_amd_memory_pool_free failed\n");
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
index ffb406d4558c7..dceb8eca54d41 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: %run %t 2>&1 | FileCheck %s
 //
 // hsa_amd_pointer_info on vmem reserved via reserve_align() must report the
@@ -9,22 +9,23 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
+
 #include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>
 
-#include "hsa_amd_test_helpers.h"
-
 int main() {
-  if (hsa_amd_test_require_init()) return 1;
+  if (hsa_amd_test_require_init())
+    return 1;
 
-  const size_t kSize = 4096;  // 4KiB size
-  // Here alignment is larger than a page size, so the reserved address will be
-  // aligned to 64KiB.
-  const uint64_t kAlign = 65536;  // 64KiB alignment
-  void* mem = nullptr;
+  const size_t kSize = 4096; //4KiB size
+  // Here alignment is larger than a page size, so the reserved address will be aligned to 64KiB.
+  const uint64_t kAlign = 65536; //64KiB alignment
+  void *mem = nullptr;
   // Host-accessible vmem reserve via NO_REGISTER (host mmap, not KFD VA).
   if (hsa_amd_vmem_address_reserve_align(
           &mem, kSize, /*address=*/0, kAlign,
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
index 1ae83d4efe28d..53b7f571fcbcf 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
@@ -1,5 +1,4 @@
-//===-- hsa_amd_test_helpers.h - shared helpers for AMDGPU ASan HSA tests
-//--===//
+//===-- hsa_amd_test_helpers.h - shared helpers for AMDGPU ASan HSA tests --===//
 //
 // Common ROCm/HSA discovery and init helpers for hsa_amd*.cpp tests in this
 // directory. Intended only for compiler-rt lit tests.
@@ -11,6 +10,7 @@
 
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
+
 #include <stdio.h>
 
 inline int hsa_amd_test_require_init() {
@@ -35,9 +35,10 @@ struct HsaAmdPoolSearch {
   bool found;
 };
 
-inline hsa_status_t hsa_amd_test_find_runtime_alloc_pool_cb(
-    hsa_amd_memory_pool_t pool, void* data) {
-  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
+inline hsa_status_t
+hsa_amd_test_find_runtime_alloc_pool_cb(hsa_amd_memory_pool_t pool,
+                                        void *data) {
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
   bool allow = false;
   if (hsa_amd_memory_pool_get_info(
           pool, HSA_AMD_MEMORY_POOL_INFO_RUNTIME_ALLOC_ALLOWED, &allow) !=
@@ -49,24 +50,28 @@ inline hsa_status_t hsa_amd_test_find_runtime_alloc_pool_cb(
   return HSA_STATUS_INFO_BREAK;
 }
 
-inline hsa_status_t hsa_amd_test_find_agent_with_runtime_alloc_pool_cb(
-    hsa_agent_t agent, void* data) {
+inline hsa_status_t
+hsa_amd_test_find_agent_with_runtime_alloc_pool_cb(hsa_agent_t agent,
+                                                   void *data) {
   (void)agent;
-  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
   ps->found = false;
   hsa_status_t st = hsa_amd_agent_iterate_memory_pools(
       agent, hsa_amd_test_find_runtime_alloc_pool_cb, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK) return st;
-  if (ps->found) return HSA_STATUS_INFO_BREAK;
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
   return HSA_STATUS_SUCCESS;
 }
 
-inline int hsa_amd_test_find_first_runtime_alloc_pool(HsaAmdPoolSearch* ps) {
+inline int hsa_amd_test_find_first_runtime_alloc_pool(HsaAmdPoolSearch *ps) {
   ps->pool.handle = 0;
   ps->found = false;
   hsa_status_t it = hsa_iterate_agents(
       hsa_amd_test_find_agent_with_runtime_alloc_pool_cb, ps);
-  if (hsa_amd_test_iterate_agents_ok(it)) return 1;
+  if (hsa_amd_test_iterate_agents_ok(it))
+    return 1;
   if (!ps->found) {
     fprintf(stderr, "no runtime-alloc HSA memory pool found\n");
     return 1;
@@ -74,15 +79,17 @@ inline int hsa_amd_test_find_first_runtime_alloc_pool(HsaAmdPoolSearch* ps) {
   return 0;
 }
 
-inline hsa_status_t hsa_amd_test_find_coarse_gpu_ipc_pool_cb(
-    hsa_amd_memory_pool_t pool, void* data) {
-  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
+inline hsa_status_t
+hsa_amd_test_find_coarse_gpu_ipc_pool_cb(hsa_amd_memory_pool_t pool,
+                                         void *data) {
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
 
   hsa_amd_segment_t segment = HSA_AMD_SEGMENT_PRIVATE;
   if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT,
                                    &segment) != HSA_STATUS_SUCCESS)
     return HSA_STATUS_SUCCESS;
-  if (segment != HSA_AMD_SEGMENT_GLOBAL) return HSA_STATUS_SUCCESS;
+  if (segment != HSA_AMD_SEGMENT_GLOBAL)
+    return HSA_STATUS_SUCCESS;
 
   uint32_t global_flags = 0;
   if (hsa_amd_memory_pool_get_info(pool, HSA_AMD_MEMORY_POOL_INFO_GLOBAL_FLAGS,
@@ -103,29 +110,33 @@ inline hsa_status_t hsa_amd_test_find_coarse_gpu_ipc_pool_cb(
   return HSA_STATUS_INFO_BREAK;
 }
 
-inline hsa_status_t hsa_amd_test_find_gpu_agent_with_ipc_pool_cb(
-    hsa_agent_t agent, void* data) {
+inline hsa_status_t
+hsa_amd_test_find_gpu_agent_with_ipc_pool_cb(hsa_agent_t agent, void *data) {
   hsa_device_type_t dev = HSA_DEVICE_TYPE_CPU;
   if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
       HSA_STATUS_SUCCESS)
     return HSA_STATUS_SUCCESS;
-  if (dev != HSA_DEVICE_TYPE_GPU) return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_GPU)
+    return HSA_STATUS_SUCCESS;
 
-  auto* ps = static_cast<HsaAmdPoolSearch*>(data);
+  auto *ps = static_cast<HsaAmdPoolSearch *>(data);
   ps->found = false;
   hsa_status_t st = hsa_amd_agent_iterate_memory_pools(
       agent, hsa_amd_test_find_coarse_gpu_ipc_pool_cb, ps);
-  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK) return st;
-  if (ps->found) return HSA_STATUS_INFO_BREAK;
+  if (st != HSA_STATUS_SUCCESS && st != HSA_STATUS_INFO_BREAK)
+    return st;
+  if (ps->found)
+    return HSA_STATUS_INFO_BREAK;
   return HSA_STATUS_SUCCESS;
 }
 
-inline int hsa_amd_test_find_first_coarse_gpu_ipc_pool(HsaAmdPoolSearch* ps) {
+inline int hsa_amd_test_find_first_coarse_gpu_ipc_pool(HsaAmdPoolSearch *ps) {
   ps->pool.handle = 0;
   ps->found = false;
   hsa_status_t it =
       hsa_iterate_agents(hsa_amd_test_find_gpu_agent_with_ipc_pool_cb, ps);
-  if (hsa_amd_test_iterate_agents_ok(it)) return 1;
+  if (hsa_amd_test_iterate_agents_ok(it))
+    return 1;
   if (!ps->found) {
     fprintf(stderr,
             "no coarse-grained GPU runtime-alloc HSA memory pool found\n");
@@ -138,18 +149,19 @@ struct HsaAmdCpuAgentPick {
   hsa_agent_t agent;
 };
 
-inline void hsa_amd_test_cpu_agent_pick_init(HsaAmdCpuAgentPick* out) {
+inline void hsa_amd_test_cpu_agent_pick_init(HsaAmdCpuAgentPick *out) {
   out->agent.handle = 0;
 }
 
 inline hsa_status_t hsa_amd_test_pick_first_cpu_agent_cb(hsa_agent_t agent,
-                                                         void* data) {
-  auto* out = static_cast<HsaAmdCpuAgentPick*>(data);
+                                                         void *data) {
+  auto *out = static_cast<HsaAmdCpuAgentPick *>(data);
   hsa_device_type_t dev = HSA_DEVICE_TYPE_GPU;
   if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &dev) !=
       HSA_STATUS_SUCCESS)
     return HSA_STATUS_SUCCESS;
-  if (dev != HSA_DEVICE_TYPE_CPU) return HSA_STATUS_SUCCESS;
+  if (dev != HSA_DEVICE_TYPE_CPU)
+    return HSA_STATUS_SUCCESS;
   out->agent = agent;
   return HSA_STATUS_INFO_BREAK;
 }
@@ -158,15 +170,15 @@ struct HsaAmdAgentPick {
   hsa_agent_t agent;
 };
 
-inline void hsa_amd_test_agent_pick_init(HsaAmdAgentPick* out) {
+inline void hsa_amd_test_agent_pick_init(HsaAmdAgentPick *out) {
   out->agent.handle = 0;
 }
 
 inline hsa_status_t hsa_amd_test_pick_first_agent_cb(hsa_agent_t agent,
-                                                     void* data) {
-  auto* out = static_cast<HsaAmdAgentPick*>(data);
+                                                     void *data) {
+  auto *out = static_cast<HsaAmdAgentPick *>(data);
   out->agent = agent;
   return HSA_STATUS_INFO_BREAK;
 }
 
-#endif  // COMPILER_RT_ASAN_AMDGPU_HSA_AMD_TEST_HELPERS_H
+#endif // COMPILER_RT_ASAN_AMDGPU_HSA_AMD_TEST_HELPERS_H
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index d2476784bf267..1828e6e930bbd 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
@@ -9,20 +9,22 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
-#include <stdio.h>
 
-#include "hsa_amd_test_helpers.h"
+#include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init()) return 1;
+  if (hsa_amd_test_require_init())
+    return 1;
 
   // Size must be a non-zero multiple of the page size; address must be 0 so
   // the interceptor records the reservation for double-free diagnosis (see
   // asan_hsa_amd_vmem_address_reserve_align).
   const size_t kSize = 4096;
-  void* mem = nullptr;
+  void *mem = nullptr;
 
   // NOTE: To use `hipMallocManaged` way of reserving memory,
   // use either `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in flags.
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
index a74370e802f0d..51f90366d40b2 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
@@ -1,5 +1,5 @@
-// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib
-// -lhsa-runtime64 \ RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_memory_copy interceptor: invalid
@@ -9,6 +9,7 @@
 // UNSUPPORTED: android
 
 #include <hsa/hsa.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -19,8 +20,8 @@ int main() {
   }
 
   char buf[128];
-  char* dst = buf;
-  char* src = buf + 40;
+  char *dst = buf;
+  char *src = buf + 40;
   // Ranges [buf, buf+64) and [buf+40, buf+104) overlap; dst != src so the
   // interceptor runs CHECK_RANGES_OVERLAP.
   (void)hsa_memory_copy(dst, src, 64);

>From 10d281217174d38813f7482d998437136d0fa8b9 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 23 Jun 2026 12:19:07 +0530
Subject: [PATCH 39/46] [compiler-rt][asan] Separate device allocation entry
 point

Drop the in-body #if SANITIZER_AMDHSA from the allocation path: make
AllocateImpl take a block-allocator functor and add an AMDHSA-only
AllocateDevice() so DeviceAllocationInfo no longer threads through the
shared host allocate path.
---
 compiler-rt/lib/asan/asan_allocator.cpp | 50 ++++++++++++++-----------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index dee06b646e4c6..fb228037db28b 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -537,26 +537,16 @@ struct Allocator {
     return true;
   }
 
-  // Route the raw block allocation to the underlying allocator. The device
-  // heap tier (da_info) only exists for SANITIZER_AMDHSA, where `allocator` is
-  // a DeviceCombinedAllocator; other builds use a plain CombinedAllocator whose
-  // Allocate() has no device parameter.
-  void* AllocateBlock(AllocatorCache* cache, uptr needed_size,
-                      DeviceAllocationInfo* da_info) {
-#if SANITIZER_AMDHSA
-    return allocator.Allocate(cache, needed_size, 8, da_info);
-#else
-    (void)da_info;
-    return allocator.Allocate(cache, needed_size, 8);
-#endif
-  }
-
   // -------------------- Allocation/Deallocation routines ---------------
+  // Shared allocation core. `block_alloc(cache, needed_size)` supplies the raw
+  // backing block and is the only step that differs between the host heap and
+  // the AMDGPU device heap, so no device knowledge leaks into this routine.
   // may_return_null tells AllocateImpl() whether OOM should produce a nullptr
   // (true) or a fatal Report*+Die() (false).
+  template <typename BlockAllocFn>
   void* AllocateImpl(uptr size, uptr alignment, BufferedStackTrace* stack,
                      AllocType alloc_type, bool can_fill, bool may_return_null,
-                     DeviceAllocationInfo* da_info) {
+                     BlockAllocFn block_alloc) {
     if (UNLIKELY(!AsanInited()))
       AsanInitFromRtl();
     if (UNLIKELY(IsRssLimitExceeded())) {
@@ -611,11 +601,11 @@ struct Allocator {
     void* allocated;
     if (t) {
       AllocatorCache* cache = GetAllocatorCache(&t->malloc_storage());
-      allocated = AllocateBlock(cache, needed_size, da_info);
+      allocated = block_alloc(cache, needed_size);
     } else {
       SpinMutexLock l(&fallback_mutex);
       AllocatorCache* cache = &fallback_allocator_cache;
-      allocated = AllocateBlock(cache, needed_size, da_info);
+      allocated = block_alloc(cache, needed_size);
     }
     if (UNLIKELY(!allocated)) {
       SetAllocatorOutOfMemory();
@@ -698,14 +688,29 @@ struct Allocator {
     return res;
   }
 
-  // Defer to the global, flag controlled, OOM policy.
+  // Host heap entry point. Defers to the global, flag controlled, OOM policy.
   void* Allocate(uptr size, uptr alignment, BufferedStackTrace* stack,
-                 AllocType alloc_type, bool can_fill,
-                 DeviceAllocationInfo* da_info = nullptr) {
+                 AllocType alloc_type, bool can_fill) {
     return AllocateImpl(size, alignment, stack, alloc_type, can_fill,
-                        AllocatorMayReturnNull(), da_info);
+                        AllocatorMayReturnNull(),
+                        [this](AllocatorCache* cache, uptr needed_size) {
+                          return allocator.Allocate(cache, needed_size, 8);
+                        });
   }
 
+#if SANITIZER_AMDHSA
+  // Device-heap entry point; keeps DeviceAllocationInfo off the host path.
+  void* AllocateDevice(uptr size, uptr alignment, BufferedStackTrace* stack,
+                       AllocType alloc_type, bool can_fill,
+                       DeviceAllocationInfo* da_info) {
+    return AllocateImpl(
+        size, alignment, stack, alloc_type, can_fill, AllocatorMayReturnNull(),
+        [this, da_info](AllocatorCache* cache, uptr needed_size) {
+          return allocator.Allocate(cache, needed_size, 8, da_info);
+        });
+  }
+#endif
+
   // Set quarantine flag if chunk is allocated, issue ASan error report on
   // available and quarantined chunks. Return true on success, false otherwise.
   bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk* m, void* ptr,
@@ -1499,7 +1504,8 @@ namespace __asan {
 
 void* AsanHsaAllocate(uptr size, uptr alignment, BufferedStackTrace* stack,
                       DeviceAllocationInfo* da_info) {
-  return instance.Allocate(size, alignment, stack, FROM_MALLOC, false, da_info);
+  return instance.AllocateDevice(size, alignment, stack, FROM_MALLOC,
+                                 /*can_fill=*/false, da_info);
 }
 
 void AsanHsaDeallocate(void* ptr, BufferedStackTrace* stack) {

>From 9680b7752bdaadcea55a6e7427028f42f941001c Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 23 Jun 2026 13:58:07 +0530
Subject: [PATCH 40/46] [compiler-rt][asan] Add `use-after-free` test cases for
 memory_pool and vmem api's.

---
 ...a_amd_memory_pool_allocate_double_free.cpp |  2 +-
 .../hsa_amd_memory_pool_allocate_uaf.cpp      | 45 +++++++++++++++++
 ...vmem_address_reserve_align_double_free.cpp |  5 +-
 ...hsa_amd_vmem_address_reserve_align_uaf.cpp | 49 +++++++++++++++++++
 4 files changed, 97 insertions(+), 4 deletions(-)
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index 82b21962b0948..86748105fed09 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -4,7 +4,7 @@
 //
 // Regression test for the AddressSanitizer hsa_amd_memory_pool_allocate /
 // hsa_amd_memory_pool_free interceptors: freeing the same pool allocation
-// twice is diagnosed (same family of checks as double-free on malloc).
+// twice is diagnosed as double-free.
 //
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
new file mode 100644
index 0000000000000..3be0a5e9041b8
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
@@ -0,0 +1,45 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// Regression test for the AddressSanitizer hsa_amd_memory_pool_allocate /
+// hsa_amd_memory_pool_free interceptors: Using the same freed pool allocation twice is diagnosed as use-after-free.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include "hsa_amd_test_helpers.h"
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+int main() {
+  if (hsa_amd_test_require_init())
+    return 1;
+
+  HsaAmdPoolSearch ps;
+  if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
+    return 1;
+
+  constexpr size_t kBytes = 64;
+  void *mem = nullptr;
+  if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
+          HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
+    return 1;
+  }
+
+  (void)hsa_amd_memory_pool_free(mem);
+  auto *p = reinterpret_cast<volatile char *>(mem);
+  p[0] = 1; // Use-after-free
+
+  fprintf(stderr, "expected use-after-free report\n");
+  return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer: heap-use-after-free
+// CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
+// CHECK: SUMMARY: AddressSanitizer: heap-use-after-free {{.*}}hsa_amd_memory_pool_allocate_uaf.cpp:37:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index 1828e6e930bbd..83534414fc7d0 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -3,8 +3,7 @@
 // RUN: not %run %t 2>&1 | FileCheck %s
 //
 // Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
-// hsa_amd_vmem_address_free interceptors: freeing the same reserved range twice
-// is diagnosed.
+// hsa_amd_vmem_address_free interceptors: Using the same freed reserved range twice is diagnosed as double-free.
 //
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
@@ -27,7 +26,7 @@ int main() {
   void *mem = nullptr;
 
   // NOTE: To use `hipMallocManaged` way of reserving memory,
-  // use either `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in flags.
+  // use `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in `flags`.
   if (hsa_amd_vmem_address_reserve_align(
           &mem, kSize, /*address=*/0,
           /*alignment=*/4096,
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
new file mode 100644
index 0000000000000..c156c6df39d79
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
@@ -0,0 +1,49 @@
+// RUN: %clangxx_asan -O0 -isystem %rocm_include %s -o %t -L%rocm_lib -lhsa-runtime64 \
+// RUN:   -Wl,-rpath,%rocm_lib -Wl,-rpath,%compiler_rt_libdir
+// RUN: not %run %t 2>&1 | FileCheck %s
+//
+// Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
+// hsa_amd_vmem_address_free interceptors: Using the same freed reserved range is diagnosed as use-after-free.
+//
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// UNSUPPORTED: android
+
+#include "hsa_amd_test_helpers.h"
+
+#include <hsa/hsa.h>
+#include <hsa/hsa_ext_amd.h>
+
+#include <stdio.h>
+
+int main() {
+  if (hsa_amd_test_require_init())
+    return 1;
+
+  // Size must be a non-zero multiple of the page size; address must be 0 so
+  // the interceptor records the reservation for double-free diagnosis (see
+  // asan_hsa_amd_vmem_address_reserve_align).
+  const size_t kSize = 4096;
+  void *mem = nullptr;
+
+  // NOTE: To use `hipMallocManaged` way of reserving memory,
+  // use `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in `flags`.
+  if (hsa_amd_vmem_address_reserve_align(
+          &mem, kSize, /*address=*/0,
+          /*alignment=*/4096,
+          /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER) != HSA_STATUS_SUCCESS ||
+      !mem) {
+    fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
+    return 1;
+  }
+
+  (void)hsa_amd_vmem_address_free(mem, kSize);
+  auto *p = reinterpret_cast<volatile char *>(mem);
+  p[0] = 1; // Use-after-free
+
+  fprintf(stderr, "expected double-free report\n");
+  return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer: heap-use-after-free
+// CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
+// CHECK: SUMMARY: AddressSanitizer: heap-use-after-free {{.*}}hsa_amd_vmem_address_reserve_align_uaf.cpp:41:{{[0-9]+}} in main
\ No newline at end of file

>From e80bc040112e277a177208e162516e74b5709e59 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 30 Jun 2026 14:54:32 +0530
Subject: [PATCH 41/46] [compiler-rt][asan][AMDGPU] Self-heal device header
 lookup after runtime re-init

GetHeader() cached the runtime-shutdown state in a sticky flag that was
never cleared, so after an hsa shutdown -> hsa_init cycle it stayed
stuck in the single-page fallback path.

Query DeviceBackend::IsRuntimeShutdown() directly instead, so lookup
recovers once the runtime is re-initialized.
---
 .../sanitizer_allocator_device.h              | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
index cfcaa64c81564..16fc16ffc1fae 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_device.h
@@ -263,7 +263,7 @@ class DeviceAllocatorT {
     if (p < min_mmap_)
       return nullptr;
     if (p >= max_mmap_) {
-      // TODO (bingma): If dev_runtime_unloaded_ = true, map_size is limited
+      // TODO (bingma): When the device runtime is unloaded, map_size is limited
       // to one page and we might miss a valid 'ptr'. If we hit cases where
       // this kind of miss is unacceptable, we will need to implement a full
       // solution with higher cost
@@ -353,19 +353,15 @@ class DeviceAllocatorT {
   }
 
   Header* GetHeader(uptr chunk, Header* h) const {
-    // Device allocator has dependency on device runtime. If device runtime
-    // is unloaded, GetPointerInfo() will fail. For such case, we can still
-    // return a valid value for map_beg, map_size will be limited to one page
-    if (LIKELY(!dev_runtime_unloaded_)) {
+    // Device allocator depends on the device runtime. Query the authoritative
+    // shutdown state directly (a cheap atomic). It is reset by
+    // ClearRuntimeShutdownState() on hsa re-init, so header lookup self-heals
+    // after a shutdown/re-init cycle without a sticky per-allocator flag.
+    if (LIKELY(!DeviceBackend::IsRuntimeShutdown())) {
       if (DeviceBackend::GetPointerInfo(chunk, h))
         return h;
-      // If GetPointerInfo() fails, we don't assume the runtime is unloaded yet.
-      // We just return a conservative single-page header. Here mark/check the
-      // runtime shutdown state
-      dev_runtime_unloaded_ = DeviceBackend::IsRuntimeShutdown();
     }
-    // If we reach here, device runtime is unloaded.
-    // Fallback: conservative single-page header
+    // Runtime down (or GetPointerInfo failed): conservative single-page header.
     h->map_beg = chunk;
     h->map_size = page_size_;
     return h;
@@ -379,7 +375,6 @@ class DeviceAllocatorT {
 
   bool enabled_;
   bool mem_funcs_inited_;
-  mutable bool dev_runtime_unloaded_;
   // Maximum of mem_funcs_init_count_ is 2:
   //   1. The initial init called from Init(...), it could fail if
   //      the device runtime is dynamically loaded with dlopen()

>From 501c695a70f8e621eca78735c694e69e4adde280 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 23 Jun 2026 15:30:51 +0530
Subject: [PATCH 42/46] [compiler-rt][asan][AMDGPU] Report reserve/first-free
 stacks for VMEM double-free

GPU-only HSA VMEM reservations (hsa_amd_vmem_address_reserve_align
without HSA_AMD_VMEM_ADDRESS_NO_REGISTER) are tracked by
VmemGpuReserveTracker rather than the host ASan heap, so they carry no
AsanChunk metadata. The previous double-free diagnostic could only print
the current (second) free stack, and a guard was added in
HeapAddressDescription::Print() to paper over the missing chunk info.
Instead, record full provenance for these reservations and emit a
dedicated report:
- VmemGpuReserveTracker now stores the reserve and first-free StackDepot
  ids and tids; OnReserve()/MarkFreed() capture them and
  GetReservationInfo() exposes them to the reporter.
- ReportVmemDoubleFree() prints the reservation bounds plus the "first
  freed by" and "previously reserved by" stacks, without overloading the
  host HeapAddressDescription model.
- The vmem double-free interceptor path wires this up; stack ids are
  computed on the ASan side so the tracker stays free of stack handling.
- Revert the HeapAddressDescription::Print() early-return guard, which
  checked the wrong sentinel and is unnecessary now.  Extend the vmem
  double-free lit test to check the richer output.
---
 compiler-rt/lib/asan/asan_descriptions.cpp    |  3 --
 compiler-rt/lib/asan/asan_hsa_allocator.cpp   | 16 ++++++---
 compiler-rt/lib/asan/asan_report.cpp          | 33 +++++++++++++++++++
 compiler-rt/lib/asan/asan_report.h            |  7 ++++
 .../sanitizer_allocator_amdgpu.cpp            | 32 ++++++++++++++++--
 .../sanitizer_allocator_amdgpu.h              | 21 ++++++++++--
 ...vmem_address_reserve_align_double_free.cpp | 12 ++++---
 7 files changed, 108 insertions(+), 16 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_descriptions.cpp b/compiler-rt/lib/asan/asan_descriptions.cpp
index e6e815591f4f6..551b819a4c436 100644
--- a/compiler-rt/lib/asan/asan_descriptions.cpp
+++ b/compiler-rt/lib/asan/asan_descriptions.cpp
@@ -418,9 +418,6 @@ void StackAddressDescription::Print() const {
 }
 
 void HeapAddressDescription::Print() const {
-  if (alloc_tid == kInvalidTid)
-    return;
-
   PrintHeapChunkAccess(addr, chunk_access);
 
   asanThreadRegistry().CheckLocked();
diff --git a/compiler-rt/lib/asan/asan_hsa_allocator.cpp b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
index 149321b1a60d4..86828d0ccb1ce 100644
--- a/compiler-rt/lib/asan/asan_hsa_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_hsa_allocator.cpp
@@ -24,6 +24,7 @@
 #  include "sanitizer_common/sanitizer_allocator_checks.h"
 #  include "sanitizer_common/sanitizer_errno.h"
 #  include "sanitizer_common/sanitizer_hsa.h"
+#  include "sanitizer_common/sanitizer_stackdepot.h"
 
 namespace __asan {
 
@@ -186,7 +187,8 @@ hsa_status_t asan_hsa_amd_vmem_address_reserve_align(
         ptr, size, address, alignment, flags);
     if (status == HSA_STATUS_SUCCESS && ptr && *ptr)
       __sanitizer::VmemGpuReserveTracker::Get().OnReserve(
-          reinterpret_cast<uptr>(*ptr), size);
+          reinterpret_cast<uptr>(*ptr), size, StackDepotPut(*stack),
+          GetCurrentTidOrInvalid());
     return status;
   }
 
@@ -234,15 +236,21 @@ hsa_status_t asan_hsa_amd_vmem_address_free(void* ptr, size_t size,
     case VmemGpuReserveTracker::kFirstFree: {
       const hsa_status_t status = REAL(hsa_amd_vmem_address_free)(ptr, size);
       if (status == HSA_STATUS_SUCCESS)
-        VmemGpuReserveTracker::Get().MarkFreed(ptr_uptr, size);
+        VmemGpuReserveTracker::Get().MarkFreed(
+            ptr_uptr, size, StackDepotPut(*stack), GetCurrentTidOrInvalid());
       return status;
     }
     case VmemGpuReserveTracker::kSizeMismatch:
       errno = errno_EINVAL;
       return HSA_STATUS_ERROR_INVALID_ARGUMENT;
-    case VmemGpuReserveTracker::kDoubleFree:
-      ReportDoubleFree(ptr_uptr, stack);
+    case VmemGpuReserveTracker::kDoubleFree: {
+      VmemGpuReserveTracker::ReservationInfo info = {};
+      VmemGpuReserveTracker::Get().GetReservationInfo(ptr_uptr, &info);
+      ReportVmemDoubleFree(ptr_uptr, size, info.reserve_stack_id,
+                           info.reserve_tid, info.first_free_stack_id,
+                           info.first_free_tid, stack);
       return HSA_STATUS_SUCCESS;
+    }
   }
   // Passthrough: untracked vmem frees (eg: fixed-address reservations) go
   // straight to ROCr.
diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp
index 331ce8e321c11..d8279539d76fe 100644
--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -270,6 +270,39 @@ void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
   in_report.ReportError(error);
 }
 
+#if SANITIZER_AMDHSA
+void ReportVmemDoubleFree(uptr addr, uptr size, u32 reserve_stack_id,
+                          u32 reserve_tid, u32 first_free_stack_id,
+                          u32 first_free_tid, BufferedStackTrace* free_stack) {
+  ScopedInErrorReport in_report;
+  Decorator d;
+  Printf("%s", d.Error());
+  Report(
+      "ERROR: AddressSanitizer: attempting double-free on %p in thread %s:\n",
+      (void*)addr, AsanThreadIdAndName(GetCurrentTidOrInvalid()).c_str());
+  Printf("%s", d.Default());
+  CHECK_GT(free_stack->size, 0);
+  free_stack->Print();
+
+  // GPU-only VMEM reservations have no AsanChunk metadata; describe the tracked
+  // reservation bounds and the reserve / first-free provenance instead.
+  Printf("%s%p is a device VMEM reservation of size %zu [%p,%p)%s\n",
+         d.Location(), (void*)addr, size, (void*)addr, (void*)(addr + size),
+         d.Default());
+  if (first_free_stack_id) {
+    Printf("%sfirst freed by thread %s here:%s\n", d.Allocation(),
+           AsanThreadIdAndName(first_free_tid).c_str(), d.Default());
+    StackDepotGet(first_free_stack_id).Print();
+  }
+  if (reserve_stack_id) {
+    Printf("%spreviously reserved by thread %s here:%s\n", d.Allocation(),
+           AsanThreadIdAndName(reserve_tid).c_str(), d.Default());
+    StackDepotGet(reserve_stack_id).Print();
+  }
+  ReportErrorSummary("double-free", free_stack);
+}
+#endif  // SANITIZER_AMDHSA
+
 void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size,
                                  uptr delete_alignment,
                                  BufferedStackTrace *free_stack) {
diff --git a/compiler-rt/lib/asan/asan_report.h b/compiler-rt/lib/asan/asan_report.h
index d67cb04daba68..ed1a80d8652b2 100644
--- a/compiler-rt/lib/asan/asan_report.h
+++ b/compiler-rt/lib/asan/asan_report.h
@@ -56,6 +56,13 @@ void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size,
 void ReportFreeSizeMismatch(uptr addr, uptr delete_size, uptr delete_alignment,
                             BufferedStackTrace* free_stack);
 void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
+#if SANITIZER_AMDHSA
+// Double-free of a GPU-only HSA VMEM reservation. Such reservations have no
+// AsanChunk metadata, so provenance is supplied from VmemGpuReserveTracker.
+void ReportVmemDoubleFree(uptr addr, uptr size, u32 reserve_stack_id,
+                          u32 reserve_tid, u32 first_free_stack_id,
+                          u32 first_free_tid, BufferedStackTrace* free_stack);
+#endif  // SANITIZER_AMDHSA
 void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
 void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
                              AllocType alloc_type,
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index d3ccf27c986df..f3591d3fc3a82 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -334,13 +334,18 @@ void VmemGpuReserveTracker::EnsureInited() {
   atomic_store(&inited_, 1, memory_order_release);
 }
 
-void VmemGpuReserveTracker::OnReserve(uptr ptr, uptr size) {
+void VmemGpuReserveTracker::OnReserve(uptr ptr, uptr size, u32 reserve_stack_id,
+                                      u32 reserve_tid) {
   EnsureInited();
   SpinMutexLock l(&mu_);
   VmemGpuReservation entry;
   entry.ptr = ptr;
   entry.size = size;
   entry.freed = false;
+  entry.reserve_stack_id = reserve_stack_id;
+  entry.reserve_tid = reserve_tid;
+  entry.first_free_stack_id = 0;
+  entry.first_free_tid = 0;
   reservations_.push_back(entry);
 }
 
@@ -361,17 +366,40 @@ VmemGpuReserveTracker::FreeResult VmemGpuReserveTracker::CheckFree(uptr ptr,
   return kNotTracked;
 }
 
-void VmemGpuReserveTracker::MarkFreed(uptr ptr, uptr size) {
+void VmemGpuReserveTracker::MarkFreed(uptr ptr, uptr size, u32 free_stack_id,
+                                      u32 free_tid) {
   EnsureInited();
   SpinMutexLock l(&mu_);
   for (uptr i = 0; i < reservations_.size(); ++i) {
     VmemGpuReservation& r = reservations_[i];
     if (r.ptr == ptr && r.size == size) {
       r.freed = true;
+      r.first_free_stack_id = free_stack_id;
+      r.first_free_tid = free_tid;
       return;
     }
   }
 }
 
+bool VmemGpuReserveTracker::GetReservationInfo(uptr ptr, ReservationInfo* out) {
+  if (!out)
+    return false;
+  EnsureInited();
+  SpinMutexLock l(&mu_);
+  for (uptr i = 0; i < reservations_.size(); ++i) {
+    const VmemGpuReservation& r = reservations_[i];
+    if (r.ptr != ptr)
+      continue;
+    out->base = r.ptr;
+    out->size = r.size;
+    out->reserve_stack_id = r.reserve_stack_id;
+    out->reserve_tid = r.reserve_tid;
+    out->first_free_stack_id = r.first_free_stack_id;
+    out->first_free_tid = r.first_free_tid;
+    return true;
+  }
+  return false;
+}
+
 }  // namespace __sanitizer
 #endif  // SANITIZER_AMDHSA
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
index 9f82753a2393a..43b4d33edfda1 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.h
@@ -51,19 +51,36 @@ class VmemGpuReserveTracker {
     kDoubleFree,
   };
 
+  // Provenance returned for a double-freed reservation so the reporter can
+  // print the reserve and first-free stacks (captured as StackDepot ids).
+  struct ReservationInfo {
+    uptr base;
+    uptr size;
+    u32 reserve_stack_id;
+    u32 reserve_tid;
+    u32 first_free_stack_id;
+    u32 first_free_tid;
+  };
+
   static VmemGpuReserveTracker& Get();
 
-  void OnReserve(uptr ptr, uptr size);
+  void OnReserve(uptr ptr, uptr size, u32 reserve_stack_id, u32 reserve_tid);
   // CheckFree validates without updating state; call MarkFreed only after the
   // real hsa_amd_vmem_address_free succeeds.
   FreeResult CheckFree(uptr ptr, uptr size);
-  void MarkFreed(uptr ptr, uptr size);
+  void MarkFreed(uptr ptr, uptr size, u32 free_stack_id, u32 free_tid);
+  // Fills *out for the reservation at ptr. Returns false when untracked.
+  bool GetReservationInfo(uptr ptr, ReservationInfo* out);
 
  private:
   struct VmemGpuReservation {
     uptr ptr;
     uptr size;
     bool freed;
+    u32 reserve_stack_id;
+    u32 reserve_tid;
+    u32 first_free_stack_id;
+    u32 first_free_tid;
   };
 
   void EnsureInited();
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index 83534414fc7d0..a38cc46e2aa25 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -27,10 +27,9 @@ int main() {
 
   // NOTE: To use `hipMallocManaged` way of reserving memory,
   // use `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in `flags`.
-  if (hsa_amd_vmem_address_reserve_align(
-          &mem, kSize, /*address=*/0,
-          /*alignment=*/4096,
-          /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER) != HSA_STATUS_SUCCESS ||
+  if (hsa_amd_vmem_address_reserve_align(&mem, kSize, /*address=*/0,
+                                         /*alignment=*/4096,
+                                         /*flags=*/0) != HSA_STATUS_SUCCESS ||
       !mem) {
     fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
     return 1;
@@ -43,5 +42,8 @@ int main() {
   return 0;
 }
 
-// CHECK: ERROR: AddressSanitizer: attempting double-free
+// CHECK: ERROR: AddressSanitizer: attempting double-free on {{0x[0-9a-f]+}} in thread T0
+// CHECK: is a device VMEM reservation of size {{[0-9]+}}
+// CHECK: first freed by thread T0 here:
+// CHECK: previously reserved by thread T0 here:
 // CHECK: SUMMARY: AddressSanitizer: double-free

>From c8a2d9991c7471b0c2a759e48cce2651c4a7a3fb Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Fri, 10 Jul 2026 09:50:06 +0530
Subject: [PATCH 43/46] [compiler-rt][asan] Drop redundant vmem guard in
 ReportDoubleFree

ReportDoubleFree is only reached for chunks already read as
CHUNK_QUARANTINE, and GPU-only vmem double-frees are handled in the HSA
interceptor via ReportVmemDoubleFree, so the FindHeapChunkByAddress
guard was dead code. Restore the function to its upstream form.
---
 compiler-rt/lib/asan/asan_report.cpp | 18 +-----------------
 1 file changed, 1 insertion(+), 17 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp
index d8279539d76fe..c07232cad8434 100644
--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -248,23 +248,7 @@ void ReportDeadlySignal(const SignalContext &sig) {
   in_report.ReportError(error);
 }
 
-void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
-  // GPU-only vmem reservations (VmemGpuReserveTracker) are not backed by
-  // AsanChunk metadata. Avoid reading chunk headers at such addresses to
-  // prevent potential crashes in StackDepot (e.g. 0xaa poison patterns).
-  if (!FindHeapChunkByAddress(addr).IsValid()) {
-    ScopedInErrorReport in_report;
-    Decorator d;
-    Printf("%s", d.Error());
-    Report(
-        "ERROR: AddressSanitizer: attempting double-free on %p in thread %s:\n",
-        (void*)addr, AsanThreadIdAndName(GetCurrentTidOrInvalid()).c_str());
-    Printf("%s", d.Default());
-    CHECK_GT(free_stack->size, 0);
-    free_stack->Print();
-    ReportErrorSummary("double-free", free_stack);
-    return;
-  }
+void ReportDoubleFree(uptr addr, BufferedStackTrace* free_stack) {
   ScopedInErrorReport in_report;
   ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr);
   in_report.ReportError(error);

>From ae844a80dc395cb347c27407c9f123df86946478 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Fri, 10 Jul 2026 10:15:57 +0530
Subject: [PATCH 44/46] [compiler-rt][asan][AMDGPU] Add HSA_CHECK helper to
 simplify HSA status checks in tests

Add an HSA_CHECK macro (decodes the HSA status, aborts on failure,
evaluates its argument once) and use it to replace the repeated
status-check boilerplate in the AMDGPU ASan tests. Drop the now-unused
hsa_amd_test_require_init.
---
 .../hsa_amd_ipc_memory_attach_heap_oob.cpp    | 28 +++---------
 .../AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp   | 45 ++++---------------
 .../hsa_amd_memory_async_copy_overlap.cpp     | 11 ++---
 ...a_amd_memory_pool_allocate_double_free.cpp | 12 ++---
 .../hsa_amd_memory_pool_allocate_uaf.cpp      | 12 ++---
 .../hsa_amd_pointer_info_memory_pool.cpp      | 21 ++-------
 ...sa_amd_pointer_info_vmem_reserve_align.cpp | 24 +++-------
 .../TestCases/AMDGPU/hsa_amd_test_helpers.h   | 20 ++++++---
 ...vmem_address_reserve_align_double_free.cpp | 13 ++----
 ...hsa_amd_vmem_address_reserve_align_uaf.cpp | 17 +++----
 .../AMDGPU/hsa_memory_copy_overlap.cpp        |  7 ++-
 11 files changed, 59 insertions(+), 151 deletions(-)

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
index f39b86e6fe333..1e537c6b5e504 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
@@ -23,8 +23,7 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   HsaAmdPoolSearch ps;
   if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
@@ -32,29 +31,14 @@ int main() {
 
   constexpr size_t kBytes = 64;
   void *mem = nullptr;
-  if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
-          HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem));
 
   hsa_amd_ipc_memory_t ipc = {};
-  if (hsa_amd_ipc_memory_create(mem, kBytes, &ipc) != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_ipc_memory_create failed\n");
-    (void)hsa_amd_memory_pool_free(mem);
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_ipc_memory_create(mem, kBytes, &ipc));
 
   void *mapped = nullptr;
-  if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
-                                /*mapping_agents=*/nullptr,
-                                &mapped) != HSA_STATUS_SUCCESS ||
-      !mapped) {
-    fprintf(stderr, "hsa_amd_ipc_memory_attach failed\n");
-    (void)hsa_amd_memory_pool_free(mem);
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
+                                      /*mapping_agents=*/nullptr, &mapped));
 
   HsaAmdCpuAgentPick cpu;
   hsa_amd_test_cpu_agent_pick_init(&cpu);
@@ -78,4 +62,4 @@ int main() {
 
 // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
 // CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
-// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:71:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: heap-buffer-overflow {{.*}}hsa_amd_ipc_memory_attach_heap_oob.cpp:55:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
index f84b1bbdde2a2..f5a074e8669fc 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
@@ -22,8 +22,7 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   HsaAmdPoolSearch ps;
   if (hsa_amd_test_find_first_coarse_gpu_ipc_pool(&ps))
@@ -31,50 +30,22 @@ int main() {
 
   constexpr size_t kBytes = 64;
   void *mem = nullptr;
-  if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
-          HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem));
 
   hsa_amd_ipc_memory_t ipc = {};
-  if (hsa_amd_ipc_memory_create(mem, kBytes, &ipc) != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_ipc_memory_create failed\n");
-    (void)hsa_amd_memory_pool_free(mem);
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_ipc_memory_create(mem, kBytes, &ipc));
 
   void *mapped = nullptr;
-  if (hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
-                                /*mapping_agents=*/nullptr,
-                                &mapped) != HSA_STATUS_SUCCESS ||
-      !mapped) {
-    fprintf(stderr, "hsa_amd_ipc_memory_attach failed\n");
-    (void)hsa_amd_memory_pool_free(mem);
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_ipc_memory_attach(&ipc, kBytes, /*num_agents=*/0,
+                                      /*mapping_agents=*/nullptr, &mapped));
 
   hsa_amd_pointer_info_t info = {};
   info.size = sizeof(hsa_amd_pointer_info_t);
-  if (hsa_amd_pointer_info(mapped, &info, nullptr, nullptr, nullptr) !=
-      HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_pointer_info on imported mapping failed\n");
-    (void)hsa_amd_ipc_memory_detach(mapped);
-    (void)hsa_amd_memory_pool_free(mem);
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_pointer_info(mapped, &info, nullptr, nullptr, nullptr));
 
-  if (hsa_amd_ipc_memory_detach(mapped) != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_ipc_memory_detach failed\n");
-    (void)hsa_amd_memory_pool_free(mem);
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_ipc_memory_detach(mapped));
 
-  if (hsa_amd_memory_pool_free(mem) != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_memory_pool_free failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_memory_pool_free(mem));
 
   printf("ipc roundtrip ok\n");
   return 0;
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
index c773b55b8e866..f78aca309d70b 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
@@ -17,8 +17,7 @@
 #include <stdlib.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   HsaAmdAgentPick pick;
   hsa_amd_test_agent_pick_init(&pick);
@@ -31,12 +30,8 @@ int main() {
   }
 
   hsa_signal_t completion = {};
-  if (hsa_signal_create(/*initial_value=*/0, /*num_consumers=*/0,
-                        /*consumers=*/nullptr,
-                        &completion) != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_signal_create failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_signal_create(/*initial_value=*/0, /*num_consumers=*/0,
+                              /*consumers=*/nullptr, &completion));
 
   char buf[128];
   char *dst = buf;
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index 86748105fed09..b5260006f03fc 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -17,20 +17,14 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   HsaAmdPoolSearch ps;
   if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
     return 1;
 
   void *mem = nullptr;
-  if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
-          HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem));
 
   (void)hsa_amd_memory_pool_free(mem);
   (void)hsa_amd_memory_pool_free(mem);
@@ -40,4 +34,4 @@ int main() {
 }
 
 // CHECK: ERROR: AddressSanitizer: attempting double-free
-// CHECK: SUMMARY: AddressSanitizer: double-free {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:36:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: double-free {{.*}}hsa_amd_memory_pool_allocate_double_free.cpp:30:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
index 3be0a5e9041b8..5a6755418bc66 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
@@ -16,8 +16,7 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   HsaAmdPoolSearch ps;
   if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
@@ -25,12 +24,7 @@ int main() {
 
   constexpr size_t kBytes = 64;
   void *mem = nullptr;
-  if (hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem) !=
-          HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_memory_pool_allocate(ps.pool, kBytes, 0, &mem));
 
   (void)hsa_amd_memory_pool_free(mem);
   auto *p = reinterpret_cast<volatile char *>(mem);
@@ -42,4 +36,4 @@ int main() {
 
 // CHECK: ERROR: AddressSanitizer: heap-use-after-free
 // CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
-// CHECK: SUMMARY: AddressSanitizer: heap-use-after-free {{.*}}hsa_amd_memory_pool_allocate_uaf.cpp:37:{{[0-9]+}} in main
+// CHECK: SUMMARY: AddressSanitizer: heap-use-after-free {{.*}}hsa_amd_memory_pool_allocate_uaf.cpp:31:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
index c6c54373210e5..25fe80417fccc 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
@@ -17,29 +17,19 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   HsaAmdPoolSearch ps;
   if (hsa_amd_test_find_first_runtime_alloc_pool(&ps))
     return 1;
 
   void *mem = nullptr;
-  if (hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem) !=
-          HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_memory_pool_allocate failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_memory_pool_allocate(ps.pool, 64, 0, &mem));
 
   hsa_amd_pointer_info_t info = {};
   info.size = sizeof(hsa_amd_pointer_info_t);
 
-  if (hsa_amd_pointer_info(mem, &info, nullptr, nullptr, nullptr) !=
-      HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_pointer_info failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_pointer_info(mem, &info, nullptr, nullptr, nullptr));
 
   printf("pointer_info_pool type: %d\n", info.type);
   printf("pointer_info_pool sizeInBytes: %zu\n", info.sizeInBytes);
@@ -47,10 +37,7 @@ int main() {
   printf("pointer_info_pool end: %p\n",
          (void *)((uintptr_t)info.agentBaseAddress + info.sizeInBytes));
 
-  if (hsa_amd_memory_pool_free(mem) != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_memory_pool_free failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_memory_pool_free(mem));
   return 0;
 }
 
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
index dceb8eca54d41..9b3faffa05685 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -19,21 +19,16 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   const size_t kSize = 4096; //4KiB size
   // Here alignment is larger than a page size, so the reserved address will be aligned to 64KiB.
   const uint64_t kAlign = 65536; //64KiB alignment
   void *mem = nullptr;
   // Host-accessible vmem reserve via NO_REGISTER (host mmap, not KFD VA).
-  if (hsa_amd_vmem_address_reserve_align(
-          &mem, kSize, /*address=*/0, kAlign,
-          /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER) != HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_vmem_address_reserve_align(
+      &mem, kSize, /*address=*/0, kAlign,
+      /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER));
 
   const uintptr_t user = reinterpret_cast<uintptr_t>(mem);
   if (user % kAlign != 0) {
@@ -44,11 +39,7 @@ int main() {
   hsa_amd_pointer_info_t info = {};
   info.size = sizeof(hsa_amd_pointer_info_t);
   // Intercepted by ASan, where hostBaseAddress is the user pointer.
-  if (hsa_amd_pointer_info(mem, &info, nullptr, nullptr, nullptr) !=
-      HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_pointer_info failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_pointer_info(mem, &info, nullptr, nullptr, nullptr));
 
   // Verify: User Pointer(mem) returned is hostBaseAddress as expected.
   if (info.hostBaseAddress != mem ||
@@ -63,10 +54,7 @@ int main() {
   printf("pointer_info_vmem sizeInBytes: %zu\n", info.sizeInBytes);
   printf("pointer_info_vmem begin: %p\n", info.hostBaseAddress);
 
-  if (hsa_amd_vmem_address_free(mem, kSize) != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_amd_vmem_address_free failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_vmem_address_free(mem, kSize));
   return 0;
 }
 
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
index 53b7f571fcbcf..6524cadf032b0 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_test_helpers.h
@@ -11,15 +11,21 @@
 #include <hsa/hsa.h>
 #include <hsa/hsa_ext_amd.h>
 
+#include <cstdlib>
 #include <stdio.h>
 
-inline int hsa_amd_test_require_init() {
-  if (hsa_init() != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_init failed\n");
-    return 1;
-  }
-  return 0;
-}
+/// Abort with a decoded HSA error string if `expr` is not HSA_STATUS_SUCCESS.
+#define HSA_CHECK(expr)                                                        \
+  do {                                                                         \
+    hsa_status_t hsa_check_st = (expr);                                        \
+    if (hsa_check_st != HSA_STATUS_SUCCESS) {                                  \
+      const char *hsa_check_msg = nullptr;                                     \
+      hsa_status_string(hsa_check_st, &hsa_check_msg);                         \
+      fprintf(stderr, "HSA error at %s:%d: %s (%d)\n", __FILE__, __LINE__,     \
+              hsa_check_msg ? hsa_check_msg : "unknown", hsa_check_st);        \
+      abort();                                                                 \
+    }                                                                          \
+  } while (0)
 
 /// Return 1 if `st` indicates a hard failure from hsa_iterate_agents.
 inline int hsa_amd_test_iterate_agents_ok(hsa_status_t st) {
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index a38cc46e2aa25..db9a7130323f3 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -16,8 +16,7 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   // Size must be a non-zero multiple of the page size; address must be 0 so
   // the interceptor records the reservation for double-free diagnosis (see
@@ -27,13 +26,9 @@ int main() {
 
   // NOTE: To use `hipMallocManaged` way of reserving memory,
   // use `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in `flags`.
-  if (hsa_amd_vmem_address_reserve_align(&mem, kSize, /*address=*/0,
-                                         /*alignment=*/4096,
-                                         /*flags=*/0) != HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_vmem_address_reserve_align(&mem, kSize, /*address=*/0,
+                                               /*alignment=*/4096,
+                                               /*flags=*/0));
 
   (void)hsa_amd_vmem_address_free(mem, kSize);
   (void)hsa_amd_vmem_address_free(mem, kSize);
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
index c156c6df39d79..6885e710f01f7 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
@@ -16,8 +16,7 @@
 #include <stdio.h>
 
 int main() {
-  if (hsa_amd_test_require_init())
-    return 1;
+  HSA_CHECK(hsa_init());
 
   // Size must be a non-zero multiple of the page size; address must be 0 so
   // the interceptor records the reservation for double-free diagnosis (see
@@ -27,14 +26,10 @@ int main() {
 
   // NOTE: To use `hipMallocManaged` way of reserving memory,
   // use `HSA_AMD_VMEM_ADDRESS_NO_REGISTER` in `flags`.
-  if (hsa_amd_vmem_address_reserve_align(
-          &mem, kSize, /*address=*/0,
-          /*alignment=*/4096,
-          /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER) != HSA_STATUS_SUCCESS ||
-      !mem) {
-    fprintf(stderr, "hsa_amd_vmem_address_reserve_align failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_amd_vmem_address_reserve_align(
+      &mem, kSize, /*address=*/0,
+      /*alignment=*/4096,
+      /*flags=*/HSA_AMD_VMEM_ADDRESS_NO_REGISTER));
 
   (void)hsa_amd_vmem_address_free(mem, kSize);
   auto *p = reinterpret_cast<volatile char *>(mem);
@@ -46,4 +41,4 @@ int main() {
 
 // CHECK: ERROR: AddressSanitizer: heap-use-after-free
 // CHECK-NEXT: WRITE of size 1 at {{0x[0-9a-f]+}} thread T0
-// CHECK: SUMMARY: AddressSanitizer: heap-use-after-free {{.*}}hsa_amd_vmem_address_reserve_align_uaf.cpp:41:{{[0-9]+}} in main
\ No newline at end of file
+// CHECK: SUMMARY: AddressSanitizer: heap-use-after-free {{.*}}hsa_amd_vmem_address_reserve_align_uaf.cpp:36:{{[0-9]+}} in main
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
index 51f90366d40b2..78a7c2d8832a8 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
@@ -8,16 +8,15 @@
 // REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
+#include "hsa_amd_test_helpers.h"
+
 #include <hsa/hsa.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 
 int main() {
-  if (hsa_init() != HSA_STATUS_SUCCESS) {
-    fprintf(stderr, "hsa_init failed\n");
-    return 1;
-  }
+  HSA_CHECK(hsa_init());
 
   char buf[128];
   char *dst = buf;

>From 5b8285327d4a715bdd1b11622c2664fd23b573c2 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Tue, 14 Jul 2026 11:19:07 +0530
Subject: [PATCH 45/46] [compiler-rt][asan][AMDGPU] Gate VMEM tests on
 HSA_VMEM_SUPPORTED env flag

VMEM API availability is a runtime KFD/kernel property, so expose it as
the `hsa-vmem` lit feature when HSA_VMEM_SUPPORTED is set and gate the
VMEM tests on it via REQUIRES.
---
 .../AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp |  2 +-
 .../hsa_amd_vmem_address_reserve_align_double_free.cpp |  2 +-
 .../AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp  |  2 +-
 .../test/asan/TestCases/AMDGPU/lit.local.cfg.py        | 10 ++++++++++
 4 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
index 9b3faffa05685..b7cd5800f931e 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -6,7 +6,7 @@
 // user-visible base and size, not the internal HSA backing mapping. A fixed
 // +page offset is wrong when alignment is larger than the page size.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm, hsa-vmem
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index db9a7130323f3..cdb33cb73d998 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -5,7 +5,7 @@
 // Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
 // hsa_amd_vmem_address_free interceptors: Using the same freed reserved range twice is diagnosed as double-free.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm, hsa-vmem
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
index 6885e710f01f7..9f84ee490b233 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
@@ -5,7 +5,7 @@
 // Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
 // hsa_amd_vmem_address_free interceptors: Using the same freed reserved range is diagnosed as use-after-free.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm, hsa-vmem
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
index daa89c52a2a9c..ef0a5eb933fd0 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
@@ -93,6 +93,16 @@ def patch_body(body):
             config.unsupported = True
         else:
             config.available_features.add("rocm")
+            # VMEM API availability is a runtime KFD/kernel property and cannot
+            # be detected at test-discovery time. Let the environment declare it
+            # (HSA_VMEM_SUPPORTED=1) so VMEM tests gate on `REQUIRES: hsa-vmem`.
+            if os.environ.get("HSA_VMEM_SUPPORTED", "").strip().lower() in (
+                "1",
+                "true",
+                "yes",
+                "on",
+            ):
+                config.available_features.add("hsa-vmem")
             # Linux ASan defaults to leak detection; disable for ROCm/HSA tests.
             _asan = config.environment.get("ASAN_OPTIONS", "")
             if _asan:

>From 8b449e2a7378fd5afd02e98784818dcc94b04f03 Mon Sep 17 00:00:00 2001
From: Amit Pandey <pandey.kumaramit2023 at gmail.com>
Date: Fri, 17 Jul 2026 14:20:07 +0530
Subject: [PATCH 46/46] [compiler-rt][asan] Enable AMDHSA host support by
 default on Linux

Make SANITIZER_AMDHSA a platform-derived macro (Linux, non-Android) in
sanitizer_platform.h instead of an opt-in CMake flag, and drop the
SANITIZER_AMDHSA option and -D injection. The HSA path stays inert at
runtime unless the ROCm/HSA runtime is dlopen'd, so builds without ROCm
are unaffected.

Remove the sanitizer-amdgpu lit feature and its plumbing; AMDGPU tests
now gate on linux + rocm (runtime detection) only.
---
 compiler-rt/CMakeLists.txt                               | 7 -------
 compiler-rt/lib/asan/asan_allocator.h                    | 5 -----
 compiler-rt/lib/asan/asan_report.cpp                     | 4 +---
 compiler-rt/lib/asan/asan_report.h                       | 2 --
 compiler-rt/lib/sanitizer_common/sanitizer_allocator.h   | 3 +++
 .../lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp  | 3 ++-
 compiler-rt/lib/sanitizer_common/sanitizer_platform.h    | 9 ++++++++-
 compiler-rt/test/CMakeLists.txt                          | 2 --
 .../AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp        | 2 +-
 .../TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp    | 2 +-
 .../AMDGPU/hsa_amd_memory_async_copy_overlap.cpp         | 2 +-
 .../AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp  | 2 +-
 .../AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp          | 2 +-
 .../AMDGPU/hsa_amd_pointer_info_memory_pool.cpp          | 2 +-
 .../AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp   | 2 +-
 .../hsa_amd_vmem_address_reserve_align_double_free.cpp   | 2 +-
 .../AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp    | 2 +-
 .../asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp    | 2 +-
 compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py  | 5 +++--
 compiler-rt/test/lit.common.cfg.py                       | 3 ---
 compiler-rt/test/lit.common.configured.in                | 1 -
 21 files changed, 27 insertions(+), 37 deletions(-)

diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index fe6f1941018f6..d720b86c6df24 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -227,9 +227,6 @@ endif()
 
 option(SANITIZER_ALLOW_CXXABI "Allow use of C++ ABI details in ubsan" ON)
 
-option(SANITIZER_AMDHSA
-  "Build host ASan with ROCm HSA API interceptors" OFF)
-
 set(SANITIZER_CAN_USE_CXXABI OFF)
 if (cxxabi_supported AND SANITIZER_ALLOW_CXXABI)
   set(SANITIZER_CAN_USE_CXXABI ON)
@@ -564,10 +561,6 @@ elseif(COMPILER_RT_HAS_G_FLAG)
   list(APPEND SANITIZER_COMMON_CFLAGS -g)
 endif()
 
-if(SANITIZER_AMDHSA)
-  list(APPEND SANITIZER_COMMON_CFLAGS -DSANITIZER_AMDHSA=1)
-endif()
-
 if(LLVM_ENABLE_MODULES)
   # Sanitizers cannot be built with -fmodules. The interceptors intentionally
   # don't include system headers, which is incompatible with modules.
diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
index 3cb6b3b9a11f0..aad998f68927c 100644
--- a/compiler-rt/lib/asan/asan_allocator.h
+++ b/compiler-rt/lib/asan/asan_allocator.h
@@ -18,11 +18,6 @@
 #include "asan_interceptors.h"
 #include "asan_internal.h"
 #include "sanitizer_common/sanitizer_allocator.h"
-#if SANITIZER_AMDHSA
-namespace __sanitizer {
-#  include "sanitizer_common/sanitizer_allocator_amdgpu.h"
-}  // namespace __sanitizer
-#endif
 #include "sanitizer_common/sanitizer_list.h"
 #include "sanitizer_common/sanitizer_platform.h"
 
diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp
index c07232cad8434..b465879d6268a 100644
--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -248,13 +248,12 @@ void ReportDeadlySignal(const SignalContext &sig) {
   in_report.ReportError(error);
 }
 
-void ReportDoubleFree(uptr addr, BufferedStackTrace* free_stack) {
+void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
   ScopedInErrorReport in_report;
   ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr);
   in_report.ReportError(error);
 }
 
-#if SANITIZER_AMDHSA
 void ReportVmemDoubleFree(uptr addr, uptr size, u32 reserve_stack_id,
                           u32 reserve_tid, u32 first_free_stack_id,
                           u32 first_free_tid, BufferedStackTrace* free_stack) {
@@ -285,7 +284,6 @@ void ReportVmemDoubleFree(uptr addr, uptr size, u32 reserve_stack_id,
   }
   ReportErrorSummary("double-free", free_stack);
 }
-#endif  // SANITIZER_AMDHSA
 
 void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size,
                                  uptr delete_alignment,
diff --git a/compiler-rt/lib/asan/asan_report.h b/compiler-rt/lib/asan/asan_report.h
index ed1a80d8652b2..9089e4ca57308 100644
--- a/compiler-rt/lib/asan/asan_report.h
+++ b/compiler-rt/lib/asan/asan_report.h
@@ -56,13 +56,11 @@ void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size,
 void ReportFreeSizeMismatch(uptr addr, uptr delete_size, uptr delete_alignment,
                             BufferedStackTrace* free_stack);
 void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
-#if SANITIZER_AMDHSA
 // Double-free of a GPU-only HSA VMEM reservation. Such reservations have no
 // AsanChunk metadata, so provenance is supplied from VmemGpuReserveTracker.
 void ReportVmemDoubleFree(uptr addr, uptr size, u32 reserve_stack_id,
                           u32 reserve_tid, u32 first_free_stack_id,
                           u32 first_free_tid, BufferedStackTrace* free_stack);
-#endif  // SANITIZER_AMDHSA
 void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
 void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
                              AllocType alloc_type,
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
index 3d7e1876a3c69..26821f359c148 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h
@@ -81,6 +81,9 @@ struct NoOpMapUnmapCallback {
 #include "sanitizer_allocator_local_cache.h"
 #include "sanitizer_allocator_secondary.h"
 #include "sanitizer_allocator_device.h"
+#if SANITIZER_AMDHSA
+#include "sanitizer_allocator_amdgpu.h"
+#endif
 #include "sanitizer_allocator_combined.h"
 #include "sanitizer_allocator_combined_device.h"
 // clang-format on
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
index f3591d3fc3a82..5d119e961bd29 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_amdgpu.cpp
@@ -9,6 +9,8 @@
 // Part of the Sanitizer Allocator.
 //
 //===----------------------------------------------------------------------===//
+#include "sanitizer_platform.h"
+
 #if SANITIZER_AMDHSA
 #  include <dlfcn.h>  // For dlopen, dlsym
 
@@ -16,7 +18,6 @@
 #  include "sanitizer_atomic.h"
 
 namespace __sanitizer {
-#  include "sanitizer_allocator_amdgpu.h"
 
 struct HsaFunctions {
   // -------------- Memory Allocate/Deallocate Functions ----------------
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
index 2fb689916d62d..b0c3ff858cd70 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
@@ -321,8 +321,15 @@
 #  define SANITIZER_AMDGPU 0
 #endif
 
+// Host support for the AMD ROCm/HSA runtime (HSA interceptors + AMDGPU device
+// allocator tier). Enabled by default on Linux hosts; inert at runtime unless
+// the HSA runtime is dlopen'd.
 #ifndef SANITIZER_AMDHSA
-#  define SANITIZER_AMDHSA 0
+#  if SANITIZER_LINUX && !SANITIZER_ANDROID
+#    define SANITIZER_AMDHSA 1
+#  else
+#    define SANITIZER_AMDHSA 0
+#  endif
 #endif
 
 #if defined(__NVPTX__)
diff --git a/compiler-rt/test/CMakeLists.txt b/compiler-rt/test/CMakeLists.txt
index c885e1fb0e849..3fab82518e75f 100644
--- a/compiler-rt/test/CMakeLists.txt
+++ b/compiler-rt/test/CMakeLists.txt
@@ -16,8 +16,6 @@ pythonize_bool(COMPILER_RT_HAS_AARCH64_SME)
 
 pythonize_bool(COMPILER_RT_HAS_NO_DEFAULT_CONFIG_FLAG)
 
-pythonize_bool(SANITIZER_AMDHSA)
-
 if(LLVM_TREE_AVAILABLE OR NOT COMPILER_RT_STANDALONE_BUILD)
   set(COMPILER_RT_BUILT_WITH_LLVM TRUE)
 else()
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
index 1e537c6b5e504..ce7e0cc6aee20 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_attach_heap_oob.cpp
@@ -12,7 +12,7 @@
 // enabled (best-effort hsa_amd_agents_allow_access) or the fault can be SIGSEGV
 // instead of AddressSanitizer.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
index f5a074e8669fc..308bba86d0c3c 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_ipc_memory_roundtrip.cpp
@@ -11,7 +11,7 @@
 // Coarse-grained device memory is often not mapped for CPU stores; do not
 // read/write *mapped from the host. Validate with hsa_amd_pointer_info instead.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
index f78aca309d70b..ccd9cf82fc867 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_async_copy_overlap.cpp
@@ -5,7 +5,7 @@
 // Regression test for the AddressSanitizer hsa_amd_memory_async_copy interceptor:
 // invalid overlapping ranges are diagnosed (same family of checks as memcpy).
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
index b5260006f03fc..1c7aa5fdbc559 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_double_free.cpp
@@ -6,7 +6,7 @@
 // hsa_amd_memory_pool_free interceptors: freeing the same pool allocation
 // twice is diagnosed as double-free.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
index 5a6755418bc66..1a9079b620e42 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_memory_pool_allocate_uaf.cpp
@@ -5,7 +5,7 @@
 // Regression test for the AddressSanitizer hsa_amd_memory_pool_allocate /
 // hsa_amd_memory_pool_free interceptors: Using the same freed pool allocation twice is diagnosed as use-after-free.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
index 25fe80417fccc..b94645605b5ac 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_memory_pool.cpp
@@ -6,7 +6,7 @@
 // hsa_amd_memory_pool_allocate pointers: reported sizeInBytes matches the user
 // request (ASan unwraps the page-sized host wrapper from pointer metadata).
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
index b7cd5800f931e..4f82c7aec5da1 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_pointer_info_vmem_reserve_align.cpp
@@ -6,7 +6,7 @@
 // user-visible base and size, not the internal HSA backing mapping. A fixed
 // +page offset is wrong when alignment is larger than the page size.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm, hsa-vmem
+// REQUIRES: linux, stable-runtime, rocm, hsa-vmem
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
index cdb33cb73d998..15d69874522f9 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_double_free.cpp
@@ -5,7 +5,7 @@
 // Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
 // hsa_amd_vmem_address_free interceptors: Using the same freed reserved range twice is diagnosed as double-free.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm, hsa-vmem
+// REQUIRES: linux, stable-runtime, rocm, hsa-vmem
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
index 9f84ee490b233..0a84e5d080700 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_amd_vmem_address_reserve_align_uaf.cpp
@@ -5,7 +5,7 @@
 // Regression test for the AddressSanitizer hsa_amd_vmem_address_reserve_align /
 // hsa_amd_vmem_address_free interceptors: Using the same freed reserved range is diagnosed as use-after-free.
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm, hsa-vmem
+// REQUIRES: linux, stable-runtime, rocm, hsa-vmem
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
index 78a7c2d8832a8..746fc03c5fa10 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/hsa_memory_copy_overlap.cpp
@@ -5,7 +5,7 @@
 // Regression test for the AddressSanitizer hsa_memory_copy interceptor: invalid
 // overlapping ranges are diagnosed (same family of checks as memcpy).
 //
-// REQUIRES: sanitizer-amdgpu, linux, stable-runtime, rocm
+// REQUIRES: linux, stable-runtime, rocm
 // UNSUPPORTED: android
 
 #include "hsa_amd_test_helpers.h"
diff --git a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
index ef0a5eb933fd0..7fac14321469a 100644
--- a/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
+++ b/compiler-rt/test/asan/TestCases/AMDGPU/lit.local.cfg.py
@@ -1,7 +1,8 @@
 # Link against ROCm's HSA runtime. Tests under TestCases/AMDGPU run only when
 # lit finds a ROCm install (see lit.local.cfg.py): $ROCM_PATH or /opt/rocm,
-# with include/hsa/hsa.h and libhsa-runtime64. Compiler-rt must be built with
-# SANITIZER_AMDHSA enabled. The suite uses the dynamic ASan runtime only.
+# with include/hsa/hsa.h and libhsa-runtime64. Host ASan is built with HSA
+# support by default on Linux (SANITIZER_AMDHSA in sanitizer_platform.h). The
+# suite uses the dynamic ASan runtime only.
 
 import glob
 import os
diff --git a/compiler-rt/test/lit.common.cfg.py b/compiler-rt/test/lit.common.cfg.py
index 4821178500f49..12446b4708d20 100644
--- a/compiler-rt/test/lit.common.cfg.py
+++ b/compiler-rt/test/lit.common.cfg.py
@@ -562,9 +562,6 @@ def get_ios_commands_dir():
 if config.gwp_asan:
     config.available_features.add("gwp_asan")
 
-if getattr(config, "sanitizer_amdgpu", False):
-    config.available_features.add("sanitizer-amdgpu")
-
 lit.util.usePlatformSdkOnDarwin(config, lit_config)
 
 min_macos_deployment_target_substitutions = [
diff --git a/compiler-rt/test/lit.common.configured.in b/compiler-rt/test/lit.common.configured.in
index 186041654cce5..cad956aedf94a 100644
--- a/compiler-rt/test/lit.common.configured.in
+++ b/compiler-rt/test/lit.common.configured.in
@@ -26,7 +26,6 @@ set_default("clang", "@COMPILER_RT_RESOLVED_TEST_COMPILER@")
 set_default("compiler_id", "@COMPILER_RT_TEST_COMPILER_ID@")
 set_default("python_executable", "@Python3_EXECUTABLE@")
 set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@)
-set_default("sanitizer_amdgpu", @SANITIZER_AMDHSA_PYBOOL@)
 set_default("compiler_rt_intercept_libdispatch", @COMPILER_RT_INTERCEPT_LIBDISPATCH_PYBOOL@)
 set_default("compiler_rt_output_dir", "@COMPILER_RT_RESOLVED_OUTPUT_DIR@")
 set_default("compiler_rt_bindir", "@COMPILER_RT_RESOLVED_EXEC_OUTPUT_DIR@")



More information about the llvm-commits mailing list