Skip to content

Commit c094edb

Browse files
committed
[scudo] Skip special quarantine blocks in iterateOverChunks
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 f6c711b commit c094edb

File tree

2 files changed

+54
-28
lines changed

2 files changed

+54
-28
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ class Allocator {
101101
Chunk::UnpackedHeader Header = {};
102102
Header.ClassId = QuarantineClassId & Chunk::ClassIdMask;
103103
Header.SizeOrUnusedBytes = sizeof(QuarantineBatch);
104-
Header.State = Chunk::State::Allocated;
104+
// Do not use Allocated or this block will show up in iterateOverChunks.
105+
Header.State = Chunk::State::Quarantined;
105106
Chunk::storeHeader(Allocator.Cookie, Ptr, &Header);
106107

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

123-
if (UNLIKELY(Header.State != Chunk::State::Allocated))
124+
if (UNLIKELY(Header.State != Chunk::State::Quarantined))
124125
reportInvalidChunkState(AllocatorAction::Deallocating, Ptr);
125126
DCHECK_EQ(Header.ClassId, QuarantineClassId);
126127
DCHECK_EQ(Header.Offset, 0);

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

Lines changed: 51 additions & 26 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;
@@ -151,12 +152,10 @@ void TestAllocator<Config>::operator delete(void *ptr) {
151152

152153
template <class TypeParam> struct ScudoCombinedTest : public Test {
153154
ScudoCombinedTest() {
154-
UseQuarantine = std::is_same<TypeParam, scudo::AndroidConfig>::value;
155155
Allocator = std::make_unique<AllocatorT>();
156156
}
157157
~ScudoCombinedTest() {
158158
Allocator->releaseToOS(scudo::ReleaseToOS::Force);
159-
UseQuarantine = true;
160159
}
161160

162161
void RunTest();
@@ -525,30 +524,25 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, IterateOverChunks) {
525524
auto *Allocator = this->Allocator.get();
526525
// Allocates a bunch of chunks, then iterate over all the chunks, ensuring
527526
// 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-
}
527+
// other allocated chunk at this point.
528+
std::vector<void *> V;
529+
for (scudo::uptr I = 0; I < 64U; I++)
530+
V.push_back(Allocator->allocate(
531+
static_cast<scudo::uptr>(std::rand()) %
532+
(TypeParam::Primary::SizeClassMap::MaxSize / 2U),
533+
Origin));
534+
Allocator->disable();
535+
Allocator->iterateOverChunks(
536+
0U, static_cast<scudo::uptr>(SCUDO_MMAP_RANGE_SIZE - 1),
537+
[](uintptr_t Base, UNUSED size_t Size, void *Arg) {
538+
std::vector<void *> *V = reinterpret_cast<std::vector<void *> *>(Arg);
539+
void *P = reinterpret_cast<void *>(Base);
540+
EXPECT_NE(std::find(V->begin(), V->end(), P), V->end());
541+
},
542+
reinterpret_cast<void *>(&V));
543+
Allocator->enable();
544+
for (auto P : V)
545+
Allocator->deallocate(P, Origin);
552546
}
553547

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

0 commit comments

Comments
 (0)