[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