[compiler-rt] bc949f9 - [GWP-ASan] Handle wild touches of the guarded pool.

Mitch Phillips via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 28 14:15:42 PST 2023


Author: Mitch Phillips
Date: 2023-02-28T14:05:51-08:00
New Revision: bc949f923ee37e6c71b69d0c0337d0e6b499832d

URL: https://github.com/llvm/llvm-project/commit/bc949f923ee37e6c71b69d0c0337d0e6b499832d
DIFF: https://github.com/llvm/llvm-project/commit/bc949f923ee37e6c71b69d0c0337d0e6b499832d.diff

LOG: [GWP-ASan] Handle wild touches of the guarded pool.

AllocMeta could be null when returned from __gwp_asan_get_metadata() for
a bad access into the GuardedPagePool that was never allocated.
Currently, then we dereference the null pointer, oops.

Hoist the check up and print a message (only once in recoverable mode)
about the bad memory access.

Reviewed By: fmayer

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

Added: 
    compiler-rt/lib/gwp_asan/tests/never_allocated.cpp

Modified: 
    compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp
    compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
    compiler-rt/lib/gwp_asan/tests/backtrace.cpp
    compiler-rt/lib/gwp_asan/tests/harness.cpp
    compiler-rt/lib/gwp_asan/tests/harness.h
    compiler-rt/lib/gwp_asan/tests/recoverable.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp b/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp
index e012963bffd89..198db5cb074cb 100644
--- a/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp
+++ b/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp
@@ -99,6 +99,12 @@ void printHeader(Error E, uintptr_t AccessPtr,
          ThreadBuffer);
 }
 
+static bool HasReportedBadPoolAccess = false;
+static const char *kUnknownCrashText =
+    "GWP-ASan cannot provide any more information about this error. This may "
+    "occur due to a wild memory access into the GWP-ASan pool, or an "
+    "overflow/underflow that is > 512B in length.\n";
+
 void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
                 const gwp_asan::AllocationMetadata *Metadata,
                 SegvBacktrace_t SegvBacktrace, Printf_t Printf,
@@ -117,6 +123,15 @@ void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
   const gwp_asan::AllocationMetadata *AllocMeta =
       __gwp_asan_get_metadata(State, Metadata, ErrorPtr);
 
+  if (AllocMeta == nullptr) {
+    if (HasReportedBadPoolAccess) return;
+    HasReportedBadPoolAccess = true;
+    Printf("*** GWP-ASan detected a memory error ***\n");
+    ScopedEndOfReportDecorator Decorator(Printf);
+    Printf(kUnknownCrashText);
+    return;
+  }
+
   // It's unusual for a signal handler to be invoked multiple times for the same
   // allocation, but it's possible in various scenarios, like:
   //  1. A double-free or invalid-free was invoked in one thread at the same
@@ -132,9 +147,7 @@ void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
 
   Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);
   if (E == Error::UNKNOWN) {
-    Printf("GWP-ASan cannot provide any more information about this error. "
-           "This may occur due to a wild memory access into the GWP-ASan pool, "
-           "or an overflow/underflow that is > 512B in length.\n");
+    Printf(kUnknownCrashText);
     return;
   }
 
@@ -149,9 +162,6 @@ void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
 
   PrintBacktrace(Trace, TraceLength, Printf);
 
-  if (AllocMeta == nullptr)
-    return;
-
   // Maybe print the deallocation trace.
   if (__gwp_asan_is_deallocated(AllocMeta)) {
     uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);

diff  --git a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
index 046ca7ce6799f..c961e41335c81 100644
--- a/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
+++ b/compiler-rt/lib/gwp_asan/tests/CMakeLists.txt
@@ -27,7 +27,8 @@ set(GWP_ASAN_UNITTESTS
   enable_disable.cpp
   late_init.cpp
   options.cpp
-  recoverable.cpp)
+  recoverable.cpp
+  never_allocated.cpp)
 
 set(GWP_ASAN_UNIT_TEST_HEADERS
   ${GWP_ASAN_HEADERS}

diff  --git a/compiler-rt/lib/gwp_asan/tests/backtrace.cpp b/compiler-rt/lib/gwp_asan/tests/backtrace.cpp
index e878994396254..7cbbcf5fb48ce 100644
--- a/compiler-rt/lib/gwp_asan/tests/backtrace.cpp
+++ b/compiler-rt/lib/gwp_asan/tests/backtrace.cpp
@@ -68,10 +68,6 @@ TEST_P(BacktraceGuardedPoolAllocatorDeathTest, UseAfterFree) {
   ;
 }
 
-INSTANTIATE_TEST_SUITE_P(RecoverableSignalDeathTest,
-                         BacktraceGuardedPoolAllocatorDeathTest,
-                         /* Recoverable */ testing::Bool());
-
 TEST(Backtrace, Short) {
   gwp_asan::AllocationMetadata Meta;
   Meta.AllocationTrace.RecordBacktrace(

diff  --git a/compiler-rt/lib/gwp_asan/tests/harness.cpp b/compiler-rt/lib/gwp_asan/tests/harness.cpp
index ccad80ebdaad4..6d416300aeafe 100644
--- a/compiler-rt/lib/gwp_asan/tests/harness.cpp
+++ b/compiler-rt/lib/gwp_asan/tests/harness.cpp
@@ -8,6 +8,8 @@
 
 #include "gwp_asan/tests/harness.h"
 
+#include <string>
+
 namespace gwp_asan {
 namespace test {
 bool OnlyOnce() {
@@ -34,3 +36,19 @@ DeallocateMemory2(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) {
 __attribute__((optnone)) void TouchMemory(void *Ptr) {
   *(reinterpret_cast<volatile char *>(Ptr)) = 7;
 }
+
+void CheckOnlyOneGwpAsanCrash(const std::string &OutputBuffer) {
+  const char *kGwpAsanErrorString = "GWP-ASan detected a memory error";
+  size_t FirstIndex = OutputBuffer.find(kGwpAsanErrorString);
+  ASSERT_NE(FirstIndex, std::string::npos) << "Didn't detect a GWP-ASan crash";
+  ASSERT_EQ(OutputBuffer.find(kGwpAsanErrorString, FirstIndex + 1),
+            std::string::npos)
+      << "Detected more than one GWP-ASan crash:\n"
+      << OutputBuffer;
+}
+
+INSTANTIATE_TEST_SUITE_P(RecoverableTests, BacktraceGuardedPoolAllocator,
+                         /* Recoverable */ testing::Values(true));
+INSTANTIATE_TEST_SUITE_P(RecoverableAndNonRecoverableTests,
+                         BacktraceGuardedPoolAllocatorDeathTest,
+                         /* Recoverable */ testing::Bool());

diff  --git a/compiler-rt/lib/gwp_asan/tests/harness.h b/compiler-rt/lib/gwp_asan/tests/harness.h
index c8f643dbab908..89f4e11013394 100644
--- a/compiler-rt/lib/gwp_asan/tests/harness.h
+++ b/compiler-rt/lib/gwp_asan/tests/harness.h
@@ -46,6 +46,8 @@ void DeallocateMemory(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr);
 void DeallocateMemory2(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr);
 void TouchMemory(void *Ptr);
 
+void CheckOnlyOneGwpAsanCrash(const std::string &OutputBuffer);
+
 class DefaultGuardedPoolAllocator : public Test {
 public:
   void SetUp() override {

diff  --git a/compiler-rt/lib/gwp_asan/tests/never_allocated.cpp b/compiler-rt/lib/gwp_asan/tests/never_allocated.cpp
new file mode 100644
index 0000000000000..bd43b22daa1e8
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/tests/never_allocated.cpp
@@ -0,0 +1,55 @@
+//===-- never_allocated.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <string>
+
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+#include "gwp_asan/tests/harness.h"
+
+TEST_P(BacktraceGuardedPoolAllocatorDeathTest, NeverAllocated) {
+  SCOPED_TRACE("");
+  void *Ptr = GPA.allocate(0x1000);
+  GPA.deallocate(Ptr);
+
+  std::string DeathNeedle =
+      "GWP-ASan cannot provide any more information about this error";
+
+  // Trigger a guard page in a completely 
diff erent slot that's never allocated.
+  // Previously, there was a bug that this would result in nullptr-dereference
+  // in the posix crash handler.
+  char *volatile NeverAllocatedPtr = static_cast<char *>(Ptr) + 0x3000;
+  if (!Recoverable) {
+    ASSERT_DEATH(*NeverAllocatedPtr = 0, DeathNeedle);
+    return;
+  }
+
+  *NeverAllocatedPtr = 0;
+  CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
+  ASSERT_NE(std::string::npos, GetOutputBuffer().find(DeathNeedle));
+
+  // Check that subsequent invalid touches of the pool don't print a report.
+  GetOutputBuffer().clear();
+  for (size_t i = 0; i < 100; ++i) {
+    *NeverAllocatedPtr = 0;
+    *(NeverAllocatedPtr + 0x2000) = 0;
+    *(NeverAllocatedPtr + 0x3000) = 0;
+    ASSERT_TRUE(GetOutputBuffer().empty());
+  }
+
+  // Check that reports on the other slots still report a double-free, but only
+  // once.
+  GetOutputBuffer().clear();
+  GPA.deallocate(Ptr);
+  ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free"));
+  GetOutputBuffer().clear();
+  for (size_t i = 0; i < 100; ++i) {
+    DeallocateMemory(GPA, Ptr);
+    ASSERT_TRUE(GetOutputBuffer().empty());
+  }
+}

diff  --git a/compiler-rt/lib/gwp_asan/tests/recoverable.cpp b/compiler-rt/lib/gwp_asan/tests/recoverable.cpp
index adc9731e98c25..2c14ff52ef979 100644
--- a/compiler-rt/lib/gwp_asan/tests/recoverable.cpp
+++ b/compiler-rt/lib/gwp_asan/tests/recoverable.cpp
@@ -17,16 +17,6 @@
 #include "gwp_asan/crash_handler.h"
 #include "gwp_asan/tests/harness.h"
 
-void CheckOnlyOneGwpAsanCrash(const std::string &OutputBuffer) {
-  const char *kGwpAsanErrorString = "GWP-ASan detected a memory error";
-  size_t FirstIndex = OutputBuffer.find(kGwpAsanErrorString);
-  ASSERT_NE(FirstIndex, std::string::npos) << "Didn't detect a GWP-ASan crash";
-  ASSERT_EQ(OutputBuffer.find(kGwpAsanErrorString, FirstIndex + 1),
-            std::string::npos)
-      << "Detected more than one GWP-ASan crash:\n"
-      << OutputBuffer;
-}
-
 TEST_P(BacktraceGuardedPoolAllocator, MultipleDoubleFreeOnlyOneOutput) {
   SCOPED_TRACE("");
   void *Ptr = AllocateMemory(GPA);
@@ -202,6 +192,3 @@ TEST_P(BacktraceGuardedPoolAllocator, InterThreadThrashingSingleAlloc) {
   runInterThreadThrashingSingleAlloc(kNumIterations, &GPA);
   CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
 }
-
-INSTANTIATE_TEST_SUITE_P(RecoverableTests, BacktraceGuardedPoolAllocator,
-                         /* Recoverable */ testing::Values(true));


        


More information about the llvm-commits mailing list