[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
Wed Jul 2 07:41:33 PDT 2025
https://github.com/jcking updated https://github.com/llvm/llvm-project/pull/146556
>From 5615f324b0cb8d3cb054a90aedb829dfddc1ee67 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 | 9 +-
compiler-rt/lib/scudo/standalone/combined.h | 87 +++++++--
compiler-rt/lib/scudo/standalone/flags.inc | 17 +-
.../lib/scudo/standalone/internal_defs.h | 8 +
compiler-rt/lib/scudo/standalone/options.h | 3 +
compiler-rt/lib/scudo/standalone/report.cpp | 61 +++++-
compiler-rt/lib/scudo/standalone/report.h | 4 +
.../scudo/standalone/tests/combined_test.cpp | 26 +--
.../scudo/standalone/tests/report_test.cpp | 2 +-
.../scudo/standalone/tests/scudo_unit_test.h | 10 +
.../standalone/tests/scudo_unit_test_main.cpp | 8 -
.../standalone/tests/wrappers_c_test.cpp | 184 ++++++++++++++++++
.../standalone/tests/wrappers_cpp_test.cpp | 16 +-
.../lib/scudo/standalone/wrappers_c.inc | 28 ++-
.../lib/scudo/standalone/wrappers_cpp.cpp | 28 +--
15 files changed, 409 insertions(+), 82 deletions(-)
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..690db4d68c1d1 100644
--- a/compiler-rt/lib/scudo/standalone/flags.inc
+++ b/compiler-rt/lib/scudo/standalone/flags.inc
@@ -24,7 +24,7 @@ SCUDO_FLAG(int, quarantine_max_chunk_size, 0,
"Size (in bytes) up to which chunks will be quarantined (if lower "
"than or equal to).")
-SCUDO_FLAG(bool, dealloc_type_mismatch, false,
+SCUDO_FLAG(bool, dealloc_type_mismatch, 0,
"Terminate on a type mismatch in allocation-deallocation functions, "
"eg: malloc/delete, new/free, new/delete[], etc.")
@@ -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..16dd404a308f0 100644
--- a/compiler-rt/lib/scudo/standalone/internal_defs.h
+++ b/compiler-rt/lib/scudo/standalone/internal_defs.h
@@ -48,6 +48,14 @@
#define USED __attribute__((used))
#define NOEXCEPT noexcept
+#if __has_cpp_attribute(clang::fallthrough)
+#define FALLTHROUGH [[clang::fallthrough]]
+#elif __has_cpp_attribute(fallthrough)
+#define FALLTHROUGH [[fallthrough]]
+#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
// ask all libc's that use Scudo to compile us with C++20. Dynamic
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..d690c5557de5a 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,26 @@ 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)\n",
+ 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)\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..5cb59525784c8 100644
--- a/compiler-rt/lib/scudo/standalone/report.h
+++ b/compiler-rt/lib/scudo/standalone/report.h
@@ -47,6 +47,10 @@ void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, const void *Ptr,
u8 TypeA, u8 TypeB);
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..a7e614676d8e2 100644
--- a/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h
+++ b/compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h
@@ -58,4 +58,14 @@ 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_ON_ANDROID(T) DISABLED_##T
+#else
+#define DEALLOC_TYPE_MISMATCH "true"
+#define SKIP_ON_ANDROID(T) T
+#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/standalone/tests/wrappers_c_test.cpp
index f5e17d7214863..2db8416d7bdd6 100644
--- a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp
@@ -45,6 +45,8 @@ int malloc_iterate(uintptr_t base, size_t size,
void *arg);
void *valloc(size_t size);
void *pvalloc(size_t size);
+void free_sized(void *ptr, size_t size);
+void free_aligned_sized(void *ptr, size_t alignment, size_t size);
#ifndef SCUDO_ENABLE_HOOKS_TESTS
#define SCUDO_ENABLE_HOOKS_TESTS 0
@@ -186,6 +188,188 @@ TEST_F(ScudoWrappersCDeathTest, Malloc) {
EXPECT_EQ(errno, ENOMEM);
}
+TEST_F(ScudoWrappersCTest, MallocFreeSized) {
+ void *P = malloc(Size);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % FIRST_32_SECOND_64(8U, 16U), 0U);
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ if (!getenv("SKIP_TYPE_MISMATCH")) {
+ EXPECT_DEATH(free_sized(P, Size - 1), "");
+ EXPECT_DEATH(free_sized(P, Size + 1), "");
+ }
+
+ // An update to this warning in Clang now triggers in this line, but it's ok
+ // because the check is expecting a bad pointer and should fail.
+#if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfree-nonheap-object"
+#endif
+ EXPECT_DEATH(
+ free_sized(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(P) | 1U),
+ Size),
+ "");
+#if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
+#pragma GCC diagnostic pop
+#endif
+
+ free_sized(P, Size);
+ verifyDeallocHookPtr(P);
+ EXPECT_DEATH(free_sized(P, Size), "");
+}
+
+TEST_F(ScudoWrappersCTest, AlignedAllocFreeAlignedSized) {
+ const size_t Alignment = 4096U;
+ const size_t Size = Alignment * 4U;
+ void *P = aligned_alloc(Alignment, Size);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ if (!getenv("SKIP_TYPE_MISMATCH")) {
+ EXPECT_DEATH(free_aligned_sized(P, Alignment, Size - 1), "");
+ EXPECT_DEATH(free_aligned_sized(P, Alignment, Size + 1), "");
+ EXPECT_DEATH(
+ free_aligned_sized(P, size_t{1} << (sizeof(size_t) * 8 - 1), Size), "");
+ }
+
+ // An update to this warning in Clang now triggers in this line, but it's ok
+ // because the check is expecting a bad pointer and should fail.
+#if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfree-nonheap-object"
+#endif
+ EXPECT_DEATH(free_aligned_sized(reinterpret_cast<void *>(
+ reinterpret_cast<uintptr_t>(P) | 1U),
+ Alignment, Size),
+ "");
+#if defined(__has_warning) && __has_warning("-Wfree-nonheap-object")
+#pragma GCC diagnostic pop
+#endif
+
+ free_aligned_sized(P, Alignment, Size);
+ verifyDeallocHookPtr(P);
+ EXPECT_DEATH(free_aligned_sized(P, Alignment, Size), "");
+}
+
+TEST_F(ScudoWrappersCDeathTest, SKIP_ON_ANDROID(MallocFreeAlignedSized)) {
+ if (getenv("SKIP_TYPE_MISMATCH"))
+ GTEST_SKIP();
+
+ void *P = malloc(Size);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % FIRST_32_SECOND_64(8U, 16U), 0U);
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ EXPECT_DEATH(free_aligned_sized(P, 8, Size), "");
+ EXPECT_DEATH(free_aligned_sized(P, alignof(std::max_align_t), Size), "");
+
+ free_sized(P, Size);
+ verifyDeallocHookPtr(P);
+}
+
+TEST_F(ScudoWrappersCDeathTest, SKIP_ON_ANDROID(AlignedAllocFreeSized)) {
+ if (getenv("SKIP_TYPE_MISMATCH"))
+ GTEST_SKIP();
+
+ const size_t Alignment = 4096U;
+ const size_t Size = Alignment * 4U;
+ void *P = aligned_alloc(Alignment, Size);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ EXPECT_DEATH(free_sized(P, Size), "");
+
+ free_aligned_sized(P, Alignment, Size);
+ verifyDeallocHookPtr(P);
+}
+
+TEST_F(ScudoWrappersCDeathTest, SKIP_ON_ANDROID(PosixMemalignFreeSized)) {
+ if (getenv("SKIP_TYPE_MISMATCH"))
+ GTEST_SKIP();
+
+ const size_t Alignment = 4096U;
+ const size_t Size = Alignment * 4U;
+ void *P;
+ EXPECT_EQ(posix_memalign(&P, Alignment, Size), 0);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ EXPECT_DEATH(free_sized(P, Size), "");
+
+ free(P);
+ verifyDeallocHookPtr(P);
+}
+
+TEST_F(ScudoWrappersCDeathTest, SKIP_ON_ANDROID(MemalignFreeSized)) {
+ if (getenv("SKIP_TYPE_MISMATCH"))
+ GTEST_SKIP();
+
+ const size_t Alignment = 4096U;
+ const size_t Size = Alignment * 4U;
+ void *P = memalign(Alignment, Size);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(P) % Alignment, 0U);
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ EXPECT_DEATH(free_sized(P, Size), "");
+
+ free(P);
+ verifyDeallocHookPtr(P);
+}
+
+TEST_F(ScudoWrappersCDeathTest, SKIP_ON_ANDROID(PvallocFreeSized)) {
+ if (getenv("SKIP_TYPE_MISMATCH"))
+ GTEST_SKIP();
+
+ const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
+
+ const size_t Size = PageSize;
+ void *P = pvalloc(Size);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ EXPECT_DEATH(free_sized(P, Size), "");
+
+ free(P);
+ verifyDeallocHookPtr(P);
+}
+
+TEST_F(ScudoWrappersCDeathTest, SKIP_ON_ANDROID(VallocFreeSized)) {
+ if (getenv("SKIP_TYPE_MISMATCH"))
+ GTEST_SKIP();
+
+ const size_t PageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
+
+ const size_t Size = PageSize;
+ void *P = valloc(Size);
+ EXPECT_NE(P, nullptr);
+ EXPECT_LE(Size, malloc_usable_size(P));
+ verifyAllocHookPtr(P);
+ verifyAllocHookSize(Size);
+
+ EXPECT_DEATH(free_sized(P, Size), "");
+
+ free(P);
+ verifyDeallocHookPtr(P);
+}
+
TEST_F(ScudoWrappersCTest, Calloc) {
void *P = calloc(1U, Size);
EXPECT_NE(P, nullptr);
diff --git a/compiler-rt/lib/scudo/standalone/tests/wrappers_cpp_test.cpp b/compiler-rt/lib/scudo/standalone/tests/wrappers_cpp_test.cpp
index c802ed22fbad0..76be524a7aa8f 100644
--- a/compiler-rt/lib/scudo/standalone/tests/wrappers_cpp_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/wrappers_cpp_test.cpp
@@ -17,13 +17,6 @@
#include <thread>
#include <vector>
-// Android does not support checking for new/delete mismatches.
-#if SCUDO_ANDROID
-#define SKIP_MISMATCH_TESTS 1
-#else
-#define SKIP_MISMATCH_TESTS 0
-#endif
-
void operator delete(void *, size_t) noexcept;
void operator delete[](void *, size_t) noexcept;
@@ -142,11 +135,10 @@ class Pixel {
// Note that every Cxx allocation function in the test binary will be fulfilled
// by Scudo. See the comment in the C counterpart of this file.
-TEST_F(ScudoWrappersCppDeathTest, New) {
- if (getenv("SKIP_TYPE_MISMATCH") || SKIP_MISMATCH_TESTS) {
- printf("Skipped type mismatch tests.\n");
- return;
- }
+TEST_F(ScudoWrappersCppDeathTest, SKIP_ON_ANDROID(New)) {
+ if (getenv("SKIP_TYPE_MISMATCH"))
+ GTEST_SKIP();
+
testCxxNew<bool>();
testCxxNew<uint8_t>();
testCxxNew<uint16_t>();
diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c.inc b/compiler-rt/lib/scudo/standalone/wrappers_c.inc
index 59f3fb0962f8b..936fe84ec9c41 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;
@@ -139,8 +151,8 @@ INTERFACE WEAK void *SCUDO_PREFIX(memalign)(size_t alignment, size_t size) {
scudo::reportAlignmentNotPowerOfTwo(alignment);
}
}
- void *Ptr =
- SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment);
+ void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::AlignedAlloc,
+ alignment);
reportAllocation(Ptr, size);
return Ptr;
}
@@ -152,8 +164,8 @@ INTERFACE WEAK int SCUDO_PREFIX(posix_memalign)(void **memptr, size_t alignment,
scudo::reportInvalidPosixMemalignAlignment(alignment);
return EINVAL;
}
- void *Ptr =
- SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment);
+ void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::AlignedAlloc,
+ 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::AlignedAlloc, 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::AlignedAlloc,
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