[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