Skip to content

Commit d0eef22

Browse files
authored
[scudo] Skip special quarantine blocks in iterateOverChunks (llvm#159892)
If a quarantine block is allocated, it will be passed through to a user who calls iterateOverChunks. Make these special block allocations state Quarantined so they are not passed to iterateOverChunks. Remove the FIXME in the combined tests for quarantine since this fixes those tests too. Also add a specific new test that fails without this fix.
1 parent e8e97aa commit d0eef22

File tree

2 files changed

+55
-34
lines changed

2 files changed

+55
-34
lines changed

compiler-rt/lib/scudo/standalone/combined.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class Allocator {
101101
Chunk::UnpackedHeader Header = {};
102102
Header.ClassId = QuarantineClassId & Chunk::ClassIdMask;
103103
Header.SizeOrUnusedBytes = sizeof(QuarantineBatch);
104-
Header.State = Chunk::State::Allocated;
104+
Header.State = Chunk::State::Quarantined;
105105
Chunk::storeHeader(Allocator.Cookie, Ptr, &Header);
106106

107107
// Reset tag to 0 as this chunk may have been previously used for a tagged
@@ -120,7 +120,7 @@ class Allocator {
120120
Chunk::UnpackedHeader Header;
121121
Chunk::loadHeader(Allocator.Cookie, Ptr, &Header);
122122

123-
if (UNLIKELY(Header.State != Chunk::State::Allocated))
123+
if (UNLIKELY(Header.State != Chunk::State::Quarantined))
124124
reportInvalidChunkState(AllocatorAction::Deallocating, Ptr);
125125
DCHECK_EQ(Header.ClassId, QuarantineClassId);
126126
DCHECK_EQ(Header.Offset, 0);

compiler-rt/lib/scudo/standalone/tests/combined_test.cpp

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <set>
2525
#include <stdlib.h>
2626
#include <thread>
27+
#include <unordered_map>
2728
#include <vector>
2829

2930
static constexpr scudo::Chunk::Origin Origin = scudo::Chunk::Origin::Malloc;
@@ -150,14 +151,8 @@ void TestAllocator<Config>::operator delete(void *ptr) {
150151
}
151152

152153
template <class TypeParam> struct ScudoCombinedTest : public Test {
153-
ScudoCombinedTest() {
154-
UseQuarantine = std::is_same<TypeParam, scudo::AndroidConfig>::value;
155-
Allocator = std::make_unique<AllocatorT>();
156-
}
157-
~ScudoCombinedTest() {
158-
Allocator->releaseToOS(scudo::ReleaseToOS::Force);
159-
UseQuarantine = true;
160-
}
154+
ScudoCombinedTest() { Allocator = std::make_unique<AllocatorT>(); }
155+
~ScudoCombinedTest() { Allocator->releaseToOS(scudo::ReleaseToOS::Force); }
161156

162157
void RunTest();
163158

@@ -525,30 +520,25 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, IterateOverChunks) {
525520
auto *Allocator = this->Allocator.get();
526521
// Allocates a bunch of chunks, then iterate over all the chunks, ensuring
527522
// they are the ones we allocated. This requires the allocator to not have any
528-
// other allocated chunk at this point (eg: won't work with the Quarantine).
529-
// FIXME: Make it work with UseQuarantine and tagging enabled. Internals of
530-
// iterateOverChunks reads header by tagged and non-tagger pointers so one of
531-
// them will fail.
532-
if (!UseQuarantine) {
533-
std::vector<void *> V;
534-
for (scudo::uptr I = 0; I < 64U; I++)
535-
V.push_back(Allocator->allocate(
536-
static_cast<scudo::uptr>(std::rand()) %
537-
(TypeParam::Primary::SizeClassMap::MaxSize / 2U),
538-
Origin));
539-
Allocator->disable();
540-
Allocator->iterateOverChunks(
541-
0U, static_cast<scudo::uptr>(SCUDO_MMAP_RANGE_SIZE - 1),
542-
[](uintptr_t Base, UNUSED size_t Size, void *Arg) {
543-
std::vector<void *> *V = reinterpret_cast<std::vector<void *> *>(Arg);
544-
void *P = reinterpret_cast<void *>(Base);
545-
EXPECT_NE(std::find(V->begin(), V->end(), P), V->end());
546-
},
547-
reinterpret_cast<void *>(&V));
548-
Allocator->enable();
549-
for (auto P : V)
550-
Allocator->deallocate(P, Origin);
551-
}
523+
// other allocated chunk at this point.
524+
std::vector<void *> V;
525+
for (scudo::uptr I = 0; I < 64U; I++)
526+
V.push_back(Allocator->allocate(
527+
static_cast<scudo::uptr>(std::rand()) %
528+
(TypeParam::Primary::SizeClassMap::MaxSize / 2U),
529+
Origin));
530+
Allocator->disable();
531+
Allocator->iterateOverChunks(
532+
0U, static_cast<scudo::uptr>(SCUDO_MMAP_RANGE_SIZE - 1),
533+
[](uintptr_t Base, UNUSED size_t Size, void *Arg) {
534+
std::vector<void *> *V = reinterpret_cast<std::vector<void *> *>(Arg);
535+
void *P = reinterpret_cast<void *>(Base);
536+
EXPECT_NE(std::find(V->begin(), V->end(), P), V->end());
537+
},
538+
reinterpret_cast<void *>(&V));
539+
Allocator->enable();
540+
for (auto P : V)
541+
Allocator->deallocate(P, Origin);
552542
}
553543

554544
SCUDO_TYPED_TEST(ScudoCombinedDeathTest, UseAfterFree) {
@@ -1161,3 +1151,34 @@ TEST(ScudoCombinedTest, QuarantineDisabled) {
11611151
// No quarantine stats should not be present.
11621152
EXPECT_EQ(Stats.find("Stats: Quarantine"), std::string::npos);
11631153
}
1154+
1155+
// Verify that no special quarantine blocks appear in iterateOverChunks.
1156+
TEST(ScudoCombinedTest, QuarantineIterateOverChunks) {
1157+
using AllocatorT = scudo::Allocator<TestQuarantineConfig>;
1158+
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
1159+
1160+
// Do a bunch of allocations and deallocations. At the end there should
1161+
// be no special quarantine blocks in our callbacks, and no blocks at all.
1162+
std::vector<scudo::uptr> Sizes = {128, 128, 256, 256};
1163+
for (auto const Size : Sizes) {
1164+
void *Ptr = Allocator->allocate(Size, Origin);
1165+
EXPECT_NE(Ptr, nullptr);
1166+
Allocator->deallocate(Ptr, Origin);
1167+
}
1168+
std::unordered_map<uintptr_t, size_t> Pointers;
1169+
Allocator->disable();
1170+
Allocator->iterateOverChunks(
1171+
0, static_cast<scudo::uptr>(SCUDO_MMAP_RANGE_SIZE - 1),
1172+
[](uintptr_t Base, size_t Size, void *Arg) {
1173+
std::unordered_map<uintptr_t, size_t> *Pointers =
1174+
reinterpret_cast<std::unordered_map<uintptr_t, size_t> *>(Arg);
1175+
(*Pointers)[Base] = Size;
1176+
},
1177+
reinterpret_cast<void *>(&Pointers));
1178+
Allocator->enable();
1179+
1180+
for (const auto [Base, Size] : Pointers) {
1181+
EXPECT_TRUE(false) << "Unexpected pointer found in iterateOverChunks "
1182+
<< std::hex << Base << " Size " << std::dec << Size;
1183+
}
1184+
}

0 commit comments

Comments
 (0)