diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h index c9ba28a52f780..329ec4596482b 100644 --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -101,7 +101,7 @@ class Allocator { Chunk::UnpackedHeader Header = {}; Header.ClassId = QuarantineClassId & Chunk::ClassIdMask; Header.SizeOrUnusedBytes = sizeof(QuarantineBatch); - Header.State = Chunk::State::Allocated; + Header.State = Chunk::State::Quarantined; Chunk::storeHeader(Allocator.Cookie, Ptr, &Header); // Reset tag to 0 as this chunk may have been previously used for a tagged @@ -120,7 +120,7 @@ class Allocator { Chunk::UnpackedHeader Header; Chunk::loadHeader(Allocator.Cookie, Ptr, &Header); - if (UNLIKELY(Header.State != Chunk::State::Allocated)) + if (UNLIKELY(Header.State != Chunk::State::Quarantined)) reportInvalidChunkState(AllocatorAction::Deallocating, Ptr); DCHECK_EQ(Header.ClassId, QuarantineClassId); DCHECK_EQ(Header.Offset, 0); diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp index 1eff9ebcb7a4f..5b56b973d55f8 100644 --- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include static constexpr scudo::Chunk::Origin Origin = scudo::Chunk::Origin::Malloc; @@ -150,14 +151,8 @@ void TestAllocator::operator delete(void *ptr) { } template struct ScudoCombinedTest : public Test { - ScudoCombinedTest() { - UseQuarantine = std::is_same::value; - Allocator = std::make_unique(); - } - ~ScudoCombinedTest() { - Allocator->releaseToOS(scudo::ReleaseToOS::Force); - UseQuarantine = true; - } + ScudoCombinedTest() { Allocator = std::make_unique(); } + ~ScudoCombinedTest() { Allocator->releaseToOS(scudo::ReleaseToOS::Force); } void RunTest(); @@ -525,30 +520,25 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, IterateOverChunks) { auto *Allocator = this->Allocator.get(); // Allocates a bunch of chunks, then iterate over all the chunks, ensuring // they are the ones we allocated. This requires the allocator to not have any - // other allocated chunk at this point (eg: won't work with the Quarantine). - // FIXME: Make it work with UseQuarantine and tagging enabled. Internals of - // iterateOverChunks reads header by tagged and non-tagger pointers so one of - // them will fail. - if (!UseQuarantine) { - std::vector V; - for (scudo::uptr I = 0; I < 64U; I++) - V.push_back(Allocator->allocate( - static_cast(std::rand()) % - (TypeParam::Primary::SizeClassMap::MaxSize / 2U), - Origin)); - Allocator->disable(); - Allocator->iterateOverChunks( - 0U, static_cast(SCUDO_MMAP_RANGE_SIZE - 1), - [](uintptr_t Base, UNUSED size_t Size, void *Arg) { - std::vector *V = reinterpret_cast *>(Arg); - void *P = reinterpret_cast(Base); - EXPECT_NE(std::find(V->begin(), V->end(), P), V->end()); - }, - reinterpret_cast(&V)); - Allocator->enable(); - for (auto P : V) - Allocator->deallocate(P, Origin); - } + // other allocated chunk at this point. + std::vector V; + for (scudo::uptr I = 0; I < 64U; I++) + V.push_back(Allocator->allocate( + static_cast(std::rand()) % + (TypeParam::Primary::SizeClassMap::MaxSize / 2U), + Origin)); + Allocator->disable(); + Allocator->iterateOverChunks( + 0U, static_cast(SCUDO_MMAP_RANGE_SIZE - 1), + [](uintptr_t Base, UNUSED size_t Size, void *Arg) { + std::vector *V = reinterpret_cast *>(Arg); + void *P = reinterpret_cast(Base); + EXPECT_NE(std::find(V->begin(), V->end(), P), V->end()); + }, + reinterpret_cast(&V)); + Allocator->enable(); + for (auto P : V) + Allocator->deallocate(P, Origin); } SCUDO_TYPED_TEST(ScudoCombinedDeathTest, UseAfterFree) { @@ -1161,3 +1151,34 @@ TEST(ScudoCombinedTest, QuarantineDisabled) { // No quarantine stats should not be present. EXPECT_EQ(Stats.find("Stats: Quarantine"), std::string::npos); } + +// Verify that no special quarantine blocks appear in iterateOverChunks. +TEST(ScudoCombinedTest, QuarantineIterateOverChunks) { + using AllocatorT = scudo::Allocator; + auto Allocator = std::unique_ptr(new AllocatorT()); + + // Do a bunch of allocations and deallocations. At the end there should + // be no special quarantine blocks in our callbacks, and no blocks at all. + std::vector Sizes = {128, 128, 256, 256}; + for (auto const Size : Sizes) { + void *Ptr = Allocator->allocate(Size, Origin); + EXPECT_NE(Ptr, nullptr); + Allocator->deallocate(Ptr, Origin); + } + std::unordered_map Pointers; + Allocator->disable(); + Allocator->iterateOverChunks( + 0, static_cast(SCUDO_MMAP_RANGE_SIZE - 1), + [](uintptr_t Base, size_t Size, void *Arg) { + std::unordered_map *Pointers = + reinterpret_cast *>(Arg); + (*Pointers)[Base] = Size; + }, + reinterpret_cast(&Pointers)); + Allocator->enable(); + + for (const auto [Base, Size] : Pointers) { + EXPECT_TRUE(false) << "Unexpected pointer found in iterateOverChunks " + << std::hex << Base << " Size " << std::dec << Size; + } +}