[compiler-rt] scudo: Support free_sized and free_aligned_sized from C23 (PR #146556)

Justin King via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 1 08:51:29 PDT 2025


https://github.com/jcking created https://github.com/llvm/llvm-project/pull/146556

None

>From 48f356fc8b00fd8567f145133b65d323f86cfee8 Mon Sep 17 00:00:00 2001
From: Justin King <jcking at google.com>
Date: Tue, 17 Jun 2025 10:52:16 -0700
Subject: [PATCH] scudo: Support free_sized and free_aligned_sized from C23

Signed-off-by: Justin King <jcking at google.com>
---
 compiler-rt/lib/scudo/standalone/chunk.h      |  2 +-
 compiler-rt/lib/scudo/standalone/combined.h   | 44 ++++++++++++++-----
 compiler-rt/lib/scudo/standalone/options.h    |  2 +
 compiler-rt/lib/scudo/standalone/report.cpp   | 13 ++++++
 compiler-rt/lib/scudo/standalone/report.h     |  5 ++-
 .../lib/scudo/standalone/wrappers_c.inc       | 24 +++++++---
 .../lib/scudo/standalone/wrappers_cpp.cpp     | 28 ++++++------
 7 files changed, 84 insertions(+), 34 deletions(-)

diff --git a/compiler-rt/lib/scudo/standalone/chunk.h b/compiler-rt/lib/scudo/standalone/chunk.h
index 9da2dc57e71a1..f64c1eb93127c 100644
--- a/compiler-rt/lib/scudo/standalone/chunk.h
+++ b/compiler-rt/lib/scudo/standalone/chunk.h
@@ -56,7 +56,7 @@ enum Origin : u8 {
   Malloc = 0,
   New = 1,
   NewArray = 2,
-  Memalign = 3,
+  AlignedAlloc = 3,
 };
 
 enum State : u8 { Available = 0, Allocated = 1, Quarantined = 2 };
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 87acdec2a3bac..880b9b51d8b64 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -433,7 +433,9 @@ class Allocator {
   }
 
   NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin, uptr DeleteSize = 0,
-                           UNUSED uptr Alignment = MinAlignment) {
+                           bool HasDeleteSize = false,
+                           uptr DeleteAlignment = MinAlignment,
+                           bool HasDeleteAlignment = false) {
     if (UNLIKELY(!Ptr))
       return;
 
@@ -456,7 +458,12 @@ class Allocator {
     }
 #endif // GWP_ASAN_HOOKS
 
-    if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment)))
+    if (UNLIKELY(HasDeleteAlignment && !isPowerOfTwo(DeleteAlignment)))
+      reportAlignmentNotPowerOfTwo(DeleteAlignment);
+
+    if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), HasDeleteAlignment
+                                                             ? DeleteAlignment
+                                                             : MinAlignment)))
       reportMisalignedPointer(AllocatorAction::Deallocating, Ptr);
 
     void *TaggedPtr = Ptr;
@@ -471,19 +478,31 @@ class Allocator {
     const Options Options = Primary.Options.load();
     if (Options.get(OptionBit::DeallocTypeMismatch)) {
       if (UNLIKELY(Header.OriginOrWasZeroed != Origin)) {
-        // With the exception of memalign'd chunks, that can be still be free'd.
-        if (Header.OriginOrWasZeroed != Chunk::Origin::Memalign ||
-            Origin != Chunk::Origin::Malloc)
+        // With the exception of aligned_alloc'd chunks, which can be free'd or
+        // free_aligned_sized'd but not free_sized'd.
+        if (UNLIKELY(Header.OriginOrWasZeroed != Chunk::Origin::AlignedAlloc ||
+                     Origin != Chunk::Origin::Malloc || !HasDeleteSize))
           reportDeallocTypeMismatch(AllocatorAction::Deallocating, Ptr,
                                     Header.OriginOrWasZeroed, Origin);
       }
     }
 
     const uptr Size = getSize(Ptr, &Header);
-    if (DeleteSize && Options.get(OptionBit::DeleteSizeMismatch)) {
+    if ((Origin == Chunk::Origin::New || Origin == Chunk::Origin::NewArray) &&
+        DeleteSize && Options.get(OptionBit::DeleteSizeMismatch)) {
       if (UNLIKELY(DeleteSize != Size))
         reportDeleteSizeMismatch(Ptr, DeleteSize, Size);
     }
+    if (Origin == Chunk::Origin::Malloc && HasDeleteSize &&
+        Options.get(OptionBit::FreeSizeMismatch)) {
+      if (UNLIKELY(DeleteSize != Size))
+        reportFreeSizeMismatch(Ptr, DeleteSize, Size);
+    }
+    if (Origin == Chunk::Origin::AlignedAlloc && HasDeleteAlignment &&
+        Options.get(OptionBit::FreeAlignmentMismatch)) {
+      if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), DeleteAlignment)))
+        reportFreeAlignmentMismatch(Ptr, DeleteAlignment);
+    }
 
     quarantineOrDeallocateChunk(Options, TaggedPtr, &Header, Size);
   }
@@ -520,7 +539,7 @@ class Allocator {
     void *OldTaggedPtr = OldPtr;
     OldPtr = getHeaderTaggedPointer(OldPtr);
 
-    if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(OldPtr), MinAlignment)))
+    if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(OldPtr), Alignment)))
       reportMisalignedPointer(AllocatorAction::Reallocating, OldPtr);
 
     Chunk::UnpackedHeader Header;
@@ -529,11 +548,14 @@ class Allocator {
     if (UNLIKELY(Header.State != Chunk::State::Allocated))
       reportInvalidChunkState(AllocatorAction::Reallocating, OldPtr);
 
-    // Pointer has to be allocated with a malloc-type function. Some
-    // applications think that it is OK to realloc a memalign'ed pointer, which
-    // will trigger this check. It really isn't.
     if (Options.get(OptionBit::DeallocTypeMismatch)) {
-      if (UNLIKELY(Header.OriginOrWasZeroed != Chunk::Origin::Malloc))
+      // There is no language prohibiting the use of realloc with
+      // aligned_alloc/posix_memalign/memalign and etc. However there is no
+      // guarantee that the allocator being used with malloc is the same as
+      // operator new. There is also no guarantee that they share the same
+      // minimum alignment guarantees. So we reject these.
+      if (UNLIKELY(Header.OriginOrWasZeroed == Chunk::Origin::New ||
+                   Header.OriginOrWasZeroed == Chunk::Origin::NewArray))
         reportDeallocTypeMismatch(AllocatorAction::Reallocating, OldPtr,
                                   Header.OriginOrWasZeroed,
                                   Chunk::Origin::Malloc);
diff --git a/compiler-rt/lib/scudo/standalone/options.h b/compiler-rt/lib/scudo/standalone/options.h
index b20142a415903..b8b926ccff315 100644
--- a/compiler-rt/lib/scudo/standalone/options.h
+++ b/compiler-rt/lib/scudo/standalone/options.h
@@ -25,6 +25,8 @@ enum class OptionBit {
   UseOddEvenTags,
   UseMemoryTagging,
   AddLargeAllocationSlack,
+  FreeSizeMismatch,
+  FreeAlignmentMismatch,
 };
 
 struct Options {
diff --git a/compiler-rt/lib/scudo/standalone/report.cpp b/compiler-rt/lib/scudo/standalone/report.cpp
index b97a74b078c2f..7fc219f5d5dc8 100644
--- a/compiler-rt/lib/scudo/standalone/report.cpp
+++ b/compiler-rt/lib/scudo/standalone/report.cpp
@@ -161,6 +161,19 @@ void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
       Size, ExpectedSize);
 }
 
+void NORETURN reportFreeSizeMismatch(const void *Ptr, uptr Size, uptr ExpectedSize) {
+  ScopedErrorReport Report;
+  Report.append(
+      "invalid sized free when deallocating address %p (%zu vs %zu)\n", Ptr,
+      Size, ExpectedSize);
+}
+
+void NORETURN reportFreeAlignmentMismatch(const void *Ptr, uptr Alignment) {
+  ScopedErrorReport Report;
+  Report.append("invalid aligned free when deallocating address %p (%zu)\n",
+                Ptr, Alignment);
+}
+
 void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) {
   ScopedErrorReport Report;
   Report.append(
diff --git a/compiler-rt/lib/scudo/standalone/report.h b/compiler-rt/lib/scudo/standalone/report.h
index c397dd3fc9c65..6a8d9ccfed831 100644
--- a/compiler-rt/lib/scudo/standalone/report.h
+++ b/compiler-rt/lib/scudo/standalone/report.h
@@ -45,8 +45,9 @@ void NORETURN reportInvalidChunkState(AllocatorAction Action, const void *Ptr);
 void NORETURN reportMisalignedPointer(AllocatorAction Action, const void *Ptr);
 void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, const void *Ptr,
                                         u8 TypeA, u8 TypeB);
-void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
-                                       uptr ExpectedSize);
+void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size, uptr ExpectedSize);
+void NORETURN reportFreeSizeMismatch(const void *Ptr, uptr Size, uptr ExpectedSize);
+void NORETURN reportFreeAlignmentMismatch(const void *Ptr, uptr Alignment);
 
 // C wrappers errors.
 void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment);
diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c.inc b/compiler-rt/lib/scudo/standalone/wrappers_c.inc
index 59f3fb0962f8b..714160220974a 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c.inc
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c.inc
@@ -68,6 +68,18 @@ INTERFACE WEAK void SCUDO_PREFIX(free)(void *ptr) {
   SCUDO_ALLOCATOR.deallocate(ptr, scudo::Chunk::Origin::Malloc);
 }
 
+INTERFACE WEAK void SCUDO_PREFIX(free_sized)(void *ptr, size_t size) {
+  reportDeallocation(ptr);
+  SCUDO_ALLOCATOR.deallocate(ptr, scudo::Chunk::Origin::Malloc, size, true);
+}
+
+INTERFACE WEAK void
+SCUDO_PREFIX(free_aligned_sized)(void *ptr, size_t alignment, size_t size) {
+  reportDeallocation(ptr);
+  SCUDO_ALLOCATOR.deallocate(ptr, scudo::Chunk::Origin::AlignedAlloc, size,
+                             true, alignment, true);
+}
+
 INTERFACE WEAK struct SCUDO_MALLINFO SCUDO_PREFIX(mallinfo)(void) {
   struct SCUDO_MALLINFO Info = {};
   scudo::StatCounters Stats;
@@ -140,7 +152,7 @@ INTERFACE WEAK void *SCUDO_PREFIX(memalign)(size_t alignment, size_t size) {
     }
   }
   void *Ptr =
-      SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment);
+      SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, alignment);
   reportAllocation(Ptr, size);
   return Ptr;
 }
@@ -153,7 +165,7 @@ INTERFACE WEAK int SCUDO_PREFIX(posix_memalign)(void **memptr, size_t alignment,
     return EINVAL;
   }
   void *Ptr =
-      SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment);
+      SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, alignment);
   if (UNLIKELY(!Ptr))
     return ENOMEM;
   reportAllocation(Ptr, size);
@@ -174,7 +186,7 @@ INTERFACE WEAK void *SCUDO_PREFIX(pvalloc)(size_t size) {
   // pvalloc(0) should allocate one page.
   void *Ptr =
       SCUDO_ALLOCATOR.allocate(size ? scudo::roundUp(size, PageSize) : PageSize,
-                               scudo::Chunk::Origin::Memalign, PageSize);
+                               scudo::Chunk::Origin::Malloc, PageSize);
   reportAllocation(Ptr, scudo::roundUp(size, PageSize));
 
   return scudo::setErrnoOnNull(Ptr);
@@ -218,7 +230,7 @@ INTERFACE WEAK void *SCUDO_PREFIX(realloc)(void *ptr, size_t size) {
 }
 
 INTERFACE WEAK void *SCUDO_PREFIX(valloc)(size_t size) {
-  void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign,
+  void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc,
                                        scudo::getPageSizeCached());
   reportAllocation(Ptr, size);
 
@@ -307,8 +319,8 @@ INTERFACE WEAK void *SCUDO_PREFIX(aligned_alloc)(size_t alignment,
     scudo::reportInvalidAlignedAllocAlignment(alignment, size);
   }
 
-  void *Ptr =
-      SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, alignment);
+  void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::AlignedAlloc,
+                                       alignment);
   reportAllocation(Ptr, size);
 
   return scudo::setErrnoOnNull(Ptr);
diff --git a/compiler-rt/lib/scudo/standalone/wrappers_cpp.cpp b/compiler-rt/lib/scudo/standalone/wrappers_cpp.cpp
index 098d4f71acc4a..ac51082d10120 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_cpp.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_cpp.cpp
@@ -104,47 +104,47 @@ INTERFACE WEAK void operator delete[](void *ptr,
 }
 INTERFACE WEAK void operator delete(void *ptr, size_t size) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, size);
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, size, true);
 }
 INTERFACE WEAK void operator delete[](void *ptr, size_t size) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, size);
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, size, true);
 }
 INTERFACE WEAK void operator delete(void *ptr,
                                     std::align_val_t align) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, 0,
-                       static_cast<scudo::uptr>(align));
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, 0, false,
+                       static_cast<scudo::uptr>(align), true);
 }
 INTERFACE WEAK void operator delete[](void *ptr,
                                       std::align_val_t align) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, 0,
-                       static_cast<scudo::uptr>(align));
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, 0, false,
+                       static_cast<scudo::uptr>(align), true);
 }
 INTERFACE WEAK void operator delete(void *ptr, std::align_val_t align,
                                     std::nothrow_t const &) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, 0,
-                       static_cast<scudo::uptr>(align));
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, 0, false,
+                       static_cast<scudo::uptr>(align), true);
 }
 INTERFACE WEAK void operator delete[](void *ptr, std::align_val_t align,
                                       std::nothrow_t const &) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, 0,
-                       static_cast<scudo::uptr>(align));
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, 0, false,
+                       static_cast<scudo::uptr>(align), true);
 }
 INTERFACE WEAK void operator delete(void *ptr, size_t size,
                                     std::align_val_t align) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, size,
-                       static_cast<scudo::uptr>(align));
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::New, size, true,
+                       static_cast<scudo::uptr>(align), true);
 }
 INTERFACE WEAK void operator delete[](void *ptr, size_t size,
                                       std::align_val_t align) NOEXCEPT {
   reportDeallocation(ptr);
-  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, size,
-                       static_cast<scudo::uptr>(align));
+  Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, size, true,
+                       static_cast<scudo::uptr>(align), true);
 }
 
 #endif // !SCUDO_ANDROID || !_BIONIC



More information about the llvm-commits mailing list