diff --git a/compiler-rt/lib/scudo/standalone/chunk.h b/compiler-rt/lib/scudo/standalone/chunk.h --- a/compiler-rt/lib/scudo/standalone/chunk.h +++ b/compiler-rt/lib/scudo/standalone/chunk.h @@ -91,8 +91,7 @@ getHeaderSize()); } -inline -const AtomicPackedHeader *getConstAtomicHeader(const void *Ptr) { +inline const AtomicPackedHeader *getConstAtomicHeader(const void *Ptr) { return reinterpret_cast( reinterpret_cast(Ptr) - getHeaderSize()); } @@ -118,9 +117,8 @@ atomic_store_relaxed(getAtomicHeader(Ptr), NewPackedHeader); } -inline -void loadHeader(u32 Cookie, const void *Ptr, - UnpackedHeader *NewUnpackedHeader) { +inline void loadHeader(u32 Cookie, const void *Ptr, + UnpackedHeader *NewUnpackedHeader) { PackedHeader NewPackedHeader = atomic_load_relaxed(getConstAtomicHeader(Ptr)); *NewUnpackedHeader = bit_cast(NewPackedHeader); if (UNLIKELY(NewUnpackedHeader->Checksum != @@ -141,8 +139,8 @@ reportHeaderRace(Ptr); } -inline -bool isValid(u32 Cookie, const void *Ptr, UnpackedHeader *NewUnpackedHeader) { +inline bool isValid(u32 Cookie, const void *Ptr, + UnpackedHeader *NewUnpackedHeader) { PackedHeader NewPackedHeader = atomic_load_relaxed(getConstAtomicHeader(Ptr)); *NewUnpackedHeader = bit_cast(NewPackedHeader); return NewUnpackedHeader->Checksum == diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -457,6 +457,18 @@ Stats.get(S); } + // Returns true if the pointer provided was allocated by the current + // allocator instance, which is compliant with tcmalloc's ownership concept. + // A corrupted chunk will not be reported as owned, which is WAI. + bool isOwned(const void *Ptr) { + initThreadMaybe(); + if (!Ptr || !isAligned(reinterpret_cast(Ptr), MinAlignment)) + return false; + Chunk::UnpackedHeader Header; + return Chunk::isValid(Cookie, Ptr, &Header) && + Header.State == Chunk::State::Allocated; + } + private: using SecondaryT = typename Params::Secondary; typedef typename PrimaryT::SizeClassMap SizeClassMap; @@ -468,6 +480,9 @@ static const uptr MaxAllowedMallocSize = FIRST_32_SECOND_64(1UL << 31, 1ULL << 40); + static_assert(MinAlignment >= sizeof(Chunk::PackedHeader), + "Minimal alignment must at least cover a chunk header."); + // Constants used by the chunk iteration mechanism. static const u32 BlockMarker = 0x44554353U; static const uptr InvalidChunk = ~static_cast(0); diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp @@ -32,6 +32,12 @@ Deleter); Allocator->reset(); + EXPECT_FALSE(Allocator->isOwned(&Mutex)); + EXPECT_FALSE(Allocator->isOwned(&Allocator)); + scudo::u64 StackVariable = 0x42424242U; + EXPECT_FALSE(Allocator->isOwned(&StackVariable)); + EXPECT_EQ(StackVariable, 0x42424242U); + constexpr scudo::uptr MinAlignLog = FIRST_32_SECOND_64(3U, 4U); // This allocates and deallocates a bunch of chunks, with a wide range of @@ -46,6 +52,7 @@ const scudo::uptr Size = (1U << SizeLog) + Delta; void *P = Allocator->allocate(Size, Origin, Align); EXPECT_NE(P, nullptr); + EXPECT_TRUE(Allocator->isOwned(P)); EXPECT_TRUE(scudo::isAligned(reinterpret_cast(P), Align)); EXPECT_LE(Size, Allocator->getUsableSize(P)); memset(P, 0xaa, Size);