[compiler-rt] 2426cc7 - Dynamically allocate scudo allocation buffer.
Florian Mayer via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 11 16:53:22 PST 2023
Author: Florian Mayer
Date: 2023-01-11T16:53:12-08:00
New Revision: 2426cc773a9d4d11a8ef94fc411bfe9acd7eacad
URL: https://github.com/llvm/llvm-project/commit/2426cc773a9d4d11a8ef94fc411bfe9acd7eacad
DIFF: https://github.com/llvm/llvm-project/commit/2426cc773a9d4d11a8ef94fc411bfe9acd7eacad.diff
LOG: Dynamically allocate scudo allocation buffer.
This is so we can increase the buffer size for finding elusive bugs.
Tested by hand with this program
```
int main(int argc, char** argv) {
if (argc < 2)
return 1;
int n = atoi(argv[1]);
char* x = reinterpret_cast<char*>(malloc(1));
*((volatile char*)x) = 1;
free(x);
for (; n > 0; --n) {
char* y = reinterpret_cast<char*>(malloc(1024));
*((volatile char*)y) = 1;
free(y);
}
*x = 2;
return 0;
}
```
SCUDO_OPTIONS=allocation_ring_buffer_size=30000 ./uaf 1000000
-> no allocation trace
SCUDO_OPTIONS=allocation_ring_buffer_size=30000000 ./uaf 1000000
-> allocation trace
Reviewed By: hctim, eugenis
Differential Revision: https://reviews.llvm.org/D140932
Added:
Modified:
compiler-rt/lib/scudo/standalone/combined.h
compiler-rt/lib/scudo/standalone/flags.inc
compiler-rt/lib/scudo/standalone/fuzz/get_error_info_fuzzer.cpp
compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
Removed:
################################################################################
diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h
index 5b3903e0c38e3..fd375f4b83c1b 100644
--- a/compiler-rt/lib/scudo/standalone/combined.h
+++ b/compiler-rt/lib/scudo/standalone/combined.h
@@ -177,6 +177,8 @@ class Allocator {
Quarantine.init(
static_cast<uptr>(getFlags()->quarantine_size_kb << 10),
static_cast<uptr>(getFlags()->thread_local_quarantine_size_kb << 10));
+
+ initRingBuffer();
}
// Initialize the embedded GWP-ASan instance. Requires the main allocator to
@@ -932,11 +934,28 @@ class Allocator {
return PrimaryT::getRegionInfoArraySize();
}
- const char *getRingBufferAddress() const {
- return reinterpret_cast<const char *>(&RingBuffer);
+ const char *getRingBufferAddress() {
+ initThreadMaybe();
+ return reinterpret_cast<const char *>(getRingBuffer());
+ }
+
+ uptr getRingBufferSize() {
+ initThreadMaybe();
+ return ringBufferSizeInBytes(getRingBuffer()->Size);
}
- static uptr getRingBufferSize() { return sizeof(RingBuffer); }
+ static bool setRingBufferSizeForBuffer(char *Buffer, size_t Size) {
+ // Need at least one entry.
+ if (Size < sizeof(AllocationRingBuffer) +
+ sizeof(typename AllocationRingBuffer::Entry)) {
+ return false;
+ }
+ AllocationRingBuffer *RingBuffer =
+ reinterpret_cast<AllocationRingBuffer *>(Buffer);
+ RingBuffer->Size = (Size - sizeof(AllocationRingBuffer)) /
+ sizeof(typename AllocationRingBuffer::Entry);
+ return true;
+ }
static const uptr MaxTraceSize = 64;
@@ -1040,14 +1059,13 @@ class Allocator {
};
atomic_uptr Pos;
-#ifdef SCUDO_FUZZ
- static const uptr NumEntries = 2;
-#else
- static const uptr NumEntries = 32768;
-#endif
- Entry Entries[NumEntries];
+ u32 Size;
+ // An array of Size (at least one) elements of type Entry is immediately
+ // following to this struct.
};
- AllocationRingBuffer RingBuffer = {};
+ // Pointer to memory mapped area starting with AllocationRingBuffer struct,
+ // and immediately followed by Size elements of type Entry.
+ char *RawRingBuffer = {};
// The following might get optimized out by the compiler.
NOINLINE void performSanityChecks() {
@@ -1244,9 +1262,9 @@ class Allocator {
void storeRingBufferEntry(void *Ptr, u32 AllocationTrace, u32 AllocationTid,
uptr AllocationSize, u32 DeallocationTrace,
u32 DeallocationTid) {
- uptr Pos = atomic_fetch_add(&RingBuffer.Pos, 1, memory_order_relaxed);
+ uptr Pos = atomic_fetch_add(&getRingBuffer()->Pos, 1, memory_order_relaxed);
typename AllocationRingBuffer::Entry *Entry =
- &RingBuffer.Entries[Pos % AllocationRingBuffer::NumEntries];
+ getRingBufferEntry(RawRingBuffer, Pos % getRingBuffer()->Size);
// First invalidate our entry so that we don't attempt to interpret a
// partially written state in getSecondaryErrorInfo(). The fences below
@@ -1390,12 +1408,14 @@ class Allocator {
const char *RingBufferPtr) {
auto *RingBuffer =
reinterpret_cast<const AllocationRingBuffer *>(RingBufferPtr);
+ if (!RingBuffer)
+ return; // just in case; called before init
uptr Pos = atomic_load_relaxed(&RingBuffer->Pos);
- for (uptr I = Pos - 1; I != Pos - 1 - AllocationRingBuffer::NumEntries &&
- NextErrorReport != NumErrorReports;
+ for (uptr I = Pos - 1;
+ I != Pos - 1 - RingBuffer->Size && NextErrorReport != NumErrorReports;
--I) {
- auto *Entry = &RingBuffer->Entries[I % AllocationRingBuffer::NumEntries];
+ auto *Entry = getRingBufferEntry(RingBufferPtr, I % RingBuffer->Size);
uptr EntryPtr = atomic_load_relaxed(&Entry->Ptr);
if (!EntryPtr)
continue;
@@ -1460,6 +1480,45 @@ class Allocator {
Quarantine.getStats(Str);
return Str->length();
}
+
+ static typename AllocationRingBuffer::Entry *
+ getRingBufferEntry(char *RawRingBuffer, u32 N) {
+ return &reinterpret_cast<typename AllocationRingBuffer::Entry *>(
+ &RawRingBuffer[sizeof(AllocationRingBuffer)])[N];
+ }
+ static const typename AllocationRingBuffer::Entry *
+ getRingBufferEntry(const char *RawRingBuffer, u32 N) {
+ return &reinterpret_cast<const typename AllocationRingBuffer::Entry *>(
+ &RawRingBuffer[sizeof(AllocationRingBuffer)])[N];
+ }
+
+ void initRingBuffer() {
+ u32 AllocationRingBufferSize =
+ static_cast<u32>(getFlags()->allocation_ring_buffer_size);
+ // Have at least one entry so we don't need to special case.
+ if (AllocationRingBufferSize < 1)
+ AllocationRingBufferSize = 1;
+ MapPlatformData Data = {};
+ RawRingBuffer = static_cast<char *>(
+ map(/*Addr=*/nullptr, ringBufferSizeInBytes(AllocationRingBufferSize),
+ "AllocatorRingBuffer", /*Flags=*/0, &Data));
+ auto *RingBuffer = reinterpret_cast<AllocationRingBuffer *>(RawRingBuffer);
+ RingBuffer->Size = AllocationRingBufferSize;
+ static_assert(sizeof(AllocationRingBuffer) %
+ alignof(typename AllocationRingBuffer::Entry) ==
+ 0,
+ "invalid alignment");
+ }
+
+ static constexpr u32 ringBufferSizeInBytes(u32 AllocationRingBufferSize) {
+ return sizeof(AllocationRingBuffer) +
+ AllocationRingBufferSize *
+ sizeof(typename AllocationRingBuffer::Entry);
+ }
+
+ inline AllocationRingBuffer *getRingBuffer() {
+ return reinterpret_cast<AllocationRingBuffer *>(RawRingBuffer);
+ }
};
} // namespace scudo
diff --git a/compiler-rt/lib/scudo/standalone/flags.inc b/compiler-rt/lib/scudo/standalone/flags.inc
index 667dafe1f813e..c1f153bafdd96 100644
--- a/compiler-rt/lib/scudo/standalone/flags.inc
+++ b/compiler-rt/lib/scudo/standalone/flags.inc
@@ -54,3 +54,6 @@ SCUDO_FLAG(int, soft_rss_limit_mb, 0,
"Soft RSS Limit in Mb. If non-zero, once the limit is reached, all "
"subsequent calls will fail or return NULL until the RSS goes below "
"the soft limit")
+
+SCUDO_FLAG(int, allocation_ring_buffer_size, 32768,
+ "Entries to keep in the allocation ring buffer for scudo.")
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 078e44b0dfc84..74456450a4761 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
@@ -46,15 +46,14 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *Data, size_t Size) {
}
std::string RingBufferBytes = FDP.ConsumeRemainingBytesAsString();
- std::vector<char> RingBuffer(AllocatorT::getRingBufferSize(), 0);
- for (size_t i = 0; i < RingBufferBytes.length() && i < RingBuffer.size();
- ++i) {
- RingBuffer[i] = RingBufferBytes[i];
- }
+ // RingBuffer is too short.
+ if (!AllocatorT::setRingBufferSizeForBuffer(RingBufferBytes.data(),
+ RingBufferBytes.size()))
+ return 0;
scudo_error_info ErrorInfo;
AllocatorT::getErrorInfo(&ErrorInfo, FaultAddr, StackDepot.data(),
- RegionInfo.data(), RingBuffer.data(), Memory,
+ RegionInfo.data(), RingBufferBytes.data(), Memory,
MemoryTags, MemoryAddr, MemorySize);
return 0;
}
diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index d4ffdb549b1fa..ec8a03ec8c8a3 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -702,6 +702,20 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ReallocateInPlaceStress) {
}
}
+SCUDO_TYPED_TEST(ScudoCombinedTest, RingBufferSize) {
+ auto *Allocator = this->Allocator.get();
+ auto Size = Allocator->getRingBufferSize();
+ ASSERT_GT(Size, 0);
+ EXPECT_EQ(Allocator->getRingBufferAddress()[Size - 1], '\0');
+}
+
+SCUDO_TYPED_TEST(ScudoCombinedTest, RingBufferAddress) {
+ auto *Allocator = this->Allocator.get();
+ auto *Addr = Allocator->getRingBufferAddress();
+ EXPECT_NE(Addr, nullptr);
+ EXPECT_EQ(Addr, Allocator->getRingBufferAddress());
+}
+
#if SCUDO_CAN_USE_PRIMARY64
#if SCUDO_TRUSTY
More information about the llvm-commits
mailing list