diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -177,6 +177,10 @@ Quarantine.init( static_cast(getFlags()->quarantine_size_kb << 10), static_cast(getFlags()->thread_local_quarantine_size_kb << 10)); + + // init is always called before the first allocation, so we are not missing + // any allocation stacks. + InitRingBuffer(); } // Initialize the embedded GWP-ASan instance. Requires the main allocator to @@ -930,11 +934,22 @@ return PrimaryT::getRegionInfoArraySize(); } - const char *getRingBufferAddress() const { - return reinterpret_cast(&RingBuffer); + const char *getRingBufferAddress() { + initThreadMaybe(); + return reinterpret_cast(RingBuffer); + } + + uptr getRingBufferSize() { + initThreadMaybe(); + return RingBufferSizeInBytes(RingBuffer->Size); } - static uptr getRingBufferSize() { return sizeof(RingBuffer); } + static void setRingBufferFuzzMode(char *Buffer) { + AllocationRingBuffer *RingBuffer = + reinterpret_cast(Buffer); + RingBuffer->Size = 2; + } + static uptr getRingBufferSizeForFuzzer() { return RingBufferSizeInBytes(2); } static const uptr MaxTraceSize = 64; @@ -1038,14 +1053,16 @@ }; 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 = {}; + // For convenience. This is always equal to + // reinterpret_cast(RawRingerBuffer). + AllocationRingBuffer *RingBuffer = {}; // The following might get optimized out by the compiler. NOINLINE void performSanityChecks() { @@ -1242,9 +1259,9 @@ 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(&RingBuffer->Pos, 1, memory_order_relaxed); typename AllocationRingBuffer::Entry *Entry = - &RingBuffer.Entries[Pos % AllocationRingBuffer::NumEntries]; + GetRingBufferEntry(RawRingBuffer, Pos % RingBuffer->Size); // First invalidate our entry so that we don't attempt to interpret a // partially written state in getSecondaryErrorInfo(). The fences below @@ -1388,12 +1405,14 @@ const char *RingBufferPtr) { auto *RingBuffer = reinterpret_cast(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; @@ -1458,6 +1477,41 @@ Quarantine.getStats(Str); return Str->length(); } + + static typename AllocationRingBuffer::Entry * + GetRingBufferEntry(char *RawRingBuffer, u32 N) { + return &reinterpret_cast( + &RawRingBuffer[sizeof(AllocationRingBuffer)])[N]; + } + static const typename AllocationRingBuffer::Entry * + GetRingBufferEntry(const char *RawRingBuffer, u32 N) { + return &reinterpret_cast( + &RawRingBuffer[sizeof(AllocationRingBuffer)])[N]; + } + + void InitRingBuffer() { + u32 AllocationRingBufferSize = + static_cast(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( + map(/*Addr=*/nullptr, RingBufferSizeInBytes(AllocationRingBufferSize), + "AllocatorRingBuffer", /*Flags=*/0, &Data)); + RingBuffer = reinterpret_cast(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); + } }; } // namespace scudo diff --git a/compiler-rt/lib/scudo/standalone/flags.inc b/compiler-rt/lib/scudo/standalone/flags.inc --- a/compiler-rt/lib/scudo/standalone/flags.inc +++ b/compiler-rt/lib/scudo/standalone/flags.inc @@ -54,3 +54,6 @@ "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 --- 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,11 +46,13 @@ } std::string RingBufferBytes = FDP.ConsumeRemainingBytesAsString(); - std::vector RingBuffer(AllocatorT::getRingBufferSize(), 0); + std::vector RingBuffer(AllocatorT::getRingBufferSizeForFuzzer(), 0); for (size_t i = 0; i < RingBufferBytes.length() && i < RingBuffer.size(); ++i) { RingBuffer[i] = RingBufferBytes[i]; } + // Force the RingBuffer Size member to be consistent. + AllocatorT::setRingBufferFuzzMode(RingBuffer.data()); scudo_error_info ErrorInfo; AllocatorT::getErrorInfo(&ErrorInfo, FaultAddr, StackDepot.data(), diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp --- 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 "scudo/interface.h" #include "tests/scudo_unit_test.h" #include "allocator_config.h" @@ -14,6 +15,7 @@ #include "combined.h" #include +#include #include #include #include @@ -702,6 +704,20 @@ } } +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