Skip to content

scudo: Support free_sized and free_aligned_sized from C23 #146556

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jcking
Copy link
Contributor

@jcking jcking commented Jul 1, 2025

Updates scudo to support free_sized and free_aligned_sized from C23.

  • Adds delete_alignment_mismatch and enables it by default. During deallocation we check that the provided pointer is aligned at least to the provided alignment. We do not store the requested alignment, so this is the best we can do.
  • Adds free_size_mismatch and enables it by default. If the provided size to free_sized is different from the requested size, we report an error.
  • Adds free_alignment_mismatch and enables it by default. Same as delete_alignment_mismatch, except for free_aligned_sized.
  • The deallocation function now knows whether size/alignment was explicitly provided, enabling it to differentiate whether 0 was provided by the user.

For #144435

Copy link

github-actions bot commented Jul 1, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@jcking jcking force-pushed the scudo_free_aligned_sized branch 13 times, most recently from 8510f71 to e294041 Compare July 1, 2025 20:10
@jcking jcking force-pushed the scudo_free_aligned_sized branch 13 times, most recently from e274d23 to f69bc1c Compare July 2, 2025 15:34
@jcking jcking marked this pull request as ready for review July 2, 2025 15:40
@llvmbot
Copy link
Member

llvmbot commented Jul 2, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Justin King (jcking)

Changes

Updates scudo to support free_sized and free_aligned_sized from C23.

  • Adds delete_alignment_mismatch and enables it by default. During deallocation we check that the provided pointer is aligned at least to the provided alignment. We do not store the requested alignment, so this is the best we can do.
  • Adds free_size_mismatch and enables it by default. If the provided size to free_sized is different from the requested size, we report an error.
  • Adds free_alignment_mismatch and enables it by default. Same as delete_alignment_mismatch, except for free_aligned_sized.
  • The deallocation function now knows whether size/alignment was explicitly provided, enabling it to differentiate whether 0 was provided by the user.

For #144435


Patch is 36.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/146556.diff

17 Files Affected:

  • (modified) compiler-rt/lib/scudo/standalone/chunk.h (+5-4)
  • (modified) compiler-rt/lib/scudo/standalone/combined.h (+69-18)
  • (modified) compiler-rt/lib/scudo/standalone/flags.inc (+15)
  • (modified) compiler-rt/lib/scudo/standalone/internal_defs.h (+9)
  • (modified) compiler-rt/lib/scudo/standalone/options.h (+3)
  • (modified) compiler-rt/lib/scudo/standalone/report.cpp (+60-3)
  • (modified) compiler-rt/lib/scudo/standalone/report.h (+5-1)
  • (modified) compiler-rt/lib/scudo/standalone/tests/combined_test.cpp (+13-13)
  • (modified) compiler-rt/lib/scudo/standalone/tests/report_test.cpp (+1-1)
  • (modified) compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h (+13)
  • (modified) compiler-rt/lib/scudo/standalone/tests/scudo_unit_test_main.cpp (-8)
  • (modified) compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp (+180)
  • (modified) compiler-rt/lib/scudo/standalone/tests/wrappers_cpp_test.cpp (+3-11)
  • (modified) compiler-rt/lib/scudo/standalone/wrappers_c.inc (+20-8)
  • (modified) compiler-rt/lib/scudo/standalone/wrappers_cpp.cpp (+14-14)
  • (modified) compiler-rt/test/scudo/standalone/unit/gwp_asan/lit.site.cfg.py.in (+1-1)
  • (modified) llvm/docs/ScudoHardenedAllocator.rst (+10)
diff --git a/compiler-rt/lib/scudo/standalone/chunk.h b/compiler-rt/lib/scudo/standalone/chunk.h
index 9da2dc57e71a1..c0b90a206c076 100644
--- a/compiler-rt/lib/scudo/standalone/chunk.h
+++ b/compiler-rt/lib/scudo/standalone/chunk.h
@@ -53,10 +53,11 @@ namespace Chunk {
 // but https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414 prevents it from
 // happening, as it will error, complaining the number of bits is not enough.
 enum Origin : u8 {
-  Malloc = 0,
-  New = 1,
-  NewArray = 2,
-  Memalign = 3,
+  Malloc = 0,       // malloc, calloc, realloc, free, free_sized
+  New = 1,          // operator new, operator delete
+  NewArray = 2,     // operator new [], operator delete []
+  AlignedAlloc = 3, // aligned_alloc, posix_memalign, memalign, pvalloc, valloc,
+                    // free_aligned_sized
 };
 
 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..8d38f26de9375 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -170,6 +170,12 @@ class Allocator {
       Primary.Options.set(OptionBit::DeallocTypeMismatch);
     if (getFlags()->delete_size_mismatch)
       Primary.Options.set(OptionBit::DeleteSizeMismatch);
+    if (getFlags()->free_size_mismatch)
+      Primary.Options.set(OptionBit::FreeSizeMismatch);
+    if (getFlags()->free_alignment_mismatch)
+      Primary.Options.set(OptionBit::FreeAlignmentMismatch);
+    if (getFlags()->delete_alignment_mismatch)
+      Primary.Options.set(OptionBit::DeleteAlignmentMismatch);
     if (allocatorSupportsMemoryTagging<AllocatorConfig>() &&
         systemSupportsMemoryTagging())
       Primary.Options.set(OptionBit::UseMemoryTagging);
@@ -433,7 +439,8 @@ class Allocator {
   }
 
   NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin, uptr DeleteSize = 0,
-                           UNUSED uptr Alignment = MinAlignment) {
+                           bool HasDeleteSize = false, uptr DeleteAlignment = 0,
+                           bool HasDeleteAlignment = false) {
     if (UNLIKELY(!Ptr))
       return;
 
@@ -456,6 +463,9 @@ class Allocator {
     }
 #endif // GWP_ASAN_HOOKS
 
+    if (UNLIKELY(HasDeleteAlignment && !isPowerOfTwo(DeleteAlignment)))
+      reportAlignmentNotPowerOfTwo(DeleteAlignment);
+
     if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment)))
       reportMisalignedPointer(AllocatorAction::Deallocating, Ptr);
 
@@ -470,19 +480,41 @@ 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)
-          reportDeallocTypeMismatch(AllocatorAction::Deallocating, Ptr,
-                                    Header.OriginOrWasZeroed, Origin);
-      }
+      if (UNLIKELY(isOriginMismatch(
+              static_cast<Chunk::Origin>(Header.OriginOrWasZeroed), Origin,
+              HasDeleteSize)))
+        reportDeallocTypeMismatch(AllocatorAction::Deallocating, Ptr,
+                                  Header.OriginOrWasZeroed, Origin,
+                                  HasDeleteSize);
     }
 
     const uptr Size = getSize(Ptr, &Header);
-    if (DeleteSize && Options.get(OptionBit::DeleteSizeMismatch)) {
-      if (UNLIKELY(DeleteSize != Size))
-        reportDeleteSizeMismatch(Ptr, DeleteSize, Size);
+    switch (Origin) {
+    case Chunk::Origin::New:
+      FALLTHROUGH;
+    case Chunk::Origin::NewArray:
+      if (Options.get(OptionBit::DeleteSizeMismatch) && HasDeleteSize) {
+        if (UNLIKELY(DeleteSize != Size))
+          reportDeleteSizeMismatch(Ptr, DeleteSize, Size);
+      }
+      if (Options.get(OptionBit::DeleteAlignmentMismatch) &&
+          HasDeleteAlignment) {
+        if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), DeleteAlignment)))
+          reportDeleteAlignmentMismatch(Ptr, DeleteAlignment);
+      }
+      break;
+    case Chunk::Origin::AlignedAlloc:
+      if (Options.get(OptionBit::FreeAlignmentMismatch) && HasDeleteAlignment) {
+        if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), DeleteAlignment)))
+          reportFreeAlignmentMismatch(Ptr, DeleteAlignment);
+      }
+      FALLTHROUGH;
+    case Chunk::Origin::Malloc:
+      if (Options.get(OptionBit::FreeSizeMismatch) && HasDeleteSize) {
+        if (UNLIKELY(DeleteSize != Size))
+          reportFreeSizeMismatch(Ptr, DeleteSize, Size);
+      }
+      break;
     }
 
     quarantineOrDeallocateChunk(Options, TaggedPtr, &Header, Size);
@@ -529,14 +561,20 @@ 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))
-        reportDeallocTypeMismatch(AllocatorAction::Reallocating, OldPtr,
-                                  Header.OriginOrWasZeroed,
-                                  Chunk::Origin::Malloc);
+      // There is no language prohibiting the use of realloc with
+      // aligned_alloc/posix_memalign/memalign and etc. The outcome in
+      // practice is that the newly allocated memory will typically not
+      // have the same alignment but will have minimum alignment. With
+      // regards to operator new, 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, /*HasDeleteSize=*/false);
     }
 
     void *BlockBegin = getBlockBegin(OldTaggedPtr, &Header);
@@ -1746,6 +1784,19 @@ class Allocator {
     return (Bytes - sizeof(AllocationRingBuffer)) /
            sizeof(typename AllocationRingBuffer::Entry);
   }
+
+  static bool isOriginMismatch(Chunk::Origin Alloc, Chunk::Origin Dealloc,
+                               bool HasDeleteSize) {
+    if (Alloc == Dealloc) {
+      return false;
+    }
+    if (Alloc == Chunk::Origin::AlignedAlloc &&
+        Dealloc == Chunk::Origin::Malloc && !HasDeleteSize) {
+      // aligned_alloc with free is allowed, but not free_sized.
+      return false;
+    }
+    return true;
+  }
 };
 
 } // namespace scudo
diff --git a/compiler-rt/lib/scudo/standalone/flags.inc b/compiler-rt/lib/scudo/standalone/flags.inc
index ff0c28e1db7c4..cd83d962509b7 100644
--- a/compiler-rt/lib/scudo/standalone/flags.inc
+++ b/compiler-rt/lib/scudo/standalone/flags.inc
@@ -49,3 +49,18 @@ SCUDO_FLAG(int, release_to_os_interval_ms, 5000,
 SCUDO_FLAG(int, allocation_ring_buffer_size, 32768,
            "Entries to keep in the allocation ring buffer for scudo. "
            "Values less or equal to zero disable the buffer.")
+
+SCUDO_FLAG(bool, delete_alignment_mismatch, true,
+           "Terminate on an alignment mismatch between a aligned-delete and "
+           "the actual "
+           "alignment of a chunk (as provided to new/new[]).")
+
+SCUDO_FLAG(bool, free_size_mismatch, true,
+           "Terminate on a size mismatch between a free_sized and the actual "
+           "size of a chunk (as provided to malloc/calloc/realloc).")
+
+SCUDO_FLAG(bool, free_alignment_mismatch, true,
+           "Terminate on an alignment mismatch between a free_aligned_sized "
+           "and the actual "
+           "alignment of a chunk (as provided to "
+           "aligned_alloc/posix_memalign/memalign).")
diff --git a/compiler-rt/lib/scudo/standalone/internal_defs.h b/compiler-rt/lib/scudo/standalone/internal_defs.h
index 27c6b451ffe72..36e393213a004 100644
--- a/compiler-rt/lib/scudo/standalone/internal_defs.h
+++ b/compiler-rt/lib/scudo/standalone/internal_defs.h
@@ -47,6 +47,15 @@
 #define UNUSED __attribute__((unused))
 #define USED __attribute__((used))
 #define NOEXCEPT noexcept
+#ifdef __has_attribute
+#if __has_attribute(fallthrough)
+#define FALLTHROUGH __attribute__((fallthrough))
+#else
+#define FALLTHROUGH
+#endif
+#else
+#define FALLTHROUGH
+#endif
 
 // This check is only available on Clang. This is essentially an alias of
 // C++20's 'constinit' specifier which will take care of this when (if?) we can
diff --git a/compiler-rt/lib/scudo/standalone/options.h b/compiler-rt/lib/scudo/standalone/options.h
index b20142a415903..d77b9111890cc 100644
--- a/compiler-rt/lib/scudo/standalone/options.h
+++ b/compiler-rt/lib/scudo/standalone/options.h
@@ -25,6 +25,9 @@ enum class OptionBit {
   UseOddEvenTags,
   UseMemoryTagging,
   AddLargeAllocationSlack,
+  DeleteAlignmentMismatch,
+  FreeSizeMismatch,
+  FreeAlignmentMismatch,
 };
 
 struct Options {
diff --git a/compiler-rt/lib/scudo/standalone/report.cpp b/compiler-rt/lib/scudo/standalone/report.cpp
index b97a74b078c2f..e7a6a70cdc663 100644
--- a/compiler-rt/lib/scudo/standalone/report.cpp
+++ b/compiler-rt/lib/scudo/standalone/report.cpp
@@ -142,13 +142,48 @@ void NORETURN reportMisalignedPointer(AllocatorAction Action, const void *Ptr) {
                 stringifyAction(Action), Ptr);
 }
 
+static const char *stringifyAllocOrigin(Chunk::Origin AllocOrigin) {
+  switch (AllocOrigin) {
+  case Chunk::Origin::Malloc:
+    return "malloc";
+  case Chunk::Origin::New:
+    return "operator new";
+  case Chunk::Origin::NewArray:
+    return "operator new []";
+  case Chunk::Origin::AlignedAlloc:
+    return "aligned_alloc";
+  }
+  return "<invalid origin>";
+}
+
+static const char *stringifyDeallocOrigin(Chunk::Origin DeallocOrigin,
+                                          bool HasDeleteSize) {
+  switch (DeallocOrigin) {
+  case Chunk::Origin::Malloc:
+    if (HasDeleteSize)
+      return "free_sized";
+    return "free";
+  case Chunk::Origin::New:
+    return "operator delete";
+  case Chunk::Origin::NewArray:
+    return "operator delete []";
+  case Chunk::Origin::AlignedAlloc:
+    return "free_aligned_sized";
+  }
+  return "<invalid origin>";
+}
+
 // The deallocation function used is at odds with the one used to allocate the
 // chunk (eg: new[]/delete or malloc/delete, and so on).
 void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, const void *Ptr,
-                                        u8 TypeA, u8 TypeB) {
+                                        u8 TypeA, u8 TypeB,
+                                        bool HasDeleteSize) {
   ScopedErrorReport Report;
-  Report.append("allocation type mismatch when %s address %p (%d vs %d)\n",
-                stringifyAction(Action), Ptr, TypeA, TypeB);
+  Report.append(
+      "allocation type mismatch when %s address %p (%s vs %s)\n",
+      stringifyAction(Action), Ptr,
+      stringifyAllocOrigin(static_cast<Chunk::Origin>(TypeA)),
+      stringifyDeallocOrigin(static_cast<Chunk::Origin>(TypeB), HasDeleteSize));
 }
 
 // The size specified to the delete operator does not match the one that was
@@ -161,6 +196,28 @@ void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
       Size, ExpectedSize);
 }
 
+void NORETURN reportDeleteAlignmentMismatch(const void *Ptr, uptr Alignment) {
+  ScopedErrorReport Report;
+  Report.append(
+      "invalid aligned delete when deallocating address %p (%zu vs %zu)\n", Ptr,
+      getLeastSignificantSetBitIndex(reinterpret_cast<uptr>(Ptr)), Alignment);
+}
+
+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 vs %zu)\n", Ptr,
+      getLeastSignificantSetBitIndex(reinterpret_cast<uptr>(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..085446ae27081 100644
--- a/compiler-rt/lib/scudo/standalone/report.h
+++ b/compiler-rt/lib/scudo/standalone/report.h
@@ -44,9 +44,13 @@ enum class AllocatorAction : u8 {
 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);
+                                        u8 TypeA, u8 TypeB, bool HasDeleteSize);
 void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
                                        uptr ExpectedSize);
+void NORETURN reportDeleteAlignmentMismatch(const void *Ptr, uptr Alignment);
+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/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index 7e8d5b4396d2e..6026c3b2e7f9d 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -327,7 +327,7 @@ void ScudoCombinedTest<Config>::BasicTest(scudo::uptr SizeLog) {
       EXPECT_LE(Size, Allocator->getUsableSize(P));
       memset(P, 0xaa, Size);
       checkMemoryTaggingMaybe(Allocator, P, Size, Align);
-      Allocator->deallocate(P, Origin, Size);
+      Allocator->deallocate(P, Origin, Size, true);
     }
   }
 
@@ -374,7 +374,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ZeroContents) {
       for (scudo::uptr I = 0; I < Size; I++)
         ASSERT_EQ((reinterpret_cast<char *>(P))[I], '\0');
       memset(P, 0xaa, Size);
-      Allocator->deallocate(P, Origin, Size);
+      Allocator->deallocate(P, Origin, Size, true);
     }
   }
 }
@@ -392,7 +392,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ZeroFill) {
       for (scudo::uptr I = 0; I < Size; I++)
         ASSERT_EQ((reinterpret_cast<char *>(P))[I], '\0');
       memset(P, 0xaa, Size);
-      Allocator->deallocate(P, Origin, Size);
+      Allocator->deallocate(P, Origin, Size, true);
     }
   }
 }
@@ -709,7 +709,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ThreadedCombined) {
 
       while (!V.empty()) {
         auto Pair = V.back();
-        Allocator->deallocate(Pair.first, Origin, Pair.second);
+        Allocator->deallocate(Pair.first, Origin, Pair.second, true);
         V.pop_back();
       }
     });
@@ -782,26 +782,26 @@ TEST(ScudoCombinedDeathTest, DeathCombined) {
   EXPECT_NE(P, nullptr);
 
   // Invalid sized deallocation.
-  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size + 8U), "");
+  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size + 8U, true), "");
 
   // Misaligned pointer. Potentially unused if EXPECT_DEATH isn't available.
   UNUSED void *MisalignedP =
       reinterpret_cast<void *>(reinterpret_cast<scudo::uptr>(P) | 1U);
-  EXPECT_DEATH(Allocator->deallocate(MisalignedP, Origin, Size), "");
-  EXPECT_DEATH(Allocator->reallocate(MisalignedP, Size * 2U), "");
+  EXPECT_DEATH(Allocator->deallocate(MisalignedP, Origin, Size, true), "");
+  EXPECT_DEATH(Allocator->reallocate(MisalignedP, Size * 2U, true), "");
 
   // Header corruption.
   scudo::u64 *H =
       reinterpret_cast<scudo::u64 *>(scudo::Chunk::getAtomicHeader(P));
   *H ^= 0x42U;
-  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
+  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size, true), "");
   *H ^= 0x420042U;
-  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
+  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size, true), "");
   *H ^= 0x420000U;
 
   // Invalid chunk state.
-  Allocator->deallocate(P, Origin, Size);
-  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
+  Allocator->deallocate(P, Origin, Size, true);
+  EXPECT_DEATH(Allocator->deallocate(P, Origin, Size, true), "");
   EXPECT_DEATH(Allocator->reallocate(P, Size * 2U), "");
   EXPECT_DEATH(Allocator->getUsableSize(P), "");
 }
@@ -908,13 +908,13 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, DisableMemInit) {
       memset(Ptrs[I], 0xaa, Size);
     }
     for (unsigned I = 0; I != Ptrs.size(); ++I)
-      Allocator->deallocate(Ptrs[I], Origin, Size);
+      Allocator->deallocate(Ptrs[I], Origin, Size, true);
     for (unsigned I = 0; I != Ptrs.size(); ++I) {
       Ptrs[I] = Allocator->allocate(Size - 8, Origin);
       memset(Ptrs[I], 0xbb, Size - 8);
     }
     for (unsigned I = 0; I != Ptrs.size(); ++I)
-      Allocator->deallocate(Ptrs[I], Origin, Size - 8);
+      Allocator->deallocate(Ptrs[I], Origin, Size - 8, true);
     for (unsigned I = 0; I != Ptrs.size(); ++I) {
       Ptrs[I] = Allocator->allocate(Size, Origin, 1U << MinAlignLog, true);
       for (scudo::uptr J = 0; J < Size; ++J)
diff --git a/compiler-rt/lib/scudo/standalone/tests/report_test.cpp b/compiler-rt/lib/scudo/standalone/tests/report_test.cpp
index 514837df1a43a..18c5717c11c2f 100644
--- a/compiler-rt/lib/scudo/standalone/tests/report_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/report_test.cpp
@@ -41,7 +41,7 @@ TEST(ScudoReportDeathTest, Generic) {
       scudo::reportMisalignedPointer(scudo::AllocatorAction::Deallocating, P),
       "Scudo ERROR.*deallocating.*42424242");
   EXPECT_DEATH(scudo::reportDeallocTypeMismatch(
-                   scudo::AllocatorAction::Reallocating, P, 0, 1),
+                   scudo::AllocatorAction::Reallocating, P, 0, 1, false),
                "Scudo ERROR.*reallocating.*42424242");
   EXPECT_DEATH(scudo::reportDeleteSizeMismatch(P, 123, 456),
                "Scudo ERROR.*42424242.*123.*456");
diff --git a/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h b/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h
index 27c0e591a2099..cf06c221d1a5c 100644
--- a/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h
+++ b/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h
@@ -8,6 +8,8 @@
 
 #include "platform.h"
 
+#include <stdlib.h>
+
 #if SCUDO_FUCHSIA
 #include <zxtest/zxtest.h>
 using Test = ::zxtest::Test;
@@ -58,4 +60,15 @@ using Test = ::testing::Test;
 #define SCUDO_NO_TEST_MAIN
 #endif
 
+// Match Android's default configuration, which disables Scudo's mismatch
+// allocation check, as it is being triggered by some third party code.
+#if SCUDO_ANDROID
+#define DEALLOC_TYPE_MISMATCH "false"
+#define SKIP_DEALLOC_TYPE_MISMATCH 1
+#else
+#define DEALLOC_TYPE_MISMATCH "true"
+#define SKIP_DEALLOC_TYPE_MISMATCH                                             \
+  (getenv("SKIP_DEALLOC_TYPE_MISMATCH") != nullptr)
+#endif
+
 extern bool UseQuarantine;
diff --git a/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test_main.cpp b/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test_main.cpp
index 881e0265bb341..d672abb99db0c 100644
--- a/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test_main.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test_main.cpp
@@ -9,14 +9,6 @@
 #include "memtag.h"
 #include "tests/scudo_unit_test.h"
 
-// Match Android's default configuration, which disables Scudo's mismatch
-// allocation check, as it is being triggered by some third party code.
-#if SCUDO_ANDROID
-#define DEALLOC_TYPE_MISMATCH "false"
-#else
-#define DEALLOC_TYPE_MISMATCH "true"
-#endif
-
 static void EnableMemoryTaggingIfSupported() {
   if (!scudo::archSupportsMemoryTagging())
     return;
diff --git a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp b/compiler-rt/lib/scudo/standalo...
[truncated]

@@ -456,6 +463,9 @@ class Allocator {
}
#endif // GWP_ASAN_HOOKS

if (UNLIKELY(HasDeleteAlignment && !isPowerOfTwo(DeleteAlignment)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it's not be fore GuardedAlloc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly because the isAligned check is also not above. If I move this one, arguably that one should also be moved. Thoughts?

@@ -433,7 +439,8 @@ class Allocator {
}

NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin, uptr DeleteSize = 0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a separate patch
could you please

Move exiting deallocate into private as

NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin,
                           const uptr *DeleteSize, uptr *Alignment) {   // no default values

and in public section replace
deallocate -> with inline "delete{,Sized}{,Aligned}"

and update existing calls as is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jcking jcking force-pushed the scudo_free_aligned_sized branch from f69bc1c to 07f7268 Compare July 9, 2025 13:51
Signed-off-by: Justin King <jcking@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants