Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler-rt/lib/scudo/standalone/allocator_config.def
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ BASE_REQUIRED_TEMPLATE_TYPE(SecondaryT)
// Indicates possible support for Memory Tagging.
BASE_OPTIONAL(const bool, MaySupportMemoryTagging, false)

// Disable the quarantine code.
BASE_OPTIONAL(const bool, QuarantineDisabled, false)

// PRIMARY_REQUIRED_TYPE(NAME)
//
// SizeClassMap to use with the Primary.
Expand Down
12 changes: 12 additions & 0 deletions compiler-rt/lib/scudo/standalone/allocator_config_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ template <typename AllocatorConfig> struct PrimaryConfig {
return BaseConfig<AllocatorConfig>::getMaySupportMemoryTagging();
}

static constexpr bool getQuarantineDisabled() {
return BaseConfig<AllocatorConfig>::getQuarantineDisabled();
}

#define PRIMARY_REQUIRED_TYPE(NAME) \
using NAME = typename AllocatorConfig::Primary::NAME;

Expand Down Expand Up @@ -92,6 +96,10 @@ template <typename AllocatorConfig> struct SecondaryConfig {
return BaseConfig<AllocatorConfig>::getMaySupportMemoryTagging();
}

static constexpr bool getQuarantineDisabled() {
return BaseConfig<AllocatorConfig>::getQuarantineDisabled();
}

#define SECONDARY_REQUIRED_TEMPLATE_TYPE(NAME) \
template <typename T> \
using NAME = typename AllocatorConfig::Secondary::template NAME<T>;
Expand All @@ -111,6 +119,10 @@ template <typename AllocatorConfig> struct SecondaryConfig {
return BaseConfig<AllocatorConfig>::getMaySupportMemoryTagging();
}

static constexpr bool getQuarantineDisabled() {
return BaseConfig<AllocatorConfig>::getQuarantineDisabled();
}

#define SECONDARY_CACHE_OPTIONAL(TYPE, NAME, DEFAULT) \
OPTIONAL_TEMPLATE(TYPE, NAME, DEFAULT, Cache::NAME) \
static constexpr removeConst<TYPE>::type get##NAME() { \
Expand Down
31 changes: 19 additions & 12 deletions compiler-rt/lib/scudo/standalone/combined.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,10 @@ class Allocator {
const s32 ReleaseToOsIntervalMs = getFlags()->release_to_os_interval_ms;
Primary.init(ReleaseToOsIntervalMs);
Secondary.init(&Stats, ReleaseToOsIntervalMs);
Quarantine.init(
static_cast<uptr>(getFlags()->quarantine_size_kb << 10),
static_cast<uptr>(getFlags()->thread_local_quarantine_size_kb << 10));
if (!AllocatorConfig::getQuarantineDisabled())
Quarantine.init(
static_cast<uptr>(getFlags()->quarantine_size_kb << 10),
static_cast<uptr>(getFlags()->thread_local_quarantine_size_kb << 10));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hit: brackets

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

}

void enableRingBuffer() NO_THREAD_SAFETY_ANALYSIS {
Expand Down Expand Up @@ -276,16 +277,18 @@ class Allocator {
// the last two items).
void commitBack(TSD<ThisT> *TSD) {
TSD->assertLocked(/*BypassCheck=*/true);
Quarantine.drain(&TSD->getQuarantineCache(),
QuarantineCallback(*this, TSD->getSizeClassAllocator()));
if (!AllocatorConfig::getQuarantineDisabled())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:brackets

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Quarantine.drain(&TSD->getQuarantineCache(),
QuarantineCallback(*this, TSD->getSizeClassAllocator()));
TSD->getSizeClassAllocator().destroy(&Stats);
}

void drainCache(TSD<ThisT> *TSD) {
TSD->assertLocked(/*BypassCheck=*/true);
Quarantine.drainAndRecycle(
&TSD->getQuarantineCache(),
QuarantineCallback(*this, TSD->getSizeClassAllocator()));
if (!AllocatorConfig::getQuarantineDisabled())
Quarantine.drainAndRecycle(
&TSD->getQuarantineCache(),
QuarantineCallback(*this, TSD->getSizeClassAllocator()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:brackets

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

TSD->getSizeClassAllocator().drain();
}
void drainCaches() { TSDRegistry.drainCaches(this); }
Expand Down Expand Up @@ -612,7 +615,8 @@ class Allocator {
#endif
TSDRegistry.disable();
Stats.disable();
Quarantine.disable();
if (!AllocatorConfig::getQuarantineDisabled())
Quarantine.disable();
Primary.disable();
Secondary.disable();
disableRingBuffer();
Expand All @@ -623,7 +627,8 @@ class Allocator {
enableRingBuffer();
Secondary.enable();
Primary.enable();
Quarantine.enable();
if (!AllocatorConfig::getQuarantineDisabled())
Quarantine.enable();
Stats.enable();
TSDRegistry.enable();
#ifdef GWP_ASAN_HOOKS
Expand Down Expand Up @@ -1252,7 +1257,8 @@ class Allocator {
// If the quarantine is disabled, the actual size of a chunk is 0 or larger
// than the maximum allowed, we return a chunk directly to the backend.
// This purposefully underflows for Size == 0.
const bool BypassQuarantine = !Quarantine.getCacheSize() ||
const bool BypassQuarantine = AllocatorConfig::getQuarantineDisabled() ||
!Quarantine.getCacheSize() ||
((Size - 1) >= QuarantineMaxChunkSize) ||
!Header->ClassId;
if (BypassQuarantine)
Expand Down Expand Up @@ -1642,7 +1648,8 @@ class Allocator {
uptr getStats(ScopedString *Str) {
Primary.getStats(Str);
Secondary.getStats(Str);
Quarantine.getStats(Str);
if (!AllocatorConfig::getQuarantineDisabled())
Quarantine.getStats(Str);
TSDRegistry.getStats(Str);
return Str->length();
}
Expand Down
21 changes: 12 additions & 9 deletions compiler-rt/lib/scudo/standalone/secondary.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class MapAllocatorCache {
break;
}

if (Config::getQuarantineSize()) {
if (!Config::getQuarantineDisabled() && Config::getQuarantineSize()) {
QuarantinePos =
(QuarantinePos + 1) % Max(Config::getQuarantineSize(), 1u);
if (!Quarantine[QuarantinePos].isValid()) {
Expand Down Expand Up @@ -508,14 +508,16 @@ class MapAllocatorCache {

void disableMemoryTagging() EXCLUDES(Mutex) {
ScopedLock L(Mutex);
for (u32 I = 0; I != Config::getQuarantineSize(); ++I) {
if (Quarantine[I].isValid()) {
MemMapT &MemMap = Quarantine[I].MemMap;
unmapCallBack(MemMap);
Quarantine[I].invalidate();
if (!Config::getQuarantineDisabled()) {
for (u32 I = 0; I != Config::getQuarantineSize(); ++I) {
if (Quarantine[I].isValid()) {
MemMapT &MemMap = Quarantine[I].MemMap;
unmapCallBack(MemMap);
Quarantine[I].invalidate();
}
}
QuarantinePos = -1U;
}
QuarantinePos = -1U;

for (CachedBlock &Entry : LRUEntries)
Entry.MemMap.setMemoryPermission(Entry.CommitBase, Entry.CommitSize, 0);
Expand Down Expand Up @@ -575,8 +577,9 @@ class MapAllocatorCache {
if (!LRUEntries.size() || OldestTime == 0 || OldestTime > Time)
return;
OldestTime = 0;
for (uptr I = 0; I < Config::getQuarantineSize(); I++)
releaseIfOlderThan(Quarantine[I], Time);
if (!Config::getQuarantineDisabled())
for (uptr I = 0; I < Config::getQuarantineSize(); I++)
releaseIfOlderThan(Quarantine[I], Time);
for (uptr I = 0; I < Config::getEntriesArraySize(); I++)
releaseIfOlderThan(Entries[I], Time);
}
Expand Down
103 changes: 94 additions & 9 deletions compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -623,20 +623,20 @@ SCUDO_TYPED_TEST(ScudoCombinedDeathTest, DisableMemoryTagging) {
SCUDO_TYPED_TEST(ScudoCombinedTest, Stats) {
auto *Allocator = this->Allocator.get();

scudo::uptr BufferSize = 8192;
std::vector<char> Buffer(BufferSize);
scudo::uptr ActualSize = Allocator->getStats(Buffer.data(), BufferSize);
while (ActualSize > BufferSize) {
BufferSize = ActualSize + 1024;
Buffer.resize(BufferSize);
ActualSize = Allocator->getStats(Buffer.data(), BufferSize);
std::string Stats(10000, '\0');
scudo::uptr ActualSize = Allocator->getStats(Stats.data(), Stats.size());
if (ActualSize > Stats.size()) {
Stats.resize(ActualSize);
ActualSize = Allocator->getStats(Stats.data(), Stats.size());
}
std::string Stats(Buffer.begin(), Buffer.end());
EXPECT_GE(Stats.size(), ActualSize);

// Basic checks on the contents of the statistics output, which also allows us
// to verify that we got it all.
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);
// Do not explicitly check for quarantine stats since a config can disable
// them. Other tests verify this (QuarantineEnabled/QuarantineDisabled).
}

SCUDO_TYPED_TEST_SKIP_THREAD_SAFETY(ScudoCombinedTest, Drain) {
Expand Down Expand Up @@ -1076,3 +1076,88 @@ TEST(ScudoCombinedTest, BasicTrustyConfig) {

#endif
#endif

struct TestQuarantineSizeClassConfig {
static const scudo::uptr NumBits = 1;
static const scudo::uptr MinSizeLog = 10;
static const scudo::uptr MidSizeLog = 10;
static const scudo::uptr MaxSizeLog = 13;
static const scudo::u16 MaxNumCachedHint = 8;
static const scudo::uptr MaxBytesCachedLog = 12;
static const scudo::uptr SizeDelta = 0;
};

struct TestQuarantineConfig {
static const bool MaySupportMemoryTagging = false;

template <class A> using TSDRegistryT = scudo::TSDRegistrySharedT<A, 1U, 1U>;

struct Primary {
// Tiny allocator, its Primary only serves chunks of four sizes.
using SizeClassMap = scudo::FixedSizeClassMap<DeathSizeClassConfig>;
static const scudo::uptr RegionSizeLog = DeathRegionSizeLog;
static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
typedef scudo::uptr CompactPtrT;
static const scudo::uptr CompactPtrScale = 0;
static const bool EnableRandomOffset = true;
static const scudo::uptr MapSizeIncrement = 1UL << 18;
static const scudo::uptr GroupSizeLog = 18;
};
template <typename Config>
using PrimaryT = scudo::SizeClassAllocator64<Config>;

struct Secondary {
template <typename Config>
using CacheT = scudo::MapAllocatorNoCache<Config>;
};

template <typename Config> using SecondaryT = scudo::MapAllocator<Config>;
};

// Verify that the quarantine exists by default.
TEST(ScudoCombinedTest, QuarantineEnabled) {
using AllocatorT = scudo::Allocator<TestQuarantineConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());

const scudo::uptr Size = 1000U;
void *P = Allocator->allocate(Size, Origin);
EXPECT_NE(P, nullptr);
Allocator->deallocate(P, Origin);

std::string Stats(10000, '\0');
scudo::uptr ActualSize = Allocator->getStats(Stats.data(), Stats.size());
if (ActualSize > Stats.size()) {
Stats.resize(ActualSize);
ActualSize = Allocator->getStats(Stats.data(), Stats.size());
}
EXPECT_GE(Stats.size(), ActualSize);

// No quarantine stats should exist.
EXPECT_NE(Stats.find("Stats: Quarantine"), std::string::npos);
}

struct TestQuarantineDisabledConfig : TestQuarantineConfig {
static const bool QuarantineDisabled = true;
};

TEST(ScudoCombinedTest, QuarantineDisabled) {
using AllocatorT = scudo::Allocator<TestQuarantineDisabledConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());

const scudo::uptr Size = 1000U;
void *P = Allocator->allocate(Size, Origin);
EXPECT_NE(P, nullptr);
Allocator->deallocate(P, Origin);

std::string Stats(10000, '\0');
scudo::uptr ActualSize = Allocator->getStats(Stats.data(), Stats.size());
if (ActualSize > Stats.size()) {
Stats.resize(ActualSize);
ActualSize = Allocator->getStats(Stats.data(), Stats.size());
}
EXPECT_GE(Stats.size(), ActualSize);

// No quarantine stats should exist.
EXPECT_EQ(Stats.find("Stats: Quarantine"), std::string::npos);
}
Loading