[compiler-rt] [scudo] [MTE] resize stack depot for allocation ring buffer (PR #74515)
Florian Mayer via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 23 16:20:49 PST 2024
https://github.com/fmayer updated https://github.com/llvm/llvm-project/pull/74515
>From 267365a96f1beb64c28928565958058beb838036 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 1/8] [scudo] resize stack depot for allocation ring buffer
---
compiler-rt/lib/scudo/standalone/combined.h | 101 +++++++++++----
.../standalone/fuzz/get_error_info_fuzzer.cpp | 14 +--
compiler-rt/lib/scudo/standalone/platform.h | 10 --
.../lib/scudo/standalone/stack_depot.h | 118 +++++++++++++-----
.../scudo/standalone/wrappers_c_bionic.cpp | 9 +-
5 files changed, 173 insertions(+), 79 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 4624f83d142a0d..49cdc44cd80d26 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -290,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
@@ -917,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 {
@@ -941,45 +949,54 @@ 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,
uintptr_t FaultAddr, const char *DepotPtr,
- const char *RegionInfoPtr, const char *RingBufferPtr,
- size_t RingBufferSize, const char *Memory,
- const char *MemoryTags, uintptr_t MemoryAddr,
- size_t MemorySize) {
+ size_t DepotSize, const char *RegionInfoPtr,
+ const char *RingBufferPtr, size_t RingBufferSize,
+ const char *Memory, const char *MemoryTags,
+ uintptr_t MemoryAddr, size_t MemorySize) {
*ErrorInfo = {};
if (!allocatorSupportsMemoryTagging<Config>() ||
MemoryAddr + MemorySize < MemoryAddr)
return;
- auto *Depot = reinterpret_cast<const StackDepot *>(DepotPtr);
+ if (DepotPtr && DepotSize < sizeof(StackDepot)) {
+ return;
+ }
+ if (DepotPtr &&
+ !reinterpret_cast<const StackDepot *>(DepotPtr)->isValid(DepotSize)) {
+ // corrupted stack depot.
+ return;
+ }
+
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, RingBufferSize);
// 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);
}
@@ -1027,7 +1044,9 @@ class Allocator {
uptr GuardedAllocSlotSize = 0;
#endif // GWP_ASAN_HOOKS
- StackDepot Depot;
+ char *RawStackDepot = nullptr;
+ uptr StackDepotSize = 0;
+ MemMapT RawStackDepotMap;
struct AllocationRingBuffer {
struct Entry {
@@ -1243,7 +1262,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();
@@ -1276,7 +1296,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();
@@ -1291,7 +1312,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);
@@ -1312,7 +1334,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,
@@ -1377,8 +1399,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;
};
@@ -1395,13 +1419,13 @@ 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,
size_t RingBufferSize) {
auto *RingBuffer =
reinterpret_cast<const AllocationRingBuffer *>(RingBufferPtr);
size_t RingBufferElements = ringBufferElementsFromBytes(RingBufferSize);
- if (!RingBuffer || RingBufferElements == 0)
+ if (!RingBuffer || RingBufferElements == 0 || !RawStackDepot)
return;
uptr Pos = atomic_load_relaxed(&RingBuffer->Pos);
@@ -1460,9 +1484,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;
}
}
@@ -1491,6 +1516,28 @@ 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 = sizeof(StackDepot) + sizeof(atomic_u64) * RingSize +
+ sizeof(atomic_u32) * TabSize;
+ MemMapT DepotMap;
+ DepotMap.map(
+ /*Addr=*/0U, roundUp(StackDepotSize, getPageSizeCached()),
+ "scudo:stack_depot");
+ RawStackDepot = reinterpret_cast<char *>(DepotMap.getBase());
+ auto *Depot = reinterpret_cast<StackDepot *>(DepotMap.getBase());
+ Depot->init(RingSize, TabSize);
+ DCHECK(Depot->isValid(StackDepotSize));
+ RawStackDepotMap = DepotMap;
+
MemMapT MemMap;
MemMap.map(
/*Addr=*/0U,
@@ -1513,6 +1560,10 @@ class Allocator {
RawRingBufferMap.getCapacity());
}
RawRingBuffer = nullptr;
+ if (RawStackDepot) {
+ RawStackDepotMap.unmap(RawStackDepotMap.getBase(),
+ RawStackDepotMap.getCapacity());
+ }
}
static constexpr size_t ringBufferSizeInBytes(u32 RingBufferElements) {
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 5b01ebe11c0952..2cef1c44fadc9b 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
@@ -9,6 +9,7 @@
#define SCUDO_FUZZ
#include "allocator_config.h"
#include "combined.h"
+#include "common.h"
#include <fuzzer/FuzzedDataProvider.h>
@@ -31,11 +32,6 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *Data, size_t Size) {
std::string StackDepotBytes =
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 RegionInfoBytes =
FDP.ConsumeRandomLengthString(FDP.remaining_bytes());
@@ -48,9 +44,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *Data, size_t Size) {
std::string RingBufferBytes = FDP.ConsumeRemainingBytesAsString();
scudo_error_info ErrorInfo;
- AllocatorT::getErrorInfo(&ErrorInfo, FaultAddr, StackDepot.data(),
- RegionInfo.data(), RingBufferBytes.data(),
- RingBufferBytes.size(), Memory, MemoryTags,
- MemoryAddr, MemorySize);
+ AllocatorT::getErrorInfo(&ErrorInfo, FaultAddr, StackDepotBytes.data(),
+ StackDepotBytes.size(), RegionInfo.data(),
+ RingBufferBytes.data(), RingBufferBytes.size(),
+ 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 b71a86be7669f3..5af1275e32d2b6 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 12c35eb2a4f338..9508e0a9ec755a 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -10,6 +10,7 @@
#define SCUDO_STACK_DEPOT_H_
#include "atomic_helpers.h"
+#include "common.h"
#include "mutex.h"
namespace scudo {
@@ -38,7 +39,7 @@ class MurMur2HashBuilder {
}
};
-class StackDepot {
+class alignas(16) StackDepot {
HybridMutex RingEndMu;
u32 RingEnd = 0;
@@ -62,47 +63,103 @@ 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] = {};
+ uptr RingSize = 0;
+ uptr RingMask = 0;
+ uptr TabMask = 0;
+ // This is immediately followed by RingSize atomic_u64 and
+ // (TabMask + 1) atomic_u32.
+
+ 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(uptr RingSz, uptr TabSz) {
+ DCHECK(isPowerOfTwo(RingSz));
+ DCHECK(isPowerOfTwo(TabSz));
+ RingSize = RingSz;
+ RingMask = RingSz - 1;
+ TabMask = TabSz - 1;
+ }
+
+ // Ensure that RingSize, RingMask and TabMask are set up in a way that
+ // all accesses are within range of BufSize.
+ bool isValid(uptr BufSize) const {
+ if (RingSize > UINTPTR_MAX / sizeof(atomic_u64)) {
+ return false;
+ }
+ if (RingSize == 0 || !isPowerOfTwo(RingSize)) {
+ return false;
+ }
+ uptr RingBytes = sizeof(atomic_u64) * RingSize;
+ if (RingMask + 1 != RingSize) {
+ return false;
+ }
+
+ if (TabMask == 0) {
+ return false;
+ }
+ if ((TabMask - 1) > UINTPTR_MAX / sizeof(atomic_u32)) {
+ return false;
+ }
+ uptr TabSize = TabMask + 1;
+ if (!isPowerOfTwo(TabSize)) {
+ return false;
+ }
+ uptr TabBytes = sizeof(atomic_u32) * TabSize;
+
+ // Subtract and detect underflow.
+ if (BufSize < sizeof(StackDepot)) {
+ return false;
+ }
+ BufSize -= sizeof(StackDepot);
+ if (BufSize < TabBytes) {
+ return false;
+ }
+ BufSize = TabBytes;
+ if (BufSize < RingBytes) {
+ return false;
+ }
+ return BufSize == 0;
+ }
+
// 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 +168,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 +186,13 @@ 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]);
+ 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 21694c3f17fe57..e9d8c1e8d3db2f 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c_bionic.cpp
@@ -43,10 +43,9 @@ INTERFACE void __scudo_get_error_info(
const char *stack_depot, size_t stack_depot_size, const char *region_info,
const char *ring_buffer, size_t ring_buffer_size, const char *memory,
const char *memory_tags, uintptr_t memory_addr, size_t memory_size) {
- (void)(stack_depot_size);
- Allocator.getErrorInfo(error_info, fault_addr, stack_depot, region_info,
- ring_buffer, ring_buffer_size, memory, memory_tags,
- memory_addr, memory_size);
+ Allocator.getErrorInfo(error_info, fault_addr, stack_depot, stack_depot_size,
+ region_info, ring_buffer, ring_buffer_size, memory,
+ memory_tags, memory_addr, memory_size);
}
INTERFACE const char *__scudo_get_stack_depot_addr() {
@@ -54,7 +53,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() {
>From 9d0169bde9f5cf3718612f5d63058211feae3c72 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Wed, 13 Dec 2023 13:00:15 -0800
Subject: [PATCH 2/8] comments
---
compiler-rt/lib/scudo/standalone/combined.h | 27 +++++++++----
.../lib/scudo/standalone/stack_depot.h | 31 ++++++---------
.../scudo/standalone/tests/combined_test.cpp | 38 ++++++++++++++++++-
3 files changed, 67 insertions(+), 29 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 49cdc44cd80d26..cabc51339505cb 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -9,6 +9,7 @@
#ifndef SCUDO_COMBINED_H_
#define SCUDO_COMBINED_H_
+#include "atomic_helpers.h"
#include "chunk.h"
#include "common.h"
#include "flags.h"
@@ -965,18 +966,21 @@ class Allocator {
const char *RingBufferPtr, size_t RingBufferSize,
const char *Memory, const char *MemoryTags,
uintptr_t MemoryAddr, size_t MemorySize) {
+ // N.B. we need to support corrupted data in any of the buffers here. We get
+ // this information from an external process (the crashing process) that
+ // shouldn't be able to crash crash_dump.
*ErrorInfo = {};
if (!allocatorSupportsMemoryTagging<Config>() ||
MemoryAddr + MemorySize < MemoryAddr)
return;
- if (DepotPtr && DepotSize < sizeof(StackDepot)) {
- return;
- }
- if (DepotPtr &&
- !reinterpret_cast<const StackDepot *>(DepotPtr)->isValid(DepotSize)) {
- // corrupted stack depot.
- return;
+ if (DepotPtr) {
+ // check for corrupted StackDepot. First we need to check whether we can
+ // read the metadata, then whether the metadata matches the size.
+ if (DepotSize < sizeof(StackDepot))
+ return;
+ if (!reinterpret_cast<const StackDepot *>(DepotPtr)->isValid(DepotSize))
+ return;
}
size_t NextErrorReport = 0;
@@ -1518,14 +1522,23 @@ class Allocator {
static_cast<u32>(getFlags()->allocation_ring_buffer_size);
// We store alloc and free stacks for each entry.
constexpr auto kStacksPerRingBufferEntry = 2;
+ constexpr auto kMaxU32Pow2 = ~(UINT32_MAX >> 1);
+ static_assert(isPowerOfTwo(kMaxU32Pow2));
+ if (AllocationRingBufferSize > kMaxU32Pow2 / kStacksPerRingBufferEntry)
+ return;
u32 TabSize = static_cast<u32>(roundUpPowerOfTwo(kStacksPerRingBufferEntry *
AllocationRingBufferSize));
constexpr auto kFramesPerStack = 8;
static_assert(isPowerOfTwo(kFramesPerStack));
+ if (TabSize > UINT32_MAX / kFramesPerStack)
+ return;
u32 RingSize = static_cast<u32>(TabSize * kFramesPerStack);
DCHECK(isPowerOfTwo(RingSize));
static_assert(sizeof(StackDepot) % alignof(atomic_u64) == 0);
+ static_assert(sizeof(StackDepot) + UINT32_MAX * sizeof(atomic_u64) *
+ UINT32_MAX * sizeof(atomic_u32) <
+ UINTPTR_MAX);
StackDepotSize = sizeof(StackDepot) + sizeof(atomic_u64) * RingSize +
sizeof(atomic_u32) * TabSize;
MemMapT DepotMap;
diff --git a/compiler-rt/lib/scudo/standalone/stack_depot.h b/compiler-rt/lib/scudo/standalone/stack_depot.h
index 9508e0a9ec755a..072eb80f8157db 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -100,42 +100,33 @@ class alignas(16) StackDepot {
// Ensure that RingSize, RingMask and TabMask are set up in a way that
// all accesses are within range of BufSize.
bool isValid(uptr BufSize) const {
- if (RingSize > UINTPTR_MAX / sizeof(atomic_u64)) {
+ if (RingSize > UINTPTR_MAX / sizeof(atomic_u64))
return false;
- }
- if (RingSize == 0 || !isPowerOfTwo(RingSize)) {
+ if (RingSize == 0 || !isPowerOfTwo(RingSize))
return false;
- }
uptr RingBytes = sizeof(atomic_u64) * RingSize;
- if (RingMask + 1 != RingSize) {
+ if (RingMask + 1 != RingSize)
return false;
- }
- if (TabMask == 0) {
+ if (TabMask == 0)
return false;
- }
- if ((TabMask - 1) > UINTPTR_MAX / sizeof(atomic_u32)) {
+ if ((TabMask - 1) > UINTPTR_MAX / sizeof(atomic_u32))
return false;
- }
uptr TabSize = TabMask + 1;
- if (!isPowerOfTwo(TabSize)) {
+ if (!isPowerOfTwo(TabSize))
return false;
- }
uptr TabBytes = sizeof(atomic_u32) * TabSize;
// Subtract and detect underflow.
- if (BufSize < sizeof(StackDepot)) {
+ if (BufSize < sizeof(StackDepot))
return false;
- }
BufSize -= sizeof(StackDepot);
- if (BufSize < TabBytes) {
+ if (BufSize < TabBytes)
return false;
- }
- BufSize = TabBytes;
- if (BufSize < RingBytes) {
+ BufSize -= TabBytes;
+ if (BufSize < RingBytes)
return false;
- }
- return BufSize == 0;
+ return BufSize == RingBytes;
}
// Insert hash of the stack trace [Begin, End) into the stack depot, and
diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index 3dbd93cacefd68..80898e5a10083c 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "memtag.h"
+#include "stack_depot.h"
#include "tests/scudo_unit_test.h"
#include "allocator_config.h"
@@ -860,8 +861,8 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ReallocateInPlaceStress) {
SCUDO_TYPED_TEST(ScudoCombinedTest, RingBufferSize) {
auto *Allocator = this->Allocator.get();
auto Size = Allocator->getRingBufferSize();
- if (Size > 0)
- EXPECT_EQ(Allocator->getRingBufferAddress()[Size - 1], '\0');
+ ASSERT_GT(Size, 0);
+ EXPECT_EQ(Allocator->getRingBufferAddress()[Size - 1], '\0');
}
SCUDO_TYPED_TEST(ScudoCombinedTest, RingBufferAddress) {
@@ -871,6 +872,39 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, RingBufferAddress) {
EXPECT_EQ(Addr, Allocator->getRingBufferAddress());
}
+SCUDO_TYPED_TEST(ScudoCombinedTest, StackDepotSize) {
+ auto *Allocator = this->Allocator.get();
+ auto Size = Allocator->getStackDepotSize();
+ ASSERT_GT(Size, 0);
+ EXPECT_EQ(Allocator->getStackDepotAddress()[Size - 1], '\0');
+}
+
+SCUDO_TYPED_TEST(ScudoCombinedTest, StackDepotAddress) {
+ auto *Allocator = this->Allocator.get();
+ auto *Addr = Allocator->getStackDepotAddress();
+ EXPECT_NE(Addr, nullptr);
+ EXPECT_EQ(Addr, Allocator->getStackDepotAddress());
+}
+
+SCUDO_TYPED_TEST(ScudoCombinedTest, StackDepot) {
+ alignas(scudo::StackDepot) char Buf[sizeof(scudo::StackDepot) +
+ 1024 * sizeof(scudo::atomic_u64) +
+ 1024 * sizeof(scudo::atomic_u32)] = {};
+ auto *Depot = reinterpret_cast<scudo::StackDepot *>(Buf);
+ Depot->init(1024, 1024);
+ ASSERT_TRUE(Depot->isValid(sizeof(Buf)));
+ ASSERT_FALSE(Depot->isValid(sizeof(Buf) - 1));
+ scudo::uptr Stack[] = {1, 2, 3};
+ scudo::u32 Elem = Depot->insert(Buf, &Stack[0], &Stack[3]);
+ scudo::uptr RingPosPtr = 0;
+ scudo::uptr SizePtr = 0;
+ ASSERT_TRUE(Depot->find(Buf, Elem, &RingPosPtr, &SizePtr));
+ ASSERT_EQ(SizePtr, 3);
+ EXPECT_EQ(Depot->at(Buf, RingPosPtr), 1);
+ EXPECT_EQ(Depot->at(Buf, RingPosPtr + 1), 2);
+ EXPECT_EQ(Depot->at(Buf, RingPosPtr + 2), 3);
+}
+
#if SCUDO_CAN_USE_PRIMARY64
#if SCUDO_TRUSTY
>From 6d7d99663af3f0c025ff6d50758f215c2db96f73 Mon Sep 17 00:00:00 2001
From: Florian Mayer <florian.mayer at bitsrc.org>
Date: Wed, 13 Dec 2023 15:49:47 -0800
Subject: [PATCH 3/8] Update compiler-rt/lib/scudo/standalone/stack_depot.h
Co-authored-by: ChiaHungDuan <f103119 at gmail.com>
---
compiler-rt/lib/scudo/standalone/stack_depot.h | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/stack_depot.h b/compiler-rt/lib/scudo/standalone/stack_depot.h
index 072eb80f8157db..e7caa8a3d9b5dc 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -138,8 +138,12 @@ class alignas(16) StackDepot {
u32 Hash = B.get();
u32 Pos = Hash & TabMask;
- u32 RingPos = atomic_load_relaxed(&Tab(RawStackDepot)[Pos]);
- u64 Entry = atomic_load_relaxed(&Ring(RawStackDepot)[RingPos]);
+ atomic_u32 *Tab = getTab(RawStackDepot);
+ atomic_u64 *Ring = getRing(RawStackDepot);
+ u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
+ u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
+
+ // And the following uses of Tab/Ring
u64 Id = (u64(End - Begin) << 33) | (u64(Hash) << 1) | 1;
if (Entry == Id)
return Hash;
>From faeaa216bbf2bf179df1a37c33ddd6e9468f4e32 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Wed, 13 Dec 2023 15:53:52 -0800
Subject: [PATCH 4/8] fmt suggestion
---
compiler-rt/lib/scudo/standalone/stack_depot.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/stack_depot.h b/compiler-rt/lib/scudo/standalone/stack_depot.h
index e7caa8a3d9b5dc..1699d5be1e70d8 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -138,12 +138,12 @@ class alignas(16) StackDepot {
u32 Hash = B.get();
u32 Pos = Hash & TabMask;
- atomic_u32 *Tab = getTab(RawStackDepot);
- atomic_u64 *Ring = getRing(RawStackDepot);
- u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
- u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
-
- // And the following uses of Tab/Ring
+ atomic_u32 *Tab = getTab(RawStackDepot);
+ atomic_u64 *Ring = getRing(RawStackDepot);
+ u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
+ u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
+
+ // And the following uses of Tab/Ring
u64 Id = (u64(End - Begin) << 33) | (u64(Hash) << 1) | 1;
if (Entry == Id)
return Hash;
>From 2e237e98000041896a4064fa4034dd5f34c3b031 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Wed, 13 Dec 2023 15:59:47 -0800
Subject: [PATCH 5/8] Revert "fmt suggestion"
This reverts commit 86963399a908e4317a4fa8a47b9e7bb51ee7d7a5.
---
compiler-rt/lib/scudo/standalone/stack_depot.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/stack_depot.h b/compiler-rt/lib/scudo/standalone/stack_depot.h
index 1699d5be1e70d8..e7caa8a3d9b5dc 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -138,12 +138,12 @@ class alignas(16) StackDepot {
u32 Hash = B.get();
u32 Pos = Hash & TabMask;
- atomic_u32 *Tab = getTab(RawStackDepot);
- atomic_u64 *Ring = getRing(RawStackDepot);
- u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
- u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
-
- // And the following uses of Tab/Ring
+ atomic_u32 *Tab = getTab(RawStackDepot);
+ atomic_u64 *Ring = getRing(RawStackDepot);
+ u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
+ u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
+
+ // And the following uses of Tab/Ring
u64 Id = (u64(End - Begin) << 33) | (u64(Hash) << 1) | 1;
if (Entry == Id)
return Hash;
>From 8ff895204599ef06c5d30d388f3cc84a288f5991 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Wed, 13 Dec 2023 15:59:54 -0800
Subject: [PATCH 6/8] Revert "Update
compiler-rt/lib/scudo/standalone/stack_depot.h"
This reverts commit 3ff01eacd0fe13da16b83c86abe216cdb423caef.
---
compiler-rt/lib/scudo/standalone/stack_depot.h | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/stack_depot.h b/compiler-rt/lib/scudo/standalone/stack_depot.h
index e7caa8a3d9b5dc..072eb80f8157db 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -138,12 +138,8 @@ class alignas(16) StackDepot {
u32 Hash = B.get();
u32 Pos = Hash & TabMask;
- atomic_u32 *Tab = getTab(RawStackDepot);
- atomic_u64 *Ring = getRing(RawStackDepot);
- u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
- u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
-
- // And the following uses of Tab/Ring
+ 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;
>From ec3a0699f7a2d7dcc9812a7e866f7f9abf3b2e6e Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Tue, 23 Jan 2024 15:40:38 -0800
Subject: [PATCH 7/8] review
---
.../lib/scudo/standalone/stack_depot.h | 33 +++++++++++--------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/stack_depot.h b/compiler-rt/lib/scudo/standalone/stack_depot.h
index 072eb80f8157db..a6ccd679aabaf3 100644
--- a/compiler-rt/lib/scudo/standalone/stack_depot.h
+++ b/compiler-rt/lib/scudo/standalone/stack_depot.h
@@ -69,21 +69,21 @@ class alignas(16) StackDepot {
// This is immediately followed by RingSize atomic_u64 and
// (TabMask + 1) atomic_u32.
- atomic_u64 *Ring(char *RawStackDepot) {
+ atomic_u64 *getRing(char *RawStackDepot) {
return reinterpret_cast<atomic_u64 *>(RawStackDepot + sizeof(StackDepot));
}
- atomic_u32 *Tab(char *RawStackDepot) {
+ atomic_u32 *getTab(char *RawStackDepot) {
return reinterpret_cast<atomic_u32 *>(RawStackDepot + sizeof(StackDepot) +
sizeof(atomic_u64) * RingSize);
}
- const atomic_u64 *Ring(const char *RawStackDepot) const {
+ const atomic_u64 *getRing(const char *RawStackDepot) const {
return reinterpret_cast<const atomic_u64 *>(RawStackDepot +
sizeof(StackDepot));
}
- const atomic_u32 *Tab(const char *RawStackDepot) const {
+ const atomic_u32 *getTab(const char *RawStackDepot) const {
return reinterpret_cast<const atomic_u32 *>(
RawStackDepot + sizeof(StackDepot) + sizeof(atomic_u64) * RingSize);
}
@@ -132,25 +132,28 @@ class alignas(16) StackDepot {
// Insert hash of the stack trace [Begin, End) into the stack depot, and
// return the hash.
u32 insert(char *RawStackDepot, uptr *Begin, uptr *End) {
+ auto *Tab = getTab(RawStackDepot);
+ auto *Ring = getRing(RawStackDepot);
+
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(RawStackDepot)[Pos]);
- u64 Entry = atomic_load_relaxed(&Ring(RawStackDepot)[RingPos]);
+ u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
+ u64 Entry = atomic_load_relaxed(&Ring[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(RawStackDepot)[Pos], RingPos);
- atomic_store_relaxed(&Ring(RawStackDepot)[RingPos], Id);
+ atomic_store_relaxed(&Tab[Pos], RingPos);
+ atomic_store_relaxed(&Ring[RingPos], Id);
for (uptr *I = Begin; I != End; ++I) {
RingPos = (RingPos + 1) & RingMask;
- atomic_store_relaxed(&Ring(RawStackDepot)[RingPos], *I);
+ atomic_store_relaxed(&Ring[RingPos], *I);
}
RingEnd = (RingPos + 1) & RingMask;
return Hash;
@@ -161,11 +164,14 @@ class alignas(16) StackDepot {
// *RingPosPtr + *SizePtr.
bool find(const char *RawStackDepot, u32 Hash, uptr *RingPosPtr,
uptr *SizePtr) const {
+ auto *Tab = getTab(RawStackDepot);
+ auto *Ring = getRing(RawStackDepot);
+
u32 Pos = Hash & TabMask;
- u32 RingPos = atomic_load_relaxed(&Tab(RawStackDepot)[Pos]);
+ u32 RingPos = atomic_load_relaxed(&Tab[Pos]);
if (RingPos >= RingSize)
return false;
- u64 Entry = atomic_load_relaxed(&Ring(RawStackDepot)[RingPos]);
+ u64 Entry = atomic_load_relaxed(&Ring[RingPos]);
u64 HashWithTagBit = (u64(Hash) << 1) | 1;
if ((Entry & 0x1ffffffff) != HashWithTagBit)
return false;
@@ -177,13 +183,14 @@ class alignas(16) StackDepot {
MurMur2HashBuilder B;
for (uptr I = 0; I != Size; ++I) {
RingPos = (RingPos + 1) & RingMask;
- B.add(u32(atomic_load_relaxed(&Ring(RawStackDepot)[RingPos])) >> 2);
+ B.add(u32(atomic_load_relaxed(&Ring[RingPos])) >> 2);
}
return B.get() == Hash;
}
u64 at(const char *RawStackDepot, uptr RingPos) const {
- return atomic_load_relaxed(&Ring(RawStackDepot)[RingPos & RingMask]);
+ auto *Ring = getRing(RawStackDepot);
+ return atomic_load_relaxed(&Ring[RingPos & RingMask]);
}
};
>From 366bb7ee40194d867faa71467d327a41b07f9d59 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Tue, 23 Jan 2024 16:20:33 -0800
Subject: [PATCH 8/8] rearrange
---
compiler-rt/lib/scudo/standalone/combined.h | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index cabc51339505cb..0f8dcb34c16d09 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -1520,25 +1520,33 @@ 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;
constexpr auto kMaxU32Pow2 = ~(UINT32_MAX >> 1);
static_assert(isPowerOfTwo(kMaxU32Pow2));
+ constexpr auto kFramesPerStack = 8;
+ static_assert(isPowerOfTwo(kFramesPerStack));
+
+ // We need StackDepot to be aligned to 8-bytes so the ring we st ore after
+ // is correctly assigned.
+ static_assert(sizeof(StackDepot) % alignof(atomic_u64) == 0);
+
+ // Make sure the maximum sized StackDepot fits withint a uintptr_t to
+ // simplify the overflow checking.
+ static_assert(sizeof(StackDepot) + UINT32_MAX * sizeof(atomic_u64) *
+ UINT32_MAX * sizeof(atomic_u32) <
+ UINTPTR_MAX);
+
if (AllocationRingBufferSize > kMaxU32Pow2 / kStacksPerRingBufferEntry)
return;
u32 TabSize = static_cast<u32>(roundUpPowerOfTwo(kStacksPerRingBufferEntry *
AllocationRingBufferSize));
- constexpr auto kFramesPerStack = 8;
- static_assert(isPowerOfTwo(kFramesPerStack));
if (TabSize > UINT32_MAX / kFramesPerStack)
return;
u32 RingSize = static_cast<u32>(TabSize * kFramesPerStack);
DCHECK(isPowerOfTwo(RingSize));
- static_assert(sizeof(StackDepot) % alignof(atomic_u64) == 0);
- static_assert(sizeof(StackDepot) + UINT32_MAX * sizeof(atomic_u64) *
- UINT32_MAX * sizeof(atomic_u32) <
- UINTPTR_MAX);
StackDepotSize = sizeof(StackDepot) + sizeof(atomic_u64) * RingSize +
sizeof(atomic_u32) * TabSize;
MemMapT DepotMap;
More information about the llvm-commits
mailing list