[scudo] Add free_sized and free_aligned_sized (#186881)

Add one new flag, dealloc_align_mismatch that turns on/off alignment
checks. Add three new config parameters, one for deallocate type
mismatch (such as abort on new/free if true), one for checking if the
size parameter matches on dealloc and one for checking if the alignment
is correct on a dealloc.

Add extra flags to be passed for to indicate to do an align/size check.

Update report functions to better indicate the errors. Add unit tests
for all of these.

This is based on these upstream cls by jcking:

https://github.com/llvm/llvm-project/pull/147735
https://github.com/llvm/llvm-project/pull/146556
This commit is contained in:
Christopher Ferris 2026-03-20 20:26:35 +00:00 committed by GitHub
parent 66f06f54cb
commit 1b44e34b18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 724 additions and 80 deletions

View File

@ -68,6 +68,25 @@ BASE_OPTIONAL(const bool, ExactUsableSize, true)
BASE_OPTIONAL(const bool, EnableZeroOnDealloc, false)
#endif
// Abort if there is a mismatch between the function type that allocates memory
// and the function type that deallocates memory.
// These conditions will abort with an error message:
// new/any C free operation
// any C allocation operation/any delete operation
// new/delete[]
// new[]/delete
// aligned alloc/free_sized
// non-aligned alloc/free_aligned_sized
BASE_OPTIONAL(const bool, AbortOnDeallocTypeMismatch, true)
// Abort if there is free function that takes a size value but that size
// does not match the allocation size.
BASE_OPTIONAL(const bool, AbortOnDeallocSizeMismatch, true)
// Abort if there is free function that takes an alignment value but that
// alignment does not match the allocation alignment.
BASE_OPTIONAL(const bool, AbortOnDeallocAlignmentMismatch, true)
// PRIMARY_REQUIRED_TYPE(NAME)
//
// SizeClassMap to use with the Primary.

View File

@ -53,12 +53,29 @@ 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
New = 1, // operator new
NewArray = 2, // operator new []
Memalign = 3, // aligned_alloc, memalign, posix_memalign, pvalloc, valloc
// These flags are not stored in the Origin in the header, used in deallocate
// for verification purposes.
Size = 0x10, // Verify size parameter.
Align = 0x20, // Verify align parameter.
// NOTE: It is currently not possible to verify a new/new [] aligned
// allocation calls delete/delete [] that is aligned due to only
// having two bits to store the Origin in the header.
};
ALWAYS_INLINE u8 originBaseType(u8 Origin) {
return (Origin & 3) == Origin::Memalign ? Origin::Malloc : Origin & 0x3;
}
ALWAYS_INLINE bool originAligned(u8 Origin) {
return Origin == Origin::Memalign || (Origin & Origin::Align);
}
ALWAYS_INLINE bool originSized(u8 Origin) { return Origin & Origin::Size; }
enum State : u8 { Available = 0, Allocated = 1, Quarantined = 2 };
typedef u64 PackedHeader;
@ -71,6 +88,10 @@ struct UnpackedHeader {
uptr SizeOrUnusedBytes : 20;
uptr Offset : 16;
uptr Checksum : 16;
ALWAYS_INLINE u8 getOrigin() { return OriginOrWasZeroed; }
ALWAYS_INLINE void setOrigin(u8 Origin) { OriginOrWasZeroed = Origin; }
};
typedef atomic_u64 AtomicPackedHeader;
static_assert(sizeof(UnpackedHeader) == sizeof(PackedHeader), "");

View File

@ -169,6 +169,8 @@ public:
Primary.Options.setFillContentsMode(PatternOrZeroFill);
if (getFlags()->dealloc_type_mismatch)
Primary.Options.set(OptionBit::DeallocTypeMismatch);
if (getFlags()->dealloc_align_mismatch)
Primary.Options.set(OptionBit::DeallocAlignMismatch);
if (getFlags()->delete_size_mismatch)
Primary.Options.set(OptionBit::DeleteSizeMismatch);
if (systemSupportsMemoryTagging())
@ -442,8 +444,66 @@ public:
SizeOrUnusedBytes, FillContents);
}
NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin, uptr DeleteSize = 0,
UNUSED uptr Alignment = MinAlignment) {
ALWAYS_INLINE void deallocate(void *Ptr, Chunk::Origin Origin) {
deallocate(Ptr, Origin, /*DeleteSize=*/0, /*DeleteAlignment=*/0);
}
ALWAYS_INLINE void deallocateSized(void *Ptr, Chunk::Origin Origin,
uptr DeleteSize) {
deallocate(Ptr, Origin | Chunk::Origin::Size, DeleteSize,
/*DeleteAlignment=*/0);
}
ALWAYS_INLINE void deallocateSizedAligned(void *Ptr, Chunk::Origin Origin,
uptr DeleteSize,
uptr DeleteAlignment) {
deallocate(Ptr, Origin | Chunk::Origin::Size | Chunk::Origin::Align,
DeleteSize, DeleteAlignment);
}
ALWAYS_INLINE void deallocateAligned(void *Ptr, Chunk::Origin Origin,
uptr DeleteAlignment) {
deallocate(Ptr, Origin | Chunk::Origin::Align,
/*DeleteSize=*/0, /*DeleteAlignment=*/DeleteAlignment);
}
ALWAYS_INLINE void checkSizeMatch(const void *Ptr,
Chunk::UnpackedHeader *Header, uptr Size,
uptr DeallocSize) {
if (AllocatorConfig::getExactUsableSize()) {
if (DeallocSize != Size)
reportDeleteSizeMismatch(Ptr, DeallocSize, Size);
} else if (DeallocSize != Size && DeallocSize != getUsableSize(Ptr, Header))
reportDeleteSizeMismatch(Ptr, DeallocSize, Size,
getUsableSize(Ptr, Header));
}
ALWAYS_INLINE void checkTypeMatch(AllocatorAction Action, const void *Ptr,
u8 AllocOrigin, u8 DeallocOrigin) {
if (UNLIKELY(Chunk::originBaseType(AllocOrigin) !=
Chunk::originBaseType(DeallocOrigin)))
reportDeallocTypeMismatch(Action, Ptr, AllocOrigin, DeallocOrigin);
// There is no way to store that a new/new [] did an aligned allocate,
// so skip that part of the verification.
if (UNLIKELY(AllocOrigin == Chunk::Origin::New ||
AllocOrigin == Chunk::Origin::NewArray))
return;
if (Chunk::originAligned(AllocOrigin)) {
// Only disallow an aligned allocation and a non-aligned deallocation
// if this is a realloc.
if (Action == AllocatorAction::Reallocating &&
!Chunk::originAligned(DeallocOrigin))
reportDeallocTypeMismatch(Action, Ptr, AllocOrigin, DeallocOrigin);
} else if (Chunk::originAligned(DeallocOrigin)) {
// Origin not aligned, dealloc aligned.
reportDeallocTypeMismatch(Action, Ptr, AllocOrigin, DeallocOrigin);
}
}
NOINLINE void deallocate(void *Ptr, u8 DeallocOrigin, uptr DeleteSize,
uptr DeleteAlignment) {
if (UNLIKELY(!Ptr))
return;
@ -469,6 +529,10 @@ public:
if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment)))
reportMisalignedPointer(AllocatorAction::Deallocating, Ptr);
if (UNLIKELY(Chunk::originAligned(DeallocOrigin) &&
!isPowerOfTwo(DeleteAlignment)))
reportAlignmentNotPowerOfTwo(DeleteAlignment);
void *TaggedPtr = Ptr;
Ptr = getHeaderTaggedPointer(Ptr);
@ -479,21 +543,22 @@ public:
reportInvalidChunkState(AllocatorAction::Deallocating, Ptr);
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);
}
}
const uptr Size = getSize(Ptr, &Header);
if (DeleteSize && Options.get(OptionBit::DeleteSizeMismatch)) {
if (UNLIKELY(DeleteSize != Size))
reportDeleteSizeMismatch(Ptr, DeleteSize, Size);
}
if (AllocatorConfig::getAbortOnDeallocSizeMismatch() &&
Chunk::originSized(DeallocOrigin) &&
Options.get(OptionBit::DeleteSizeMismatch))
checkSizeMatch(Ptr, &Header, Size, DeleteSize);
if (AllocatorConfig::getAbortOnDeallocTypeMismatch() &&
Options.get(OptionBit::DeallocTypeMismatch))
checkTypeMatch(AllocatorAction::Deallocating, Ptr, Header.getOrigin(),
DeallocOrigin);
if (UNLIKELY(AllocatorConfig::getAbortOnDeallocAlignmentMismatch() &&
Chunk::originAligned(DeallocOrigin) &&
Options.get(OptionBit::DeallocAlignMismatch) &&
!isAligned(reinterpret_cast<uptr>(Ptr), DeleteAlignment)))
reportDeleteAlignmentMismatch(Ptr, DeleteAlignment);
quarantineOrDeallocateChunk(Options, TaggedPtr, &Header, Size);
}
@ -542,12 +607,10 @@ public:
// 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);
}
if (AllocatorConfig::getAbortOnDeallocTypeMismatch() &&
Options.get(OptionBit::DeallocTypeMismatch))
checkTypeMatch(AllocatorAction::Reallocating, OldPtr, Header.getOrigin(),
Chunk::Origin::Malloc);
void *BlockBegin = getBlockBegin(OldTaggedPtr, &Header);
uptr BlockEnd;
@ -1175,7 +1238,7 @@ private:
Header.ClassId = ClassId & Chunk::ClassIdMask;
Header.State = Chunk::State::Allocated;
Header.OriginOrWasZeroed = Origin & Chunk::OriginMask;
Header.setOrigin(Origin);
Header.SizeOrUnusedBytes = SizeOrUnusedBytes & Chunk::SizeOrUnusedBytesMask;
Chunk::storeHeader(Cookie, reinterpret_cast<void *>(addHeaderTag(UserPtr)),
&Header);
@ -1307,7 +1370,7 @@ private:
Header.ClassId = ClassId & Chunk::ClassIdMask;
Header.State = Chunk::State::Allocated;
Header.OriginOrWasZeroed = Origin & Chunk::OriginMask;
Header.setOrigin(Origin);
Header.SizeOrUnusedBytes = SizeOrUnusedBytes & Chunk::SizeOrUnusedBytesMask;
Chunk::storeHeader(Cookie, Ptr, &Header);

View File

@ -32,6 +32,11 @@ SCUDO_FLAG(bool, delete_size_mismatch, true,
"Terminate on a size mismatch between a sized-delete and the actual "
"size of a chunk (as provided to new/new[]).")
SCUDO_FLAG(
bool, dealloc_align_mismatch, true,
"Terminate on an alignment mismatch in allocation-deallocation functions, "
"eg: memalign/free_aligned_sized, etc.")
#if SCUDO_FUCHSIA
SCUDO_FLAG(int, zero_on_dealloc_max_size, 0,
"Only chunks smaller or equal to this threshold will be zeroed on "

View File

@ -19,6 +19,7 @@ enum class OptionBit {
MayReturnNull,
FillContents0of2,
FillContents1of2,
DeallocAlignMismatch,
DeallocTypeMismatch,
DeleteSizeMismatch,
TrackAllocationStacks,

View File

@ -128,6 +128,33 @@ static const char *stringifyAction(AllocatorAction Action) {
return "<invalid action>";
}
static const char *stringifyOrigin(u8 Origin) {
switch (Chunk::originBaseType(Origin)) {
case Chunk::Origin::Malloc:
return "malloc";
case Chunk::Origin::New:
return "new";
case Chunk::Origin::NewArray:
return "new[]";
case Chunk::Origin::Memalign:
return "memalign";
default:
return "<invalid origin>";
}
}
static const char *stringifyOriginFlags(u8 Origin) {
bool Sized = Chunk::originSized(Origin);
bool Aligned = Chunk::originAligned(Origin);
if (Sized && Aligned)
return "sized aligned ";
else if (Sized)
return "sized ";
else if (Aligned)
return "aligned ";
return "";
}
// The chunk is not in a state congruent with the operation we want to perform.
// This is usually the case with a double-free, a realloc of a freed pointer.
void NORETURN reportInvalidChunkState(AllocatorAction Action, const void *Ptr) {
@ -145,20 +172,35 @@ void NORETURN reportMisalignedPointer(AllocatorAction Action, const void *Ptr) {
// 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 AllocOrigin, u8 DeallocOrigin) {
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%s vs %s%s)\n",
stringifyAction(Action), Ptr, stringifyOriginFlags(AllocOrigin),
stringifyOrigin(AllocOrigin),
stringifyOriginFlags(DeallocOrigin),
stringifyOrigin(DeallocOrigin));
}
// The size specified to the delete operator does not match the one that was
// passed to new when allocating the chunk.
void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
uptr ExpectedSize) {
uptr ExpectedSize,
uptr ExpectedUsableSize) {
ScopedErrorReport Report;
Report.append(
"invalid sized delete when deallocating address %p (%zu vs %zu)\n", Ptr,
Size, ExpectedSize);
Report.append("invalid sized delete when deallocating address %p (%zu vs %zu",
Ptr, Size, ExpectedSize);
if (ExpectedUsableSize != 0)
Report.append(" or %zu", ExpectedUsableSize);
Report.append(")\n");
}
void NORETURN reportDeleteAlignmentMismatch(const void *Ptr, uptr Alignment) {
ScopedErrorReport Report;
Report.append("invalid aligned delete when deallocating address %p (%zu bit "
"align vs %zu bit align)\n",
Ptr,
getLeastSignificantSetBitIndex(reinterpret_cast<uptr>(Ptr)),
getLeastSignificantSetBitIndex(Alignment));
}
void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) {

View File

@ -44,9 +44,11 @@ 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 AllocOrigin, u8 DeallocOrigin);
void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
uptr ExpectedSize);
uptr ExpectedSize,
uptr ExpectedUsableSize = 0);
void NORETURN reportDeleteAlignmentMismatch(const void *Ptr, uptr Alignment);
// C wrappers errors.
void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment);

View File

@ -333,7 +333,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->deallocateSized(P, Origin, Size);
}
}
@ -382,7 +382,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->deallocateSized(P, Origin, Size);
}
}
}
@ -400,7 +400,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->deallocateSized(P, Origin, Size);
}
}
}
@ -427,7 +427,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, PatternOrZeroFill) {
ASSERT_TRUE(V == scudo::PatternFillByte || V == 0);
}
memset(P, 0xaa, Size);
Allocator->deallocate(P, Origin, Size);
Allocator->deallocateSized(P, Origin, Size);
}
}
}
@ -712,7 +712,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ThreadedCombined) {
while (!V.empty()) {
auto Pair = V.back();
Allocator->deallocate(Pair.first, Origin, Pair.second);
Allocator->deallocateSized(Pair.first, Origin, Pair.second);
V.pop_back();
}
});
@ -796,26 +796,26 @@ TEST(ScudoCombinedDeathTest, DeathCombined) {
EXPECT_NE(P, nullptr);
// Invalid sized deallocation.
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size + 8U), "");
EXPECT_DEATH(Allocator->deallocateSized(P, Origin, Size + 8U), "");
// 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->deallocateSized(MisalignedP, Origin, Size), "");
EXPECT_DEATH(Allocator->reallocate(MisalignedP, Size * 2U), "");
// 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->deallocateSized(P, Origin, Size), "");
*H ^= 0x420042U;
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
EXPECT_DEATH(Allocator->deallocateSized(P, Origin, Size), "");
*H ^= 0x420000U;
// Invalid chunk state.
Allocator->deallocate(P, Origin, Size);
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
Allocator->deallocateSized(P, Origin, Size);
EXPECT_DEATH(Allocator->deallocateSized(P, Origin, Size), "");
EXPECT_DEATH(Allocator->reallocate(P, Size * 2U), "");
EXPECT_DEATH(Allocator->getUsableSize(P), "");
}
@ -922,13 +922,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->deallocateSized(Ptrs[I], Origin, Size);
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->deallocateSized(Ptrs[I], Origin, Size - 8);
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)
@ -1606,3 +1606,272 @@ TEST(ScudoCombinedTest, StressThreadInitTSDExclusive) {
for (size_t I = 0; I < 10; I++)
RunStress<AllocatorT>();
}
struct TestMatchConfig {
static const bool MaySupportMemoryTagging = false;
template <class A> using TSDRegistryT = scudo::TSDRegistrySharedT<A, 1U, 1U>;
struct Primary {
using SizeClassMap = scudo::AndroidSizeClassMap;
#if SCUDO_CAN_USE_PRIMARY64
static const scudo::uptr RegionSizeLog = 28U;
typedef scudo::u32 CompactPtrT;
static const scudo::uptr CompactPtrScale = SCUDO_MIN_ALIGNMENT_LOG;
static const scudo::uptr GroupSizeLog = 20U;
static const bool EnableRandomOffset = true;
static const scudo::uptr MapSizeIncrement = 1UL << 18;
#else
static const scudo::uptr RegionSizeLog = 18U;
static const scudo::uptr GroupSizeLog = 18U;
typedef scudo::uptr CompactPtrT;
#endif
static const bool EnableBlockCache = false;
static const scudo::s32 MinReleaseToOsIntervalMs = 1000;
static const scudo::s32 MaxReleaseToOsIntervalMs = 1000;
};
#if SCUDO_CAN_USE_PRIMARY64
template <typename Config>
using PrimaryT = scudo::SizeClassAllocator64<Config>;
#else
template <typename Config>
using PrimaryT = scudo::SizeClassAllocator32<Config>;
#endif
template <typename Config> using SecondaryT = scudo::MapAllocator<Config>;
struct Secondary {
template <typename Config>
using CacheT = scudo::MapAllocatorNoCache<Config>;
};
};
TEST(ScudoCombinedDeathTest, DeallocateAlignNotPowerOfTwo) {
using AllocatorT = scudo::Allocator<TestMatchConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Memalign, 32);
EXPECT_DEATH(
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::Memalign, 9),
"alignment must be a power of two");
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::Memalign, 32);
}
TEST(ScudoCombinedDeathTest, TypeMismatch) {
ScopedScudoOptions Options("dealloc_type_mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Malloc);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->deallocate(Ptr, scudo::Chunk::Origin::New),
"deallocating.*\\(malloc vs new\\)");
EXPECT_DEATH(Allocator->deallocate(Ptr, scudo::Chunk::Origin::NewArray),
"deallocating.*\\(malloc vs new\\[\\]\\)");
Allocator->deallocate(Ptr, scudo::Chunk::Origin::Malloc);
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::New);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->deallocate(Ptr, scudo::Chunk::Origin::Malloc),
"deallocating.*\\(new vs malloc\\)");
EXPECT_DEATH(Allocator->deallocate(Ptr, scudo::Chunk::Origin::NewArray),
"deallocating.*\\(new vs new\\[\\]\\)");
Allocator->deallocate(Ptr, scudo::Chunk::Origin::New);
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::NewArray);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->deallocate(Ptr, scudo::Chunk::Origin::Malloc),
"deallocating.*\\(new\\[\\] vs malloc\\)");
EXPECT_DEATH(Allocator->deallocate(Ptr, scudo::Chunk::Origin::New),
"deallocating.*\\(new\\[\\] vs new\\)");
Allocator->deallocate(Ptr, scudo::Chunk::Origin::NewArray);
}
TEST(ScudoCombinedDeathTest, ReallocTypeMismatch) {
ScopedScudoOptions Options("dealloc_type_mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::New);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->reallocate(Ptr, 1000000),
"reallocating.*\\(new vs malloc\\)");
Allocator->deallocate(Ptr, scudo::Chunk::Origin::New);
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::NewArray);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->reallocate(Ptr, 1000000),
"reallocating.*\\(new\\[\\] vs malloc\\)");
Allocator->deallocate(Ptr, scudo::Chunk::Origin::NewArray);
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Memalign, 32);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->reallocate(Ptr, 1000000),
"reallocating.*\\(aligned malloc vs malloc\\)");
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::Memalign, 32);
}
TEST(ScudoCombinedDeathTest, AlignTypeMismatch) {
ScopedScudoOptions Options("dealloc_type_mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Memalign, 32);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->reallocate(Ptr, 1000),
"reallocating.*\\(aligned malloc vs malloc\\)");
// Aligned allocate with non-aligned deallocate is okay.
Allocator->deallocate(Ptr, scudo::Chunk::Origin::Malloc);
}
// Scudo currently cannot verify that a pointer allocated with an aligned
// new/new [] is deallocated with an aligned delete/delete [].
TEST(ScudoCombinedTest, NewType) {
ScopedScudoOptions Options("dealloc_type_mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::New, 1024);
EXPECT_TRUE(Ptr != nullptr);
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::New, 1024);
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::NewArray, 1024);
EXPECT_TRUE(Ptr != nullptr);
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::NewArray, 1024);
}
TEST(ScudoCombinedDeathTest, SizeMismatch) {
ScopedScudoOptions Options("delete_size_mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Malloc);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(
Allocator->deallocateSized(Ptr, scudo::Chunk::Origin::Malloc, 1000000),
"invalid sized delete when deallocating.*\\(1000000 vs 10\\)");
Allocator->deallocate(Ptr, scudo::Chunk::Origin::Malloc);
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Memalign, 32);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(Allocator->deallocateSizedAligned(
Ptr, scudo::Chunk::Origin::Malloc, 1000000, 32),
"invalid sized delete when deallocating.*\\(1000000 vs 10\\)");
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::Malloc, 32);
}
struct TestMatchNotExactUsableSizeConfig : public TestMatchConfig {
static const bool ExactUsableSize = false;
};
TEST(ScudoCombinedDeathTest, SizeNotExactUsableMismatch) {
ScopedScudoOptions Options("delete_size_mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchNotExactUsableSizeConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Malloc);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(
Allocator->deallocateSized(Ptr, scudo::Chunk::Origin::Malloc, 1000000),
"invalid sized delete when deallocating.*\\(1000000 vs 10 or .*\\)");
Allocator->deallocateSized(Ptr, scudo::Chunk::Origin::Malloc, 10);
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Memalign, 32);
EXPECT_TRUE(Ptr != nullptr);
EXPECT_DEATH(
Allocator->deallocateSizedAligned(Ptr, scudo::Chunk::Origin::Malloc,
1000000, 32),
"invalid sized delete when deallocating.*\\(1000000 vs 10 or .*\\)");
EXPECT_NE(10U, Allocator->getUsableSize(Ptr));
Allocator->deallocateSizedAligned(Ptr, scudo::Chunk::Origin::Malloc,
Allocator->getUsableSize(Ptr), 32);
}
template <class AllocatorT>
static void *getMinAlignedPointer(AllocatorT *Allocator) {
// The actual alignment is not stored in the header, so the only check
// that can be made is to verify how the pointer is aligned.
// Therefore, try and allocate a pointer which has a low alignment but
// not a high alignment so we can check for death.
std::vector<void *> Ptrs;
void *AlignedPtr = nullptr;
scudo::uptr AlignmentMask = 2 * scudo::getPageSizeCached() - 1;
for (size_t I = 0; I < 1000; ++I) {
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Memalign, 32);
EXPECT_TRUE(Ptr != nullptr);
scudo::uptr FlatPtr = reinterpret_cast<scudo::uptr>(Ptr);
if ((FlatPtr & AlignmentMask) != 0) {
AlignedPtr = Ptr;
break;
}
Ptrs.push_back(Ptr);
}
// Free all of the other pointers.
for (auto Ptr : Ptrs) {
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::Memalign, 32);
}
return AlignedPtr;
}
TEST(ScudoCombinedDeathTest, AlignMismatch) {
ScopedScudoOptions Options("dealloc_align_mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
// Pointer is guaranteed to not be aligned to 2 * page size.
void *AlignedPtr = getMinAlignedPointer<AllocatorT>(Allocator.get());
if (AlignedPtr == nullptr) {
GTEST_SKIP() << "Cannot allocate aligned pointer for test.";
}
scudo::uptr Alignment = 2 * scudo::getPageSizeCached();
EXPECT_DEATH(Allocator->deallocateAligned(
AlignedPtr, scudo::Chunk::Origin::Malloc, Alignment),
"invalid aligned delete when deallocating");
EXPECT_DEATH(Allocator->deallocateSizedAligned(
AlignedPtr, scudo::Chunk::Origin::Malloc, 10, Alignment),
"invalid aligned delete when deallocating");
Allocator->deallocateAligned(AlignedPtr, scudo::Chunk::Origin::Malloc, 32);
}
struct TestMatchOverrideConfig : public TestMatchConfig {
// Disable all type/size/alignment checks.
static const bool AbortOnDeallocTypeMismatch = false;
static const bool AbortOnDeallocSizeMismatch = false;
static const bool AbortOnDeallocAlignmentMismatch = false;
};
TEST(ScudoCombinedTest, VerifyConfigOverrideMatchChecks) {
ScopedScudoOptions Options(
"dealloc_type_mismatch=true:delete_size_mismatch=true:dealloc_align_"
"mismatch=true");
using AllocatorT = scudo::Allocator<TestMatchOverrideConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
// Verify type mismatch is ignored.
void *Ptr = Allocator->allocate(10, scudo::Chunk::Origin::New);
EXPECT_TRUE(Ptr != nullptr);
Allocator->deallocate(Ptr, scudo::Chunk::Origin::Malloc);
// Verify size mismatch is ignored.
Ptr = Allocator->allocate(10, scudo::Chunk::Origin::Malloc);
EXPECT_TRUE(Ptr != nullptr);
Allocator->deallocateSized(Ptr, scudo::Chunk::Origin::Malloc, 1000000);
// Pointer is guaranteed to not be aligned to 2 * page size.
scudo::uptr Alignment = 2 * scudo::getPageSizeCached();
Ptr = getMinAlignedPointer<AllocatorT>(Allocator.get());
if (Ptr != nullptr) {
Allocator->deallocateAligned(Ptr, scudo::Chunk::Origin::Malloc, Alignment);
}
}

View File

@ -110,11 +110,14 @@ TEST(ScudoFlagsTest, AllocatorFlags) {
Flags.setDefaults();
Flags.dealloc_type_mismatch = false;
Flags.delete_size_mismatch = false;
Flags.dealloc_align_mismatch = false;
Flags.quarantine_max_chunk_size = 1024;
Parser.parseString("dealloc_type_mismatch=true:delete_size_mismatch=true:"
"quarantine_max_chunk_size=2048");
Parser.parseString(
"dealloc_type_mismatch=true:delete_size_mismatch=true:"
"dealloc_align_mismatch=true:quarantine_max_chunk_size=2048");
EXPECT_TRUE(Flags.dealloc_type_mismatch);
EXPECT_TRUE(Flags.delete_size_mismatch);
EXPECT_TRUE(Flags.dealloc_align_mismatch);
EXPECT_EQ(2048, Flags.quarantine_max_chunk_size);
}

View File

@ -40,11 +40,30 @@ TEST(ScudoReportDeathTest, Generic) {
EXPECT_DEATH(
scudo::reportMisalignedPointer(scudo::AllocatorAction::Deallocating, P),
"Scudo ERROR.*deallocating.*42424242");
EXPECT_DEATH(
scudo::reportDeallocTypeMismatch(scudo::AllocatorAction::Reallocating, P,
scudo::Chunk::Origin::New,
scudo::Chunk::Origin::Memalign),
"Scudo ERROR.*reallocating.*42424242.*\\(new vs aligned malloc\\)");
EXPECT_DEATH(scudo::reportDeallocTypeMismatch(
scudo::AllocatorAction::Reallocating, P, 0, 1),
"Scudo ERROR.*reallocating.*42424242");
scudo::AllocatorAction::Deallocating, P,
scudo::Chunk::Origin::Memalign, scudo::Chunk::Origin::New),
"Scudo ERROR.*deallocating.*\\(aligned malloc vs new\\)");
EXPECT_DEATH(scudo::reportDeallocTypeMismatch(
scudo::AllocatorAction::Deallocating, P,
scudo::Chunk::Origin::Malloc | scudo::Chunk::Origin::Size,
scudo::Chunk::Origin::NewArray | scudo::Chunk::Origin::Size |
scudo::Chunk::Origin::Align),
"Scudo ERROR.*deallocating.*\\(sized malloc vs sized aligned "
"new\\[\\]\\)");
EXPECT_DEATH(scudo::reportDeleteSizeMismatch(P, 123, 456),
"Scudo ERROR.*42424242.*123.*456");
"Scudo ERROR.*42424242.*\\(123 vs 456\\)");
EXPECT_DEATH(scudo::reportDeleteSizeMismatch(P, 123, 456, 789),
"Scudo ERROR.*42424242.*\\(123 vs 456 or 789\\)");
EXPECT_DEATH(
scudo::reportDeleteAlignmentMismatch(reinterpret_cast<void *>(0x80),
0x100),
"Scudo ERROR.*invalid aligned delete.*\\(7 bit align vs 8 bit align\\)");
}
TEST(ScudoReportDeathTest, CSpecific) {

View File

@ -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;
@ -39,10 +31,12 @@ __scudo_default_options() {
// will disable the feature entirely.
EnableMemoryTaggingIfSupported();
if (!UseQuarantine)
return "dealloc_type_mismatch=" DEALLOC_TYPE_MISMATCH;
return "dealloc_type_mismatch=true:delete_size_mismatch=true:dealloc_align_"
"mismatch=true";
return "quarantine_size_kb=256:thread_local_quarantine_size_kb=128:"
"quarantine_max_chunk_size=512:"
"dealloc_type_mismatch=" DEALLOC_TYPE_MISMATCH;
"dealloc_type_mismatch=true:delete_size_mismatch=true:dealloc_align_"
"mismatch=true";
}
#if !defined(SCUDO_NO_TEST_MAIN)

View File

@ -25,16 +25,35 @@
#if SCUDO_FUCHSIA
// Fuchsia only has valloc
#define HAVE_VALLOC 1
#define VERIFY_FREE_SIZED_DEATH 1
#define VERIFY_FREE_ALIGNED_SIZED_DEATH 1
#elif SCUDO_ANDROID
// Android only has pvalloc/valloc on 32 bit
#if !defined(__LP64__)
#define HAVE_PVALLOC 1
#define HAVE_VALLOC 1
#endif // !defined(__LP64__)
// Disable all size/aligned checks since they are disabled on Android.
#define VERIFY_FREE_SIZED_DEATH 0
#define VERIFY_FREE_ALIGNED_SIZED_DEATH 0
#else
// All others assumed to support both functions.
#define HAVE_PVALLOC 1
#define HAVE_VALLOC 1
// Assume all others will die on these calls.
#define VERIFY_FREE_SIZED_DEATH 1
#define VERIFY_FREE_ALIGNED_SIZED_DEATH 1
#endif
// GWP-Asan doesn't do these checks.
#if defined(GWP_ASAN_HOOKS)
#undef VERIFY_FREE_SIZED_DEATH
#undef VERIFY_FREE_ALIGNED_SIZED_DEATH
#define VERIFY_FREE_SIZED_DEATH 0
#define VERIFY_FREE_ALIGNED_SIZED_DEATH 0
#endif
extern "C" {
@ -43,6 +62,8 @@ void malloc_disable(void);
int malloc_iterate(uintptr_t base, size_t size,
void (*callback)(uintptr_t base, size_t size, void *arg),
void *arg);
void free_sized(void *ptr, size_t size);
void free_aligned_sized(void *ptr, size_t alignment, size_t size);
void *valloc(size_t size);
void *pvalloc(size_t size);
@ -424,6 +445,179 @@ TEST_F(ScudoWrappersCTest, Reallocarray) {
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 (VERIFY_FREE_SIZED_DEATH) {
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);
if (VERIFY_FREE_SIZED_DEATH)
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 (VERIFY_FREE_ALIGNED_SIZED_DEATH) {
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, MallocFreeAlignedSized) {
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 (VERIFY_FREE_ALIGNED_SIZED_DEATH) {
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, AlignedAllocFreeSized) {
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 (VERIFY_FREE_SIZED_DEATH)
EXPECT_DEATH(free_sized(P, Size), "");
free_aligned_sized(P, Alignment, Size);
verifyDeallocHookPtr(P);
}
TEST_F(ScudoWrappersCDeathTest, PosixMemalignFreeSized) {
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);
if (VERIFY_FREE_SIZED_DEATH)
EXPECT_DEATH(free_sized(P, Size), "");
free(P);
verifyDeallocHookPtr(P);
}
TEST_F(ScudoWrappersCDeathTest, MemalignFreeSized) {
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);
if (VERIFY_FREE_SIZED_DEATH)
EXPECT_DEATH(free_sized(P, Size), "");
free(P);
verifyDeallocHookPtr(P);
}
#if HAVE_PVALLOC
TEST_F(ScudoWrappersCDeathTest, PvallocFreeSized) {
const size_t Size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
void *P = pvalloc(Size);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
verifyAllocHookPtr(P);
verifyAllocHookSize(Size);
if (VERIFY_FREE_SIZED_DEATH)
EXPECT_DEATH(free_sized(P, Size), "");
free(P);
verifyDeallocHookPtr(P);
}
#endif
#if HAVE_VALLOC
TEST_F(ScudoWrappersCDeathTest, VallocFreeSized) {
const size_t Size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
void *P = valloc(Size);
EXPECT_NE(P, nullptr);
EXPECT_LE(Size, malloc_usable_size(P));
verifyAllocHookPtr(P);
verifyAllocHookSize(Size);
if (VERIFY_FREE_SIZED_DEATH)
EXPECT_DEATH(free_sized(P, Size), "");
free(P);
verifyDeallocHookPtr(P);
}
#endif
#if !SCUDO_FUCHSIA
TEST_F(ScudoWrappersCTest, MallOpt) {
errno = 0;

View File

@ -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.deallocateSized(ptr, scudo::Chunk::Origin::Malloc, size);
}
INTERFACE WEAK void
SCUDO_PREFIX(free_aligned_sized)(void *ptr, size_t alignment, size_t size) {
reportDeallocation(ptr);
SCUDO_ALLOCATOR.deallocateSizedAligned(ptr, scudo::Chunk::Origin::Malloc,
size, alignment);
}
INTERFACE WEAK struct SCUDO_MALLINFO SCUDO_PREFIX(mallinfo)(void) {
struct SCUDO_MALLINFO Info = {};
scudo::StatCounters Stats;
@ -324,7 +336,7 @@ INTERFACE WEAK void *SCUDO_PREFIX(aligned_alloc)(size_t alignment,
}
void *Ptr =
SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, alignment);
SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment);
reportAllocation(Ptr, size);
return scudo::setErrnoOnNull(Ptr);

View File

@ -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.deallocateSized(ptr, scudo::Chunk::Origin::New, size);
}
INTERFACE WEAK void operator delete[](void *ptr, size_t size) NOEXCEPT {
reportDeallocation(ptr);
Allocator.deallocate(ptr, scudo::Chunk::Origin::NewArray, size);
Allocator.deallocateSized(ptr, scudo::Chunk::Origin::NewArray, size);
}
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.deallocateAligned(ptr, scudo::Chunk::Origin::New,
static_cast<scudo::uptr>(align));
}
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.deallocateAligned(ptr, scudo::Chunk::Origin::NewArray,
static_cast<scudo::uptr>(align));
}
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.deallocateAligned(ptr, scudo::Chunk::Origin::New,
static_cast<scudo::uptr>(align));
}
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.deallocateAligned(ptr, scudo::Chunk::Origin::NewArray,
static_cast<scudo::uptr>(align));
}
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.deallocateSizedAligned(ptr, scudo::Chunk::Origin::New, size,
static_cast<scudo::uptr>(align));
}
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.deallocateSizedAligned(ptr, scudo::Chunk::Origin::NewArray, size,
static_cast<scudo::uptr>(align));
}
#endif // !SCUDO_ANDROID || !_BIONIC