[compiler-rt] [ASan][Windows] Fix false positive for zero sized rtl allocations (PR #181015)

Zack Johnson via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 13 12:31:41 PST 2026


https://github.com/zacklj89 updated https://github.com/llvm/llvm-project/pull/181015

>From 79c281a4b8c9a772fae5e2b76366a6c2f1ab24cb Mon Sep 17 00:00:00 2001
From: Zack Johnson <zajohnson at microsoft.com>
Date: Wed, 11 Feb 2026 15:59:20 -0500
Subject: [PATCH 1/4] [compiler-rt][ASan][Windows] Fix false positive for zero
 sized rtl allocations

---
 compiler-rt/lib/asan/asan_allocator.cpp       |  44 +++++++
 .../TestCases/Windows/rtlsizeheap_zero.cpp    | 107 ++++++++++++++++++
 2 files changed, 151 insertions(+)
 create mode 100644 compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 05ae3a430cabd..1b314f6879529 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -93,6 +93,11 @@ class ChunkHeader {
   atomic_uint8_t chunk_state;
   u8 alloc_type : 2;
   u8 lsan_tag : 2;
+#if SANITIZER_WINDOWS
+  // True if this was a zero-size allocation upgraded to size 1.
+  // Used to report the original size (0) to the user via HeapSize/RtlSizeHeap.
+  u8 from_zero_alloc : 1;
+#endif
 
   // align < 8 -> 0
   // else      -> log2(min(align, 512)) - 2
@@ -610,6 +615,9 @@ struct Allocator {
     uptr chunk_beg = user_beg - kChunkHeaderSize;
     AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
     m->alloc_type = alloc_type;
+#if SANITIZER_WINDOWS
+    m->from_zero_alloc = upgraded_from_zero;
+#endif
     CHECK(size);
     m->SetUsedSize(size);
     m->user_requested_alignment_log = user_requested_alignment_log;
@@ -863,8 +871,22 @@ struct Allocator {
     return m->UsedSize();
   }
 
+#if SANITIZER_WINDOWS
+  // Returns true if the allocation at p was a zero-size request that was
+  // internally upgraded to size 1.
+  bool FromZeroAllocation(uptr p) {
+    return reinterpret_cast<AsanChunk *>(p - kChunkHeaderSize)->from_zero_alloc;
+  }
+#endif
+
   uptr AllocationSizeFast(uptr p) {
+#if SANITIZER_WINDOWS
+    AsanChunk *c = reinterpret_cast<AsanChunk *>(p - kChunkHeaderSize);
+    if (c->from_zero_alloc) return 0;
+    return c->UsedSize();
+#else
     return reinterpret_cast<AsanChunk *>(p - kChunkHeaderSize)->UsedSize();
+#endif
   }
 
   AsanChunkView FindHeapChunkByAddress(uptr addr) {
@@ -1125,6 +1147,14 @@ uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
     GET_STACK_TRACE_FATAL(pc, bp);
     ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
   }
+#if SANITIZER_WINDOWS
+  // Zero-size allocations are internally upgraded to size 1, but we should
+  // report the originally requested size (0) to the user via
+  // HeapSize/RtlSizeHeap.
+  if (usable_size > 0 &&
+      instance.FromZeroAllocation(reinterpret_cast<uptr>(ptr)))
+    return 0;
+#endif
   return usable_size;
 }
 
@@ -1222,7 +1252,14 @@ void asan_delete_array_sized_aligned(void *ptr, uptr size, uptr alignment,
 }
 
 uptr asan_mz_size(const void *ptr) {
+#if SANITIZER_WINDOWS
+  uptr size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+  if (size > 0 && instance.FromZeroAllocation(reinterpret_cast<uptr>(ptr)))
+    return 0;
+  return size;
+#else
   return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+#endif
 }
 
 void asan_mz_force_lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
@@ -1366,6 +1403,13 @@ uptr __sanitizer_get_allocated_size(const void *p) {
     GET_STACK_TRACE_FATAL_HERE;
     ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
   }
+#if SANITIZER_WINDOWS
+  // Zero-size allocations are internally upgraded to size 1, but report
+  // the originally requested size (0) to the user via
+  // HeapSize/RtlSizeHeap.
+  if (instance.FromZeroAllocation(ptr))
+    return 0;
+#endif
   return allocated_size;
 }
 
diff --git a/compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp b/compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp
new file mode 100644
index 0000000000000..741275c6cac02
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp
@@ -0,0 +1,107 @@
+// RUN: %clang_cl_asan %s %Fe%t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %clang_cl_asan %s %Fe%t /DFAIL_CHECK
+// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+//
+// Verify that zero-size heap allocations report size 0 through Windows heap
+// size APIs, preventing false positives when the result is used with memset.
+
+#include <malloc.h>
+#include <stdio.h>
+#include <windows.h>
+
+using AllocateFunctionPtr = PVOID(__stdcall *)(PVOID, ULONG, SIZE_T);
+using FreeFunctionPtr = BOOL(__stdcall *)(PVOID, ULONG, PVOID);
+using SizeFunctionPtr = SIZE_T(__stdcall *)(PVOID, ULONG, PVOID);
+
+int main() {
+  HMODULE NtDllHandle = GetModuleHandle("ntdll.dll");
+  if (!NtDllHandle) {
+    puts("Couldn't load ntdll");
+    return -1;
+  }
+
+  auto RtlAllocateHeap_ptr =
+      (AllocateFunctionPtr)GetProcAddress(NtDllHandle, "RtlAllocateHeap");
+  auto RtlFreeHeap_ptr =
+      (FreeFunctionPtr)GetProcAddress(NtDllHandle, "RtlFreeHeap");
+  auto RtlSizeHeap_ptr =
+      (SizeFunctionPtr)GetProcAddress(NtDllHandle, "RtlSizeHeap");
+
+  if (!RtlAllocateHeap_ptr || !RtlFreeHeap_ptr || !RtlSizeHeap_ptr) {
+    puts("Couldn't find Rtl heap functions");
+    return -1;
+  }
+
+  // Test RtlAllocateHeap with zero size
+  {
+    char *buffer =
+        (char *)RtlAllocateHeap_ptr(GetProcessHeap(), HEAP_ZERO_MEMORY, 0);
+    if (buffer) {
+      auto size = RtlSizeHeap_ptr(GetProcessHeap(), 0, buffer);
+      memset(buffer, 0, size);
+#ifdef FAIL_CHECK
+      // heap-buffer-overflow since actual size is 0
+      memset(buffer, 0, 1);
+#endif
+      RtlFreeHeap_ptr(GetProcessHeap(), 0, buffer);
+    }
+  }
+
+  // Test malloc with zero size
+  {
+    char *buffer = (char *)malloc(0);
+    if (buffer) {
+      auto size = _msize(buffer);
+      auto rtl_size = RtlSizeHeap_ptr(GetProcessHeap(), 0, buffer);
+      memset(buffer, 0, size);
+      memset(buffer, 0, rtl_size);
+      free(buffer);
+    }
+  }
+
+  // Test operator new with zero size
+  {
+    char *buffer = new char[0];
+    auto size = _msize(buffer);
+    auto rtl_size = RtlSizeHeap_ptr(GetProcessHeap(), 0, buffer);
+    memset(buffer, 0, size);
+    memset(buffer, 0, rtl_size);
+    delete[] buffer;
+  }
+
+  // Test GlobalAlloc with zero size.
+  // GlobalAlloc calls RtlAllocateHeap internally.
+  {
+    HGLOBAL hMem = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, 0);
+    if (hMem) {
+      char *buffer = (char *)hMem;
+      auto size = GlobalSize(hMem);
+      auto rtl_size = RtlSizeHeap_ptr(GetProcessHeap(), 0, buffer);
+      memset(buffer, 0, size);
+      memset(buffer, 0, rtl_size);
+      GlobalFree(hMem);
+    }
+  }
+
+  // Test LocalAlloc with zero size.
+  // LocalAlloc calls RtlAllocateHeap internally.
+  {
+    HLOCAL hMem = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, 0);
+    if (hMem) {
+      char *buffer = (char *)hMem;
+      auto size = LocalSize(hMem);
+      auto rtl_size = RtlSizeHeap_ptr(GetProcessHeap(), 0, buffer);
+      memset(buffer, 0, size);
+      memset(buffer, 0, rtl_size);
+      LocalFree(hMem);
+    }
+  }
+
+  puts("Success");
+  return 0;
+}
+
+// CHECK: Success
+// CHECK-NOT: AddressSanitizer: heap-buffer-overflow
+// CHECK-FAIL: AddressSanitizer: heap-buffer-overflow

>From a81af46ba5164f783d85bb8700070a50a5e72cf3 Mon Sep 17 00:00:00 2001
From: Zack Johnson <zajohnson at microsoft.com>
Date: Thu, 12 Feb 2026 10:00:29 -0500
Subject: [PATCH 2/4] PR Feedback + fixing tests

---
 compiler-rt/lib/asan/asan_allocator.cpp       | 40 ++++++++++---------
 compiler-rt/lib/asan/asan_malloc_win.cpp      | 14 ++++++-
 .../Windows/heaprealloc_alloc_zero.cpp        | 27 ++++---------
 .../TestCases/Windows/rtlsizeheap_zero.cpp    |  4 +-
 4 files changed, 43 insertions(+), 42 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index 1b314f6879529..e2f2872e2d713 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -875,18 +875,21 @@ struct Allocator {
   // Returns true if the allocation at p was a zero-size request that was
   // internally upgraded to size 1.
   bool FromZeroAllocation(uptr p) {
-    return reinterpret_cast<AsanChunk *>(p - kChunkHeaderSize)->from_zero_alloc;
+    return reinterpret_cast<AsanChunk*>(p - kChunkHeaderSize)->from_zero_alloc;
+  }
+
+  // Marks an existing size 1 allocation as having originally been zero-size.
+  // Used by SharedReAlloc which augments size 0 to 1 before calling
+  // asan_realloc, bypassing Allocate's own zero-size tracking.
+  void MarkAsZeroAllocation(uptr p) {
+    AsanChunk* m = reinterpret_cast<AsanChunk*>(p - kChunkHeaderSize);
+    m->from_zero_alloc = 1;
+    PoisonShadow(p, ASAN_SHADOW_GRANULARITY, kAsanHeapLeftRedzoneMagic);
   }
 #endif
 
   uptr AllocationSizeFast(uptr p) {
-#if SANITIZER_WINDOWS
-    AsanChunk *c = reinterpret_cast<AsanChunk *>(p - kChunkHeaderSize);
-    if (c->from_zero_alloc) return 0;
-    return c->UsedSize();
-#else
     return reinterpret_cast<AsanChunk *>(p - kChunkHeaderSize)->UsedSize();
-#endif
   }
 
   AsanChunkView FindHeapChunkByAddress(uptr addr) {
@@ -1251,17 +1254,23 @@ void asan_delete_array_sized_aligned(void *ptr, uptr size, uptr alignment,
   asan_delete_sized_aligned(ptr, size, alignment, stack, /*array=*/true);
 }
 
-uptr asan_mz_size(const void *ptr) {
-#if SANITIZER_WINDOWS
+uptr asan_mz_size(const void* ptr) {
   uptr size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+
+#if SANITIZER_WINDOWS
   if (size > 0 && instance.FromZeroAllocation(reinterpret_cast<uptr>(ptr)))
     return 0;
-  return size;
-#else
-  return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
 #endif
+
+  return size;
 }
 
+#if SANITIZER_WINDOWS
+void asan_mark_zero_allocation(void* ptr) {
+  instance.MarkAsZeroAllocation(reinterpret_cast<uptr>(ptr));
+}
+#endif
+
 void asan_mz_force_lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
   instance.ForceLock();
 }
@@ -1403,13 +1412,6 @@ uptr __sanitizer_get_allocated_size(const void *p) {
     GET_STACK_TRACE_FATAL_HERE;
     ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
   }
-#if SANITIZER_WINDOWS
-  // Zero-size allocations are internally upgraded to size 1, but report
-  // the originally requested size (0) to the user via
-  // HeapSize/RtlSizeHeap.
-  if (instance.FromZeroAllocation(ptr))
-    return 0;
-#endif
   return allocated_size;
 }
 
diff --git a/compiler-rt/lib/asan/asan_malloc_win.cpp b/compiler-rt/lib/asan/asan_malloc_win.cpp
index ea6f7dfaa08cf..0a556c90220ec 100644
--- a/compiler-rt/lib/asan/asan_malloc_win.cpp
+++ b/compiler-rt/lib/asan/asan_malloc_win.cpp
@@ -53,6 +53,14 @@ BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
 
 using namespace __asan;
 
+#  if SANITIZER_WINDOWS
+// Marks an allocation as originally zero-size. Must be called on allocations
+// that were changed from size 0 to 1 outside of Allocate() (e.g. SharedReAlloc).
+namespace __asan {
+void asan_mark_zero_allocation(void *ptr);
+}
+#  endif
+
 // MT: Simply defining functions with the same signature in *.obj
 // files overrides the standard functions in the CRT.
 // MD: Memory allocation functions are defined in the CRT .dll,
@@ -371,7 +379,8 @@ void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
   // passing a 0 size into asan_realloc will free the allocation.
   // To avoid this and keep behavior consistent, fudge the size if 0.
   // (asan_malloc already does this)
-  if (dwBytes == 0)
+  bool was_zero_size = (dwBytes == 0);
+  if (was_zero_size)
     dwBytes = 1;
 
   size_t old_size;
@@ -382,6 +391,9 @@ void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
   if (ptr == nullptr)
     return nullptr;
 
+  if (was_zero_size)
+    asan_mark_zero_allocation(ptr);
+
   if (dwFlags & HEAP_ZERO_MEMORY) {
     size_t new_size = asan_malloc_usable_size(ptr, pc, bp);
     if (old_size < new_size)
diff --git a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
index 6a5f8a1e7ea09..07b7003c0cbff 100644
--- a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
@@ -10,29 +10,16 @@ int main() {
   if (ptr)
     std::cerr << "allocated!\n";
 
-  // Check the 'allocate 1 instead of 0' hack hasn't changed
-  // Note that as of b3452d90b043a398639e62b0ab01aa339cc649de, dereferencing
-  // the pointer will be detected as a heap-buffer-overflow.
+  // Zero-size allocations are internally upgraded to size 1.
+  // __sanitizer_get_allocated_size reports bytes reserved, not requested.
+  // Dereferencing the pointer will be detected as a heap-buffer-overflow.
   if (__sanitizer_get_allocated_size(ptr) != 1)
     return 1;
 
   free(ptr);
 
-  /*
-        HeapAlloc hack for our asan interceptor is to change 0
-        sized allocations to size 1 to avoid weird inconsistencies
-        between how realloc and heaprealloc handle 0 size allocations.
-
-        Note this test relies on these instructions being intercepted.
-        Without ASAN HeapRealloc on line 27 would return a ptr whose
-        HeapSize would be 0. This test makes sure that the underlying behavior
-        of our hack hasn't changed underneath us.
-
-        We can get rid of the test (or change it to test for the correct
-        behavior) once we fix the interceptor or write a different allocator
-        to handle 0 sized allocations properly by default.
-
-    */
+  // Zero-size HeapAlloc/HeapReAlloc should report size 0 via HeapSize,
+  // matching the originally requested size.
   ptr = HeapAlloc(GetProcessHeap(), 0, 0);
   if (!ptr)
     return 1;
@@ -40,8 +27,8 @@ int main() {
   if (!ptr2)
     return 1;
   size_t heapsize = HeapSize(GetProcessHeap(), 0, ptr2);
-  if (heapsize != 1) { // will be 0 without ASAN turned on
-    std::cerr << "HeapAlloc size failure! " << heapsize << " != 1\n";
+  if (heapsize != 0) {
+    std::cerr << "HeapAlloc size failure! " << heapsize << " != 0\n";
     return 1;
   }
   void *ptr3 = HeapReAlloc(GetProcessHeap(), 0, ptr2, 3);
diff --git a/compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp b/compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp
index 741275c6cac02..4a98a99d2acb1 100644
--- a/compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/rtlsizeheap_zero.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cl_asan %s %Fe%t
-// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %env_asan_opts=windows_hook_rtl_allocators=true %run %t 2>&1 | FileCheck %s
 // RUN: %clang_cl_asan %s %Fe%t /DFAIL_CHECK
-// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: %env_asan_opts=windows_hook_rtl_allocators=true not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
 //
 // Verify that zero-size heap allocations report size 0 through Windows heap
 // size APIs, preventing false positives when the result is used with memset.

>From 389fd52ca5d9effe164b5390614dfacfc77d1e24 Mon Sep 17 00:00:00 2001
From: Zack Johnson <zajohnson at microsoft.com>
Date: Fri, 13 Feb 2026 10:42:46 -0500
Subject: [PATCH 3/4] revert test behavior and clang-format

---
 compiler-rt/lib/asan/asan_malloc_win.cpp      |  5 ++--
 .../Windows/heaprealloc_alloc_zero.cpp        | 23 +++++++++++++++----
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_malloc_win.cpp b/compiler-rt/lib/asan/asan_malloc_win.cpp
index 0a556c90220ec..90ca475f4d94e 100644
--- a/compiler-rt/lib/asan/asan_malloc_win.cpp
+++ b/compiler-rt/lib/asan/asan_malloc_win.cpp
@@ -55,9 +55,10 @@ using namespace __asan;
 
 #  if SANITIZER_WINDOWS
 // Marks an allocation as originally zero-size. Must be called on allocations
-// that were changed from size 0 to 1 outside of Allocate() (e.g. SharedReAlloc).
+// that were changed from size 0 to 1 outside of Allocate() (e.g.
+// SharedReAlloc).
 namespace __asan {
-void asan_mark_zero_allocation(void *ptr);
+void asan_mark_zero_allocation(void* ptr);
 }
 #  endif
 
diff --git a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
index 07b7003c0cbff..685db65af70ce 100644
--- a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
@@ -10,16 +10,29 @@ int main() {
   if (ptr)
     std::cerr << "allocated!\n";
 
-  // Zero-size allocations are internally upgraded to size 1.
-  // __sanitizer_get_allocated_size reports bytes reserved, not requested.
-  // Dereferencing the pointer will be detected as a heap-buffer-overflow.
+  // Check the 'allocate 1 instead of 0' hack hasn't changed
+  // Note that as of b3452d90b043a398639e62b0ab01aa339cc649de, dereferencing
+  // the pointer will be detected as a heap-buffer-overflow.
   if (__sanitizer_get_allocated_size(ptr) != 1)
     return 1;
 
   free(ptr);
 
-  // Zero-size HeapAlloc/HeapReAlloc should report size 0 via HeapSize,
-  // matching the originally requested size.
+  /*
+        HeapAlloc hack for our asan interceptor is to change 0
+        sized allocations to size 1 to avoid weird inconsistencies
+        between how realloc and heaprealloc handle 0 size allocations.
+
+        Note this test relies on these instructions being intercepted.
+        Without ASAN HeapRealloc on line 27 would return a ptr whose
+        HeapSize would be 0. This test makes sure that the underlying behavior
+        of our hack hasn't changed underneath us.
+
+        We can get rid of the test (or change it to test for the correct
+        behavior) once we fix the interceptor or write a different allocator
+        to handle 0 sized allocations properly by default.
+
+    */
   ptr = HeapAlloc(GetProcessHeap(), 0, 0);
   if (!ptr)
     return 1;

>From 9d07a3f93b364865a7649d67645c0a33256aa055 Mon Sep 17 00:00:00 2001
From: Zack Johnson <zajohnson at microsoft.com>
Date: Fri, 13 Feb 2026 15:31:23 -0500
Subject: [PATCH 4/4] PR feedback - DCHECK and comments

---
 compiler-rt/lib/asan/asan_allocator.cpp           | 15 ++++++++++-----
 compiler-rt/lib/asan/asan_malloc_win.cpp          |  2 --
 .../TestCases/Windows/heaprealloc_alloc_zero.cpp  |  1 +
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
index e2f2872e2d713..59aeec6a6ae66 100644
--- a/compiler-rt/lib/asan/asan_allocator.cpp
+++ b/compiler-rt/lib/asan/asan_allocator.cpp
@@ -1151,12 +1151,15 @@ uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
     ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
   }
 #if SANITIZER_WINDOWS
-  // Zero-size allocations are internally upgraded to size 1, but we should
-  // report the originally requested size (0) to the user via
-  // HeapSize/RtlSizeHeap.
+  // Zero-size allocations are internally upgraded to size 1 so that
+  // malloc(0)/new(0) return unique non-NULL pointers as required by the
+  // standard. Windows heap APIs (HeapSize, RtlSizeHeap, _msize) should still
+  // report the originally requested size (0).
   if (usable_size > 0 &&
-      instance.FromZeroAllocation(reinterpret_cast<uptr>(ptr)))
+      instance.FromZeroAllocation(reinterpret_cast<uptr>(ptr))) {
+    DCHECK(usable_size == 1);
     return 0;
+  }
 #endif
   return usable_size;
 }
@@ -1258,8 +1261,10 @@ uptr asan_mz_size(const void* ptr) {
   uptr size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
 
 #if SANITIZER_WINDOWS
-  if (size > 0 && instance.FromZeroAllocation(reinterpret_cast<uptr>(ptr)))
+  if (size > 0 && instance.FromZeroAllocation(reinterpret_cast<uptr>(ptr))) {
+    DCHECK(size == 1);
     return 0;
+  }
 #endif
 
   return size;
diff --git a/compiler-rt/lib/asan/asan_malloc_win.cpp b/compiler-rt/lib/asan/asan_malloc_win.cpp
index 90ca475f4d94e..e728591cc145e 100644
--- a/compiler-rt/lib/asan/asan_malloc_win.cpp
+++ b/compiler-rt/lib/asan/asan_malloc_win.cpp
@@ -53,14 +53,12 @@ BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
 
 using namespace __asan;
 
-#  if SANITIZER_WINDOWS
 // Marks an allocation as originally zero-size. Must be called on allocations
 // that were changed from size 0 to 1 outside of Allocate() (e.g.
 // SharedReAlloc).
 namespace __asan {
 void asan_mark_zero_allocation(void* ptr);
 }
-#  endif
 
 // MT: Simply defining functions with the same signature in *.obj
 // files overrides the standard functions in the CRT.
diff --git a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
index 685db65af70ce..9c9f7ee57a8da 100644
--- a/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
+++ b/compiler-rt/test/asan/TestCases/Windows/heaprealloc_alloc_zero.cpp
@@ -40,6 +40,7 @@ int main() {
   if (!ptr2)
     return 1;
   size_t heapsize = HeapSize(GetProcessHeap(), 0, ptr2);
+  // HeapSize will retrieve the user-defined size, not the ASan-allocated size of 1
   if (heapsize != 0) {
     std::cerr << "HeapAlloc size failure! " << heapsize << " != 0\n";
     return 1;



More information about the llvm-commits mailing list