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 @@ -19,6 +19,7 @@ #include static constexpr scudo::Chunk::Origin Origin = scudo::Chunk::Origin::Malloc; +static constexpr scudo::uptr MinAlignLog = FIRST_32_SECOND_64(3U, 4U); // Fuchsia complains that the function is not used. UNUSED static void disableDebuggerdMaybe() { @@ -100,6 +101,8 @@ UseQuarantine = true; } + void BasicTest(scudo::uptr SizeLogMin, scudo::uptr SizeLogMax); + using AllocatorT = TestAllocator; std::unique_ptr Allocator; }; @@ -128,13 +131,15 @@ EXPECT_EQ(StackBuffer[I], 0x42U); } -TYPED_TEST(ScudoCombinedTest, BasicCombined) { +template +void ScudoCombinedTest::BasicTest(scudo::uptr SizeLogMin, + scudo::uptr SizeLogMax) { auto *Allocator = this->Allocator.get(); // This allocates and deallocates a bunch of chunks, with a wide range of // sizes and alignments, with a focus on sizes that could trigger weird // behaviors (plus or minus a small delta of a power of two for example). - for (scudo::uptr SizeLog = 0U; SizeLog <= 20U; SizeLog++) { + for (scudo::uptr SizeLog = SizeLogMin; SizeLog <= SizeLogMax; SizeLog++) { for (scudo::uptr AlignLog = MinAlignLog; AlignLog <= 16U; AlignLog++) { const scudo::uptr Align = 1U << AlignLog; for (scudo::sptr Delta = -32; Delta <= 32; Delta++) { @@ -154,10 +159,14 @@ } } +TYPED_TEST(ScudoCombinedTest, BasicCombined0) { this->BasicTest(0, 16); } +TYPED_TEST(ScudoCombinedTest, BasicCombined1) { this->BasicTest(16, 18); } +TYPED_TEST(ScudoCombinedTest, BasicCombined2) { this->BasicTest(19, 19); } +TYPED_TEST(ScudoCombinedTest, BasicCombined3) { this->BasicTest(20, 20); } + TYPED_TEST(ScudoCombinedTest, ZeroContents) { auto *Allocator = this->Allocator.get(); - constexpr scudo::uptr MinAlignLog = FIRST_32_SECOND_64(3U, 4U); // Ensure that specifying ZeroContents returns a zero'd out block. for (scudo::uptr SizeLog = 0U; SizeLog <= 20U; SizeLog++) { for (scudo::uptr Delta = 0U; Delta <= 4U; Delta++) { @@ -191,6 +200,8 @@ } TYPED_TEST(ScudoCombinedTest, PatternOrZeroFill) { + auto *Allocator = this->Allocator.get(); + // Ensure that specifying PatternOrZeroFill returns a pattern or zero filled // block. The primary allocator only produces pattern filled blocks if MTE // is disabled, so we only require pattern filled blocks in that case. @@ -216,6 +227,8 @@ } TYPED_TEST(ScudoCombinedTest, BlockReuse) { + auto *Allocator = this->Allocator.get(); + // Verify that a chunk will end up being reused, at some point. const scudo::uptr NeedleSize = 1024U; void *NeedleP = Allocator->allocate(NeedleSize, Origin); @@ -231,12 +244,12 @@ EXPECT_TRUE(Found); } -TYPED_TEST(ScudoCombinedTest, TODO) { - constexpr scudo::uptr MaxSize = TypeParam::Primary::SizeClassMap::MaxSize; +TYPED_TEST(ScudoCombinedTest, ReallocateLarge) { + auto *Allocator = this->Allocator.get(); // Reallocate a large chunk all the way down to a byte, verifying that we // preserve the data in the process. - scudo::uptr Size = MaxSize * 2; + scudo::uptr Size = TypeParam::Primary::SizeClassMap::MaxSize * 2; const scudo::uptr DataSize = 2048U; void *P = Allocator->allocate(Size, Origin); const char Marker = 0xab; @@ -250,13 +263,19 @@ P = NewP; } Allocator->deallocate(P, Origin); +} + +TYPED_TEST(ScudoCombinedTest, ReallocateSame) { + auto *Allocator = this->Allocator.get(); // Check that reallocating a chunk to a slightly smaller or larger size // returns the same chunk. This requires that all the sizes we iterate on use // the same block size, but that should be the case for MaxSize - 64 with our // default class size maps. - constexpr scudo::uptr ReallocSize = MaxSize - 64; - P = Allocator->allocate(ReallocSize, Origin); + constexpr scudo::uptr ReallocSize = + TypeParam::Primary::SizeClassMap::MaxSize - 64; + void *P = Allocator->allocate(ReallocSize, Origin); + const char Marker = 0xab; memset(P, Marker, ReallocSize); for (scudo::sptr Delta = -32; Delta < 32; Delta += 8) { const scudo::uptr NewSize = ReallocSize + Delta; @@ -267,31 +286,33 @@ checkMemoryTaggingMaybe(Allocator, NewP, NewSize, 0); } Allocator->deallocate(P, Origin); +} +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). - if (!UseQuarantine) { - std::vector V; - for (scudo::uptr I = 0; I < 64U; I++) - V.push_back(Allocator->allocate(rand() % (MaxSize / 2U), Origin)); - Allocator->disable(); - Allocator->iterateOverChunks( - 0U, static_cast(SCUDO_MMAP_RANGE_SIZE - 1), - [](uintptr_t Base, 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(); - while (!V.empty()) { - Allocator->deallocate(V.back(), Origin); - V.pop_back(); - } - } + std::vector V; + for (scudo::uptr I = 0; I < 64U; I++) + V.push_back(Allocator->allocate( + rand() % (TypeParam::Primary::SizeClassMap::MaxSize / 2U), Origin)); + Allocator->disable(); + Allocator->iterateOverChunks( + 0U, static_cast(SCUDO_MMAP_RANGE_SIZE - 1), + [](uintptr_t Base, 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); +} - Allocator->releaseToOS(); +TYPED_TEST(ScudoCombinedTest, UseAfterFree) { + auto *Allocator = this->Allocator.get(); // Check that use-after-free is detected. for (scudo::uptr SizeLog = 0U; SizeLog <= 20U; SizeLog++) { @@ -315,6 +336,10 @@ }, ""); } +} + +TYPED_TEST(ScudoCombinedTest, DisableMemoryTagging) { + auto *Allocator = this->Allocator.get(); if (Allocator->useMemoryTaggingTestOnly()) { // Check that disabling memory tagging works correctly. @@ -336,6 +361,10 @@ // Re-enable them now. scudo::enableMemoryTagChecksTestOnly(); } +} + +TYPED_TEST(ScudoCombinedTest, Stats) { + auto *Allocator = this->Allocator.get(); scudo::uptr BufferSize = 8192; std::vector Buffer(BufferSize); @@ -351,6 +380,17 @@ EXPECT_NE(Stats.find("Stats: SizeClassAllocator"), std::string::npos); EXPECT_NE(Stats.find("Stats: MapAllocator"), std::string::npos); EXPECT_NE(Stats.find("Stats: Quarantine"), std::string::npos); +} + +TYPED_TEST(ScudoCombinedTest, CacheDrain) { + auto *Allocator = this->Allocator.get(); + + std::vector V; + for (scudo::uptr I = 0; I < 64U; I++) + V.push_back(Allocator->allocate( + rand() % (TypeParam::Primary::SizeClassMap::MaxSize / 2U), Origin)); + for (auto P : V) + Allocator->deallocate(P, Origin); bool UnlockRequired; auto *TSD = Allocator->getTSDRegistry()->getTSDAndLock(&UnlockRequired);