[GWP-ASan] Fix up bad report for in-page underflow w/ UaF

Complex scenario, but reports when there's both a use-after-free and
buffer-underflow that is in-page (i.e. doesn't touch the guard page)
ended up generating a pretty bad report:

'Use After Free at 0x7ff392e88fef (18446744073709551615 bytes into a
1-byte allocation at 0x7ff392e88ff0) by thread 3836722 here:'

(note the 2^64-bytes-into-alloc, very cool and good!)

Fix up that case, and add a diagnostic about when you have both a
use-after-free and a buffer-overflow that it's probably a bogus report
(assuming the developer didn't *really* screw up and have a uaf+overflow
bug at the same time).

Reviewed By: vitalybuka

Differential Revision: https://reviews.llvm.org/D139885

GitOrigin-RevId: dcf23e13615f88bdd4975058595ee60cf1d5811c
diff --git a/crash_handler.cpp b/crash_handler.cpp
index 6b4c39e..48f54e2 100644
--- a/crash_handler.cpp
+++ b/crash_handler.cpp
@@ -52,7 +52,14 @@
   if (State->FailureType != Error::UNKNOWN)
     return State->FailureType;
 
-  // Let's try and figure out what the source of this error is.
+  // Check for use-after-free.
+  if (addrToMetadata(State, Metadata, ErrorPtr)->IsDeallocated)
+    return Error::USE_AFTER_FREE;
+
+  // Check for buffer-overflow. Because of allocation alignment or left/right
+  // page placement, we can have buffer-overflows that don't touch a guarded
+  // page, but these are not possible to detect unless it's also a
+  // use-after-free, which is handled above.
   if (State->isGuardPage(ErrorPtr)) {
     size_t Slot = State->getNearestSlot(ErrorPtr);
     const AllocationMetadata *SlotMeta =
@@ -67,13 +74,6 @@
     return Error::BUFFER_UNDERFLOW;
   }
 
-  // Access wasn't a guard page, check for use-after-free.
-  const AllocationMetadata *SlotMeta =
-      addrToMetadata(State, Metadata, ErrorPtr);
-  if (SlotMeta->IsDeallocated) {
-    return Error::USE_AFTER_FREE;
-  }
-
   // If we have reached here, the error is still unknown.
   return Error::UNKNOWN;
 }
diff --git a/optional/segv_handler_posix.cpp b/optional/segv_handler_posix.cpp
index 5c9bb9f..b3e72c9 100644
--- a/optional/segv_handler_posix.cpp
+++ b/optional/segv_handler_posix.cpp
@@ -47,15 +47,12 @@
   // appended to a log file automatically per Printf() call.
   constexpr size_t kDescriptionBufferLen = 128;
   char DescriptionBuffer[kDescriptionBufferLen] = "";
+
+  bool AccessWasInBounds = false;
   if (E != Error::UNKNOWN && Metadata != nullptr) {
     uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);
     size_t Size = __gwp_asan_get_allocation_size(Metadata);
-    if (E == Error::USE_AFTER_FREE) {
-      snprintf(DescriptionBuffer, kDescriptionBufferLen,
-               "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
-               AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
-               Address);
-    } else if (AccessPtr < Address) {
+    if (AccessPtr < Address) {
       snprintf(DescriptionBuffer, kDescriptionBufferLen,
                "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",
                Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,
@@ -65,9 +62,15 @@
                "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",
                AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
                Address);
-    } else {
+    } else if (E == Error::DOUBLE_FREE) {
       snprintf(DescriptionBuffer, kDescriptionBufferLen,
                "(a %zu-byte allocation) ", Size);
+    } else {
+      AccessWasInBounds = true;
+      snprintf(DescriptionBuffer, kDescriptionBufferLen,
+               "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
+               AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
+               Address);
     }
   }
 
@@ -81,8 +84,19 @@
   else
     snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);
 
-  Printf("%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
-         AccessPtr, DescriptionBuffer, ThreadBuffer);
+  const char *OutOfBoundsAndUseAfterFreeWarning = "";
+  if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) {
+    OutOfBoundsAndUseAfterFreeWarning =
+        " (warning: buffer overflow/underflow detected on a free()'d "
+        "allocation. This either means you have a buffer-overflow and a "
+        "use-after-free at the same time, or you have a long-lived "
+        "use-after-free bug where the allocation/deallocation metadata below "
+        "has already been overwritten and is likely bogus)";
+  }
+
+  Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
+         OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer,
+         ThreadBuffer);
 }
 
 void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,