[PATCH] D73557: [GWP-ASan] Crash Handler API.

Mitch Phillips via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 28 07:45:22 PST 2020


hctim marked 33 inline comments as done.
hctim added inline comments.
Herald added a subscriber: dexonsmith.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:1
 //===-- guarded_pool_allocator.cpp ------------------------------*- C++ -*-===//
 //
----------------
Most of the changes in this file are `/s/MyMember/State.MyMember`


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:130
   if (Opts.SampleRate < 0) {
-    Opts.Printf("GWP-ASan Error: SampleRate is < 0.\n");
-    exit(EXIT_FAILURE);
+    assert(false && "GWP-ASan Error: SampleRate is < 0.");
+    __builtin_trap();
----------------
No `Printf` maintained internally any more, so we assert and trap instead.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:221
+
+static uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) {
+  return Ptr & ~(PageSize - 1);
----------------
Used to be a member function (`GPA::getPageAddr()`), but shouldn't be.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:320
 
-size_t GuardedPoolAllocator::maximumAllocationSize() const { return PageSize; }
+static AllocationMetadata *addrToMetadata(const AllocatorState *State,
+                                          AllocationMetadata *Metadata,
----------------
Slight functional change, `addrToMetadata` used to be implemented differently.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:409
+
+// ============================================================================
+// Implementation of crash handler API.
----------------
Start of implementation of new crash handler functions. All very simple implementation (just find the metadata, and then return data).


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:413
-namespace {
-// Prints the provided error and metadata information.
-void printErrorType(Error E, uintptr_t AccessPtr, AllocationMetadata *Meta,
----------------
This code was moved (with some modifications) to `crash_handler_posix.cpp`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:516
 
-struct ScopedEndOfReportDecorator {
-  ScopedEndOfReportDecorator(options::Printf_t Printf) : Printf(Printf) {}
----------------
Code here (and below) moved to `crash_handler_posix.cpp`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:22
 namespace gwp_asan {
+enum class Error {
+  UNKNOWN,
+  USE_AFTER_FREE,
+  DOUBLE_FREE,
+  INVALID_FREE,
+  BUFFER_OVERFLOW,
+  BUFFER_UNDERFLOW
+};
+
+const char *ErrorToString(const Error &E);
+
+// This struct contains all the metadata recorded about a single allocation made
+// by GWP-ASan. If `AllocationMetadata.Addr` is zero, the metadata is non-valid.
+struct AllocationMetadata {
+  static constexpr uint64_t kInvalidThreadID = UINT64_MAX;
+
+  // The number of bytes used to store a compressed stack frame. On 64-bit
+  // platforms, assuming a compression ratio of 50%, this should allow us to
+  // store ~64 frames per trace.
+  static constexpr size_t kStackFrameStorageBytes = 256;
+
+  // Maximum number of stack frames to collect on allocation/deallocation. The
+  // actual number of collected frames may be less than this as the stack
+  // frames are compressed into a fixed memory range.
+  static constexpr size_t kMaxTraceLengthToCollect = 128;
+
+  // Records the given allocation metadata into this struct.
+  void RecordAllocation(uintptr_t Addr, size_t Size,
+                        options::Backtrace_t Backtrace);
+
+  // Record that this allocation is now deallocated.
+  void RecordDeallocation(options::Backtrace_t Backtrace, bool &RecursiveGuard);
+
+  struct CallSiteInfo {
+    // The compressed backtrace to the allocation/deallocation.
+    uint8_t CompressedTrace[kStackFrameStorageBytes];
+    // The thread ID for this trace, or kInvalidThreadID if not available.
+    uint64_t ThreadID = kInvalidThreadID;
+    // The size of the compressed trace (in bytes). Zero indicates that no
+    // trace was collected.
+    size_t TraceSize = 0;
+  };
+
+  // The address of this allocation. If zero, the rest of this struct isn't
+  // valid, as the allocation has never occurred.
+  uintptr_t Addr = 0;
+  // Represents the actual size of the allocation.
+  size_t Size = 0;
+
+  CallSiteInfo AllocationTrace;
+  CallSiteInfo DeallocationTrace;
+
+  // Whether this allocation has been deallocated yet.
+  bool IsDeallocated = false;
+};
+
+// This holds the state that's shared between the GWP-ASan allocator and the
+// crash handler. This, in conjunction with the Metadata array, forms the entire
+// set of information required for understanding a GWP-ASan crash.
+struct AllocatorState {
+  // Returns whether the provided pointer is a current sampled allocation that
+  // is owned by this pool.
+  GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
+    uintptr_t P = reinterpret_cast<uintptr_t>(Ptr);
+    return P < GuardedPagePoolEnd && GuardedPagePool <= P;
+  }
+
+  // Returns the address of the N-th guarded slot.
+  uintptr_t slotToAddr(size_t N) const;
+
+  // Returns the largest allocation that is supported by this pool.
+  size_t maximumAllocationSize() const;
+
+  // Gets the nearest slot to the provided address.
+  size_t getNearestSlot(uintptr_t Ptr) const;
+
+  // Returns whether the provided pointer is a guard page or not. The pointer
+  // must be within memory owned by this pool, else the result is undefined.
+  bool isGuardPage(uintptr_t Ptr) const;
+
+  // The number of guarded slots that this pool holds.
+  size_t MaxSimultaneousAllocations = 0;
+
+  // Pointer to the pool of guarded slots. Note that this points to the start of
+  // the pool (which is a guard page), not a pointer to the first guarded page.
+  uintptr_t GuardedPagePool = 0;
+  uintptr_t GuardedPagePoolEnd = 0;
+
+  // Cached page size for this system in bytes.
+  size_t PageSize = 0;
+
+  // The type and address of an internally-detected failure. For INVALID_FREE
+  // and DOUBLE_FREE, these errors are detected in GWP-ASan, which will set
+  // these values and terminate the process.
+  Error FailureType = Error::UNKNOWN;
+  uintptr_t FailureAddress = 0;
+};
+
 // This class is the primary implementation of the allocator portion of GWP-
 // ASan. It is the sole owner of the pool of sequentially allocated guarded
----------------
This is moved just down below in this file.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:78
+
+// This holds the state that's shared between the GWP-ASan allocator and the
+// crash handler. This, in conjunction with the Metadata array, forms the entire
----------------
These functions and members are pulled out of `GuardedPoolAllocator`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:149
 
-  // Returns the largest allocation that is supported by this pool. Any
-  // allocations larger than this should go to the regular system allocator.
----------------
Moved into `AllocatorState`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:153
-
-  // Dumps an error report (including allocation and deallocation stack traces).
-  // An optional error may be provided if the caller knows what the error is
----------------
Removed in favour of new crash handler API.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:194
 
-  // Install the SIGSEGV crash handler for printing use-after-free and heap-
-  // buffer-{under|over}flow exceptions. This is platform specific as even
----------------
Installation of segv is now in `optional/crash_handler.h`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:202
-
-  // Returns the index of the slot that this pointer resides in. If the pointer
-  // is not owned by this pool, the result is undefined.
----------------
Functions moved into `AllocatorState`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:213
 
-  // Returns the address of the page that this pointer resides in.
-  uintptr_t getPageAddr(uintptr_t Ptr) const;
----------------
Functions moved into `AllocatorState`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:235
 
-  // Returns the diagnosis for an unknown error. If the diagnosis is not
-  // Error::INVALID_FREE or Error::UNKNOWN, the metadata for the slot
----------------
Deprecated in favour of new crash handler.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:247
 
-  // Cached page size for this system in bytes.
-  size_t PageSize = 0;
----------------
Members (and below) moved into `AllocatorState`.


================
Comment at: compiler-rt/lib/gwp_asan/guarded_pool_allocator.h:274
 
-  // Printf function supplied by the implementing allocator. We can't (in
-  // general) use printf() from the cstdlib as it may malloc(), causing infinite
----------------
We now don't store a `Printf` or `PrintBacktrace` function, as these are the crash handler's responsibility (and GPA no longer is responsible for handling crashes).


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler.h:17
+namespace crash_handler {
+// ================================ Requirements ===============================
+// This function must be provided by the supporting allocator only when this
----------------
Moved from `options.h`. Now `Printf_t` and `PrintBacktrace_t` are correctly only used by the inbuilt crash handler, and not managed by `GPA`.


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler.h:62
+
+// Install the SIGSEGV crash handler for printing use-after-free and heap-
+// buffer-{under|over}flow exceptions if the user asked for it. This is platform
----------------
This is now the entry point to install the internal (segv-based) crash handler, and the allocator is responsible for inspecting `Options.installSignalHandlers` and call this function if true.

The allocator is responsible for providing us with the proper printing/backtrace functions, but we also provide multiple implementations that can be used.


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler_posix.cpp:34
+
+static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
+  if (GPAForSignalHandler) {
----------------
Moved from `guarded_pool_allocator.cpp`.


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler_posix.cpp:60
+
+struct ScopedEndOfReportDecorator {
+  ScopedEndOfReportDecorator(gwp_asan::crash_handler::Printf_t Printf)
----------------
Moved from `guarded_pool_allocator.cpp`.


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler_posix.cpp:68
+// Prints the provided error and metadata information.
+void printHeader(Error E, uintptr_t AccessPtr,
+                 const gwp_asan::AllocatorState *State,
----------------
Moved from `guarded_pool_allocator.cpp` (with minor modifications to use new API instead of old).


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler_posix.cpp:116
+
+void defaultPrintStackTrace(uintptr_t *Trace, size_t TraceLength,
+                            gwp_asan::crash_handler::Printf_t Printf) {
----------------
Moved from `guarded_pool_allocator.cpp` (with minor modifications to use new API instead of old).


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler_posix.cpp:135
+
+void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
+                           PrintBacktrace_t PrintBacktrace,
----------------
Moved from `guarded_pool_allocator.cpp` (with minor modifications to use new API instead of old).


================
Comment at: compiler-rt/lib/gwp_asan/optional/crash_handler_posix.cpp:157
+
+void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
+                const gwp_asan::AllocationMetadata *Metadata,
----------------
Moved from `guarded_pool_allocator.cpp` (with minor modifications to use new API instead of old).


================
Comment at: compiler-rt/lib/gwp_asan/options.h:18
-// ================================ Requirements ===============================
-// This function is required to be implemented by the supporting allocator. The
-// sanitizer::Printf() function can be simply used here.
----------------
Moved to `crash_handler_api.h`.


================
Comment at: compiler-rt/lib/gwp_asan/options.h:53
 
-// ================================ Requirements ===============================
-// This function is optional for the supporting allocator, but one of the two
----------------
Moved to `crash_handler_api.h`.


================
Comment at: compiler-rt/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp:41
   if (Ptr == MAP_FAILED) {
-    Printf("Failed to map guarded pool allocator memory, errno: %d\n", errno);
-    Printf("  mmap(nullptr, %zu, ...) failed.\n", Size);
-    exit(EXIT_FAILURE);
+    assert(false && "Failed to map guarded pool allocator memory");
+    __builtin_trap();
----------------
No more `Printf` in GPA, so assert and trap instread.


================
Comment at: compiler-rt/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp:94
-
-static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
-  gwp_asan::GuardedPoolAllocator::reportError(
----------------
Moved to `crash_handler_posix.cpp`.


================
Comment at: compiler-rt/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp:125
 
-void GuardedPoolAllocator::installSignalHandlers() {
-  struct sigaction Action;
----------------
Moved to `crash_handler_posix.cpp`.


================
Comment at: compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp:1
+//===-- crash_handler_api.cpp -----------------------------------*- C++ -*-===//
+//
----------------
New tests for the crash handler API.


================
Comment at: compiler-rt/lib/scudo/standalone/CMakeLists.txt:116
+  list(APPEND SCUDO_OBJECT_LIBS
+       RTGwpAsan RTGwpAsanBacktraceLibc RTGwpAsanCrashHandler)
   list(APPEND SCUDO_CFLAGS -DGWP_ASAN_HOOKS)
----------------
Note for @cryptoad: This is the libc backtrace and the GWP-ASan internal (segv) crash handler, no dependencies on compiler-rt here :)


================
Comment at: compiler-rt/lib/scudo/standalone/combined.h:173
     Opt.InstallForkHandlers = false;
-    Opt.Printf = Printf;
+    Opt.Backtrace = gwp_asan::options::getBacktraceFunction();
     GuardedAlloc.init(Opt);
----------------
Backtrace function comes from libc. Not sure if this WAI on Fuchsia, but we don't support GWP-ASan there so /shrug/.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D73557/new/

https://reviews.llvm.org/D73557





More information about the llvm-commits mailing list