Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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));
}

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())
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()));
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
11 changes: 7 additions & 4 deletions compiler-rt/lib/scudo/standalone/secondary.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ class MapAllocatorCache {
Entry.MemMap = MemMap;
Entry.Time = UINT64_MAX;

if (useMemoryTagging<Config>(Options)) {
bool MemoryTaggingEnabled = useMemoryTagging<Config>(Options);
if (MemoryTaggingEnabled) {
if (Interval == 0 && !SCUDO_FUCHSIA) {
// Release the memory and make it inaccessible at the same time by
// creating a new MAP_NOACCESS mapping on top of the existing mapping.
Expand Down Expand Up @@ -302,15 +303,16 @@ class MapAllocatorCache {
if (Entry.Time != 0)
Entry.Time = Time;

if (useMemoryTagging<Config>(Options) && QuarantinePos == -1U) {
if (MemoryTaggingEnabled && !useMemoryTagging<Config>(Options)) {
// If we get here then memory tagging was disabled in between when we
// read Options and when we locked Mutex. We can't insert our entry into
// the quarantine or the cache because the permissions would be wrong so
// just unmap it.
unmapCallBack(Entry.MemMap);
break;
}
if (Config::getQuarantineSize() && useMemoryTagging<Config>(Options)) {

if (Config::getQuarantineSize()) {
QuarantinePos =
(QuarantinePos + 1) % Max(Config::getQuarantineSize(), 1u);
if (!Quarantine[QuarantinePos].isValid()) {
Expand Down Expand Up @@ -513,9 +515,10 @@ class MapAllocatorCache {
Quarantine[I].invalidate();
}
}
QuarantinePos = -1U;

for (CachedBlock &Entry : LRUEntries)
Entry.MemMap.setMemoryPermission(Entry.CommitBase, Entry.CommitSize, 0);
QuarantinePos = -1U;
}

void disable() NO_THREAD_SAFETY_ANALYSIS { Mutex.lock(); }
Expand Down
122 changes: 113 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,107 @@ 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>;
};

TEST(ScudoCombinedTest, QuarantineEnabled) {
// Enable quarantine options.
EXPECT_EQ(0, setenv("SCUDO_OPTIONS",
"quarantine_max_chunk_size=65535:quarantine_size_kb=500:"
"thread_local_quarantine_size_kb=600",
true));

using AllocatorT = scudo::Allocator<TestQuarantineConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());

void *P = Allocator->allocate(100U, 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);

// Verify we get quarantine stats.
EXPECT_NE(Stats.find("Stats: Quarantine"), std::string::npos);
// Verify that quarantine values get set properly.
EXPECT_NE(Stats.find("Quarantine limits: global: 500K; thread local: 600K"),
std::string::npos);

// Clear the options for all other tests.
EXPECT_EQ(0, setenv("SCUDO_OPTIONS", "", true));
}

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

TEST(ScudoCombinedTest, QuarantineDisabled) {
// Enable quarantine options.
EXPECT_EQ(0, setenv("SCUDO_OPTIONS",
"quarantine_max_chunk_size=65535:quarantine_size_kb=500:"
"thread_local_quarantine_size_kb=600",
true));

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 because the code is not enabled.
EXPECT_EQ(Stats.find("Stats: Quarantine"), std::string::npos);

// Clear the options for all other tests.
EXPECT_EQ(0, setenv("SCUDO_OPTIONS", "", true));
}
Loading