[compiler-rt] resize stack depot for allocation ring buffer (PR #74515)
Florian Mayer via llvm-commits
llvm-commits at lists.llvm.org
Tue Dec 5 11:06:26 PST 2023
https://github.com/fmayer created https://github.com/llvm/llvm-project/pull/74515
None
>From 9b4f5f3d50e554e845905355a5477af4adad80d7 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Fri, 1 Dec 2023 16:46:29 -0800
Subject: [PATCH] resize stack depot for allocation ring buffer
---
compiler-rt/lib/scudo/standalone/combined.h | 82 ++++++++++++++-----
.../standalone/fuzz/get_error_info_fuzzer.cpp | 19 +++--
compiler-rt/lib/scudo/standalone/platform.h | 10 ---
.../lib/scudo/standalone/stack_depot.h | 80 +++++++++++-------
.../scudo/standalone/wrappers_c_bionic.cpp | 2 +-
5 files changed, 122 insertions(+), 71 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 72e5caa026e4d..c6027ae25f318 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -14,6 +14,7 @@
#include "flags.h"
#include "flags_parser.h"
#include "local_cache.h"
+#include "mem_map.h"
#include "memtag.h"
#include "options.h"
#include "quarantine.h"
@@ -289,7 +290,9 @@ class Allocator {
uptr Size =
android_unsafe_frame_pointer_chase(Stack, MaxTraceSize + DiscardFrames);
Size = Min<uptr>(Size, MaxTraceSize + DiscardFrames);
- return Depot.insert(Stack + Min<uptr>(DiscardFrames, Size), Stack + Size);
+ return reinterpret_cast<StackDepot *>(RawStackDepot)
+ ->insert(RawStackDepot, Stack + Min<uptr>(DiscardFrames, Size),
+ Stack + Size);
#else
return 0;
#endif
@@ -916,8 +919,14 @@ class Allocator {
Primary.Options.clear(OptionBit::AddLargeAllocationSlack);
}
- const char *getStackDepotAddress() const {
- return reinterpret_cast<const char *>(&Depot);
+ const char *getStackDepotAddress() {
+ initThreadMaybe();
+ return RawStackDepot;
+ }
+
+ uptr getStackDepotSize() {
+ initThreadMaybe();
+ return StackDepotSize;
}
const char *getRegionInfoArrayAddress() const {
@@ -954,13 +963,14 @@ class Allocator {
static const uptr MaxTraceSize = 64;
- static void collectTraceMaybe(const StackDepot *Depot,
+ static void collectTraceMaybe(const char *RawStackDepot,
uintptr_t (&Trace)[MaxTraceSize], u32 Hash) {
+ auto *Depot = reinterpret_cast<const StackDepot *>(RawStackDepot);
uptr RingPos, Size;
- if (!Depot->find(Hash, &RingPos, &Size))
+ if (!Depot->find(RawStackDepot, Hash, &RingPos, &Size))
return;
for (unsigned I = 0; I != Size && I != MaxTraceSize; ++I)
- Trace[I] = static_cast<uintptr_t>((*Depot)[RingPos + I]);
+ Trace[I] = static_cast<uintptr_t>(Depot->at(RawStackDepot, RingPos + I));
}
static void getErrorInfo(struct scudo_error_info *ErrorInfo,
@@ -973,25 +983,24 @@ class Allocator {
MemoryAddr + MemorySize < MemoryAddr)
return;
- auto *Depot = reinterpret_cast<const StackDepot *>(DepotPtr);
size_t NextErrorReport = 0;
// Check for OOB in the current block and the two surrounding blocks. Beyond
// that, UAF is more likely.
if (extractTag(FaultAddr) != 0)
- getInlineErrorInfo(ErrorInfo, NextErrorReport, FaultAddr, Depot,
+ getInlineErrorInfo(ErrorInfo, NextErrorReport, FaultAddr, DepotPtr,
RegionInfoPtr, Memory, MemoryTags, MemoryAddr,
MemorySize, 0, 2);
// Check the ring buffer. For primary allocations this will only find UAF;
// for secondary allocations we can find either UAF or OOB.
- getRingBufferErrorInfo(ErrorInfo, NextErrorReport, FaultAddr, Depot,
+ getRingBufferErrorInfo(ErrorInfo, NextErrorReport, FaultAddr, DepotPtr,
RingBufferPtr);
// Check for OOB in the 28 blocks surrounding the 3 we checked earlier.
// Beyond that we are likely to hit false positives.
if (extractTag(FaultAddr) != 0)
- getInlineErrorInfo(ErrorInfo, NextErrorReport, FaultAddr, Depot,
+ getInlineErrorInfo(ErrorInfo, NextErrorReport, FaultAddr, DepotPtr,
RegionInfoPtr, Memory, MemoryTags, MemoryAddr,
MemorySize, 2, 16);
}
@@ -1039,7 +1048,8 @@ class Allocator {
uptr GuardedAllocSlotSize = 0;
#endif // GWP_ASAN_HOOKS
- StackDepot Depot;
+ char *RawStackDepot = nullptr;
+ uptr StackDepotSize = 0;
struct AllocationRingBuffer {
struct Entry {
@@ -1255,7 +1265,8 @@ class Allocator {
}
void storePrimaryAllocationStackMaybe(const Options &Options, void *Ptr) {
- if (!UNLIKELY(Options.get(OptionBit::TrackAllocationStacks)))
+ if (!UNLIKELY(Options.get(OptionBit::TrackAllocationStacks)) ||
+ !RawRingBuffer)
return;
auto *Ptr32 = reinterpret_cast<u32 *>(Ptr);
Ptr32[MemTagAllocationTraceIndex] = collectStackTrace();
@@ -1288,7 +1299,8 @@ class Allocator {
void storeSecondaryAllocationStackMaybe(const Options &Options, void *Ptr,
uptr Size) {
- if (!UNLIKELY(Options.get(OptionBit::TrackAllocationStacks)))
+ if (!UNLIKELY(Options.get(OptionBit::TrackAllocationStacks)) ||
+ !RawRingBuffer)
return;
u32 Trace = collectStackTrace();
@@ -1303,7 +1315,8 @@ class Allocator {
void storeDeallocationStackMaybe(const Options &Options, void *Ptr,
u8 PrevTag, uptr Size) {
- if (!UNLIKELY(Options.get(OptionBit::TrackAllocationStacks)))
+ if (!UNLIKELY(Options.get(OptionBit::TrackAllocationStacks)) ||
+ !RawRingBuffer)
return;
auto *Ptr32 = reinterpret_cast<u32 *>(Ptr);
@@ -1324,7 +1337,7 @@ class Allocator {
static void getInlineErrorInfo(struct scudo_error_info *ErrorInfo,
size_t &NextErrorReport, uintptr_t FaultAddr,
- const StackDepot *Depot,
+ const char *RawStackDepot,
const char *RegionInfoPtr, const char *Memory,
const char *MemoryTags, uintptr_t MemoryAddr,
size_t MemorySize, size_t MinDistance,
@@ -1389,8 +1402,10 @@ class Allocator {
UntaggedFaultAddr < ChunkAddr ? BUFFER_UNDERFLOW : BUFFER_OVERFLOW;
R->allocation_address = ChunkAddr;
R->allocation_size = Header.SizeOrUnusedBytes;
- collectTraceMaybe(Depot, R->allocation_trace,
- Data[MemTagAllocationTraceIndex]);
+ if (RawStackDepot) {
+ collectTraceMaybe(RawStackDepot, R->allocation_trace,
+ Data[MemTagAllocationTraceIndex]);
+ }
R->allocation_tid = Data[MemTagAllocationTidIndex];
return NextErrorReport == NumErrorReports;
};
@@ -1407,11 +1422,11 @@ class Allocator {
static void getRingBufferErrorInfo(struct scudo_error_info *ErrorInfo,
size_t &NextErrorReport,
uintptr_t FaultAddr,
- const StackDepot *Depot,
+ const char *RawStackDepot,
const char *RingBufferPtr) {
auto *RingBuffer =
reinterpret_cast<const AllocationRingBuffer *>(RingBufferPtr);
- if (!RingBuffer || RingBuffer->Size == 0)
+ if (!RingBuffer || RingBuffer->Size == 0 || !RawStackDepot)
return;
uptr Pos = atomic_load_relaxed(&RingBuffer->Pos);
@@ -1470,9 +1485,10 @@ class Allocator {
R->allocation_address = UntaggedEntryPtr;
R->allocation_size = EntrySize;
- collectTraceMaybe(Depot, R->allocation_trace, AllocationTrace);
+ collectTraceMaybe(RawStackDepot, R->allocation_trace, AllocationTrace);
R->allocation_tid = AllocationTid;
- collectTraceMaybe(Depot, R->deallocation_trace, DeallocationTrace);
+ collectTraceMaybe(RawStackDepot, R->deallocation_trace,
+ DeallocationTrace);
R->deallocation_tid = DeallocationTid;
}
}
@@ -1501,6 +1517,27 @@ class Allocator {
return;
u32 AllocationRingBufferSize =
static_cast<u32>(getFlags()->allocation_ring_buffer_size);
+ // We store alloc and free stacks for each entry.
+ constexpr auto kStacksPerRingBufferEntry = 2;
+ u32 TabSize = static_cast<u32>(roundUpPowerOfTwo(kStacksPerRingBufferEntry *
+ AllocationRingBufferSize));
+ constexpr auto kFramesPerStack = 8;
+ static_assert(isPowerOfTwo(kFramesPerStack));
+ u32 RingSize = static_cast<u32>(TabSize * kFramesPerStack);
+ DCHECK(isPowerOfTwo(RingSize));
+ static_assert(sizeof(StackDepot) % alignof(atomic_u64) == 0);
+
+ StackDepotSize =
+ roundUp(sizeof(StackDepot) + sizeof(atomic_u64) * RingSize +
+ sizeof(atomic_u32) * TabSize,
+ getPageSizeCached());
+ MemMapT DepotMap;
+ DepotMap.map(
+ /*Addr=*/0U, StackDepotSize, "scudo:stack_depot");
+ RawStackDepot = reinterpret_cast<char *>(DepotMap.getBase());
+ auto *Depot = reinterpret_cast<StackDepot *>(DepotMap.getBase());
+ Depot->init(DepotMap, RingSize, TabSize);
+
MemMapT MemMap;
MemMap.map(
/*Addr=*/0U,
@@ -1524,6 +1561,9 @@ class Allocator {
MemMap.unmap(MemMap.getBase(), MemMap.getCapacity());
}
RawRingBuffer = nullptr;
+ if (RawStackDepot) {
+ reinterpret_cast<StackDepot *>(RawStackDepot)->free();
+ }
}
static constexpr size_t ringBufferSizeInBytes(u32 AllocationRingBufferSize) {
diff --git a/compiler-rt/lib/scudo/standalone/fuzz/get_error_info_fuzzer.cpp b/compiler-rt/lib/scudo/standalone/fuzz/get_error_info_fuzzer.cpp
index 74456450a4761..dd3da623ca5e9 100644
--- a/compiler-rt/lib/scudo/standalone/fuzz/get_error_info_fuzzer.cpp
+++ b/compiler-rt/lib/scudo/standalone/fuzz/get_error_info_fuzzer.cpp
@@ -29,13 +29,13 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *Data, size_t Size) {
size_t MemorySize = (MemoryAndTags.length() / 17) * 16;
const char *MemoryTags = Memory + MemorySize;
- std::string StackDepotBytes =
+ scudo::StackDepot Depot;
+ std::string DepotRingBytes =
FDP.ConsumeRandomLengthString(FDP.remaining_bytes());
- std::vector<char> StackDepot(sizeof(scudo::StackDepot), 0);
- for (size_t i = 0; i < StackDepotBytes.length() && i < StackDepot.size();
- ++i) {
- StackDepot[i] = StackDepotBytes[i];
- }
+ std::string DepotTabBytes =
+ FDP.ConsumeRandomLengthString(FDP.remaining_bytes());
+ Depot.SetRingForFuzzing(DepotRingBytes.c_str(), DepotRingBytes.size());
+ Depot.SetTabForFuzzing(DepotTabBytes.c_str(), DepotTabBytes.size());
std::string RegionInfoBytes =
FDP.ConsumeRandomLengthString(FDP.remaining_bytes());
@@ -52,8 +52,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *Data, size_t Size) {
return 0;
scudo_error_info ErrorInfo;
- AllocatorT::getErrorInfo(&ErrorInfo, FaultAddr, StackDepot.data(),
- RegionInfo.data(), RingBufferBytes.data(), Memory,
- MemoryTags, MemoryAddr, MemorySize);
+ AllocatorT::getErrorInfo(&ErrorInfo, FaultAddr,
+ reinterpret_cast<char *>(&Depot), RegionInfo.data(),
+ RingBufferBytes.data(), Memory, MemoryTags,
+ MemoryAddr, MemorySize);
return 0;
}
diff --git a/compiler-rt/lib/scudo/standalone/platform.h b/compiler-rt/lib/scudo/standalone/platform.h
index b71a86be7669f..5af1275e32d2b 100644
--- a/compiler-rt/lib/scudo/standalone/platform.h
+++ b/compiler-rt/lib/scudo/standalone/platform.h
@@ -63,16 +63,6 @@
#define SCUDO_CAN_USE_MTE (SCUDO_LINUX || SCUDO_TRUSTY)
#endif
-// Use smaller table sizes for fuzzing in order to reduce input size.
-// Trusty just has less available memory.
-#ifndef SCUDO_SMALL_STACK_DEPOT
-#if defined(SCUDO_FUZZ) || SCUDO_TRUSTY
-#define SCUDO_SMALL_STACK_DEPOT 1
-#else
-#define SCUDO_SMALL_STACK_DEPOT 0
-#endif
-#endif
-
#ifndef SCUDO_ENABLE_HOOKS
#define SCUDO_ENABLE_HOOKS 0
#endif
diff --git a/compiler-rt/lib/scudo/standalone/stack_depot.h b/compiler-rt/lib/scudo/standalone/stack_depot.h
index 12c35eb2a4f33..b812658c95463 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -10,6 +10,8 @@
#define SCUDO_STACK_DEPOT_H_
#include "atomic_helpers.h"
+#include "common.h"
+#include "mem_map.h"
#include "mutex.h"
namespace scudo {
@@ -38,7 +40,7 @@ class MurMur2HashBuilder {
}
};
-class StackDepot {
+class alignas(16) StackDepot {
HybridMutex RingEndMu;
u32 RingEnd = 0;
@@ -62,47 +64,62 @@ class StackDepot {
// This is achieved by re-checking the hash of the stack trace before
// returning the trace.
-#if SCUDO_SMALL_STACK_DEPOT
- static const uptr TabBits = 4;
-#else
- static const uptr TabBits = 16;
-#endif
- static const uptr TabSize = 1 << TabBits;
- static const uptr TabMask = TabSize - 1;
- atomic_u32 Tab[TabSize] = {};
-
-#if SCUDO_SMALL_STACK_DEPOT
- static const uptr RingBits = 4;
-#else
- static const uptr RingBits = 19;
-#endif
- static const uptr RingSize = 1 << RingBits;
- static const uptr RingMask = RingSize - 1;
- atomic_u64 Ring[RingSize] = {};
+ MemMapT DepotMap;
+ uptr RingSize = 0;
+ uptr RingMask = 0;
+ uptr TabMask = 0;
+
+ atomic_u64 *Ring(char *RawStackDepot) {
+ return reinterpret_cast<atomic_u64 *>(RawStackDepot + sizeof(StackDepot));
+ }
+
+ atomic_u32 *Tab(char *RawStackDepot) {
+ return reinterpret_cast<atomic_u32 *>(RawStackDepot + sizeof(StackDepot) +
+ sizeof(atomic_u64) * RingSize);
+ }
+
+ const atomic_u64 *Ring(const char *RawStackDepot) const {
+ return reinterpret_cast<const atomic_u64 *>(RawStackDepot +
+ sizeof(StackDepot));
+ }
+
+ const atomic_u32 *Tab(const char *RawStackDepot) const {
+ return reinterpret_cast<const atomic_u32 *>(
+ RawStackDepot + sizeof(StackDepot) + sizeof(atomic_u64) * RingSize);
+ }
public:
+ void init(MemMapT Map, uptr RingSz, uptr TabSz) {
+ DCHECK(isPowerOfTwo(RingSz));
+ DCHECK(isPowerOfTwo(TabSz));
+ DepotMap = Map;
+ RingSize = RingSz;
+ RingMask = RingSz - 1;
+ TabMask = TabSz - 1;
+ }
+
// Insert hash of the stack trace [Begin, End) into the stack depot, and
// return the hash.
- u32 insert(uptr *Begin, uptr *End) {
+ u32 insert(char *RawStackDepot, uptr *Begin, uptr *End) {
MurMur2HashBuilder B;
for (uptr *I = Begin; I != End; ++I)
B.add(u32(*I) >> 2);
u32 Hash = B.get();
u32 Pos = Hash & TabMask;
- u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
- u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
+ u32 RingPos = atomic_load_relaxed(&Tab(RawStackDepot)[Pos]);
+ u64 Entry = atomic_load_relaxed(&Ring(RawStackDepot)[RingPos]);
u64 Id = (u64(End - Begin) << 33) | (u64(Hash) << 1) | 1;
if (Entry == Id)
return Hash;
ScopedLock Lock(RingEndMu);
RingPos = RingEnd;
- atomic_store_relaxed(&Tab[Pos], RingPos);
- atomic_store_relaxed(&Ring[RingPos], Id);
+ atomic_store_relaxed(&Tab(RawStackDepot)[Pos], RingPos);
+ atomic_store_relaxed(&Ring(RawStackDepot)[RingPos], Id);
for (uptr *I = Begin; I != End; ++I) {
RingPos = (RingPos + 1) & RingMask;
- atomic_store_relaxed(&Ring[RingPos], *I);
+ atomic_store_relaxed(&Ring(RawStackDepot)[RingPos], *I);
}
RingEnd = (RingPos + 1) & RingMask;
return Hash;
@@ -111,12 +128,13 @@ class StackDepot {
// Look up a stack trace by hash. Returns true if successful. The trace may be
// accessed via operator[] passing indexes between *RingPosPtr and
// *RingPosPtr + *SizePtr.
- bool find(u32 Hash, uptr *RingPosPtr, uptr *SizePtr) const {
+ bool find(const char *RawStackDepot, u32 Hash, uptr *RingPosPtr,
+ uptr *SizePtr) const {
u32 Pos = Hash & TabMask;
- u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
+ u32 RingPos = atomic_load_relaxed(&Tab(RawStackDepot)[Pos]);
if (RingPos >= RingSize)
return false;
- u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
+ u64 Entry = atomic_load_relaxed(&Ring(RawStackDepot)[RingPos]);
u64 HashWithTagBit = (u64(Hash) << 1) | 1;
if ((Entry & 0x1ffffffff) != HashWithTagBit)
return false;
@@ -128,13 +146,15 @@ class StackDepot {
MurMur2HashBuilder B;
for (uptr I = 0; I != Size; ++I) {
RingPos = (RingPos + 1) & RingMask;
- B.add(u32(atomic_load_relaxed(&Ring[RingPos])) >> 2);
+ B.add(u32(atomic_load_relaxed(&Ring(RawStackDepot)[RingPos])) >> 2);
}
return B.get() == Hash;
}
- u64 operator[](uptr RingPos) const {
- return atomic_load_relaxed(&Ring[RingPos & RingMask]);
+ void free() { DepotMap.unmap(DepotMap.getBase(), DepotMap.getCapacity()); }
+
+ u64 at(const char *RawStackDepot, uptr RingPos) const {
+ return atomic_load_relaxed(&Ring(RawStackDepot)[RingPos & RingMask]);
}
};
diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp b/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
index 4fed44779b902..ee78b76efd310 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
@@ -54,7 +54,7 @@ INTERFACE const char *__scudo_get_stack_depot_addr() {
}
INTERFACE size_t __scudo_get_stack_depot_size() {
- return sizeof(scudo::StackDepot);
+ return Allocator.getStackDepotSize();
}
INTERFACE const char *__scudo_get_region_info_addr() {
More information about the llvm-commits
mailing list