diff options
author | Mitch Phillips <31459023+hctim@users.noreply.github.com> | 2023-03-01 06:11:57 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-03-01 06:11:57 +0000 |
commit | 6c03df192783511601450ef71a54892c05cc674c (patch) | |
tree | ca0e77b2fa111814f842c6fa126448e156da2d92 | |
parent | 6209dc9bc90cd5d1ae7c44145061173634ed21a5 (diff) | |
parent | 9951822be207b864b74d65f4078f87aa65e0bab2 (diff) | |
download | gwp_asan-6c03df192783511601450ef71a54892c05cc674c.tar.gz |
[GWP-ASan] Handle wild touches of the guarded pool. am: d58876b9d0 am: da07ca58e6 am: 54f6669aa2 am: b56bf6b79b am: 9951822be2
Original change: https://android-review.googlesource.com/c/platform/external/gwp_asan/+/2461753
Change-Id: I747358731df6d1bf22aa002c0e8b182cb2de9435
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | gwp_asan/optional/segv_handler_posix.cpp | 22 | ||||
-rw-r--r-- | gwp_asan/tests/backtrace.cpp | 4 | ||||
-rw-r--r-- | gwp_asan/tests/harness.cpp | 18 | ||||
-rw-r--r-- | gwp_asan/tests/harness.h | 2 | ||||
-rw-r--r-- | gwp_asan/tests/never_allocated.cpp | 55 | ||||
-rw-r--r-- | gwp_asan/tests/recoverable.cpp | 13 |
6 files changed, 91 insertions, 23 deletions
diff --git a/gwp_asan/optional/segv_handler_posix.cpp b/gwp_asan/optional/segv_handler_posix.cpp index e012963..198db5c 100644 --- a/gwp_asan/optional/segv_handler_posix.cpp +++ b/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/gwp_asan/tests/backtrace.cpp b/gwp_asan/tests/backtrace.cpp index e878994..7cbbcf5 100644 --- a/gwp_asan/tests/backtrace.cpp +++ b/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/gwp_asan/tests/harness.cpp b/gwp_asan/tests/harness.cpp index ccad80e..6d41630 100644 --- a/gwp_asan/tests/harness.cpp +++ b/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/gwp_asan/tests/harness.h b/gwp_asan/tests/harness.h index c8f643d..89f4e11 100644 --- a/gwp_asan/tests/harness.h +++ b/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/gwp_asan/tests/never_allocated.cpp b/gwp_asan/tests/never_allocated.cpp new file mode 100644 index 0000000..bd43b22 --- /dev/null +++ b/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 different 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/gwp_asan/tests/recoverable.cpp b/gwp_asan/tests/recoverable.cpp index adc9731..2c14ff5 100644 --- a/gwp_asan/tests/recoverable.cpp +++ b/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)); |