| 1 | //===-- backtrace.cpp -------------------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include <regex> |
| 10 | #include <string> |
| 11 | |
| 12 | #include "gwp_asan/common.h" |
| 13 | #include "gwp_asan/crash_handler.h" |
| 14 | #include "gwp_asan/tests/harness.h" |
| 15 | |
| 16 | TEST_P(BacktraceGuardedPoolAllocatorDeathTest, DoubleFree) { |
| 17 | void *Ptr = AllocateMemory(GPA); |
| 18 | DeallocateMemory(GPA, Ptr); |
| 19 | |
| 20 | std::string DeathRegex = "Double Free.*DeallocateMemory2.*" ; |
| 21 | DeathRegex.append("was deallocated.*DeallocateMemory[^2].*" ); |
| 22 | DeathRegex.append("was allocated.*AllocateMemory" ); |
| 23 | if (!Recoverable) { |
| 24 | EXPECT_DEATH(DeallocateMemory2(GPA, Ptr), DeathRegex); |
| 25 | return; |
| 26 | } |
| 27 | |
| 28 | // For recoverable, assert that DeallocateMemory2() doesn't crash. |
| 29 | DeallocateMemory2(GPA, Ptr); |
| 30 | // Fuchsia's zxtest doesn't have an EXPECT_THAT(testing::MatchesRegex(), ...), |
| 31 | // so check the regex manually. |
| 32 | EXPECT_TRUE(std::regex_search( |
| 33 | GetOutputBuffer(), |
| 34 | std::basic_regex(DeathRegex, std::regex_constants::extended))) |
| 35 | << "Regex \"" << DeathRegex |
| 36 | << "\" was not found in input:\n============\n" |
| 37 | << GetOutputBuffer() << "\n============" ; |
| 38 | } |
| 39 | |
| 40 | TEST_P(BacktraceGuardedPoolAllocatorDeathTest, UseAfterFree) { |
| 41 | #if defined(__linux__) && __ARM_ARCH == 7 |
| 42 | // Incomplete backtrace on Armv7 Linux |
| 43 | GTEST_SKIP(); |
| 44 | #endif |
| 45 | |
| 46 | void *Ptr = AllocateMemory(GPA); |
| 47 | DeallocateMemory(GPA, Ptr); |
| 48 | |
| 49 | std::string DeathRegex = "Use After Free.*TouchMemory.*" ; |
| 50 | DeathRegex.append("was deallocated.*DeallocateMemory[^2].*" ); |
| 51 | DeathRegex.append("was allocated.*AllocateMemory" ); |
| 52 | |
| 53 | if (!Recoverable) { |
| 54 | EXPECT_DEATH(TouchMemory(Ptr), DeathRegex); |
| 55 | return; |
| 56 | } |
| 57 | |
| 58 | // For recoverable, assert that TouchMemory() doesn't crash. |
| 59 | TouchMemory(Ptr); |
| 60 | // Fuchsia's zxtest doesn't have an EXPECT_THAT(testing::MatchesRegex(), ...), |
| 61 | // so check the regex manually. |
| 62 | EXPECT_TRUE(std::regex_search( |
| 63 | GetOutputBuffer(), |
| 64 | std::basic_regex(DeathRegex, std::regex_constants::extended))) |
| 65 | << "Regex \"" << DeathRegex |
| 66 | << "\" was not found in input:\n============\n" |
| 67 | << GetOutputBuffer() << "\n============" ; |
| 68 | ; |
| 69 | } |
| 70 | |
| 71 | TEST(Backtrace, Short) { |
| 72 | gwp_asan::AllocationMetadata Meta; |
| 73 | Meta.AllocationTrace.RecordBacktrace( |
| 74 | [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t { |
| 75 | TraceBuffer[0] = 123u; |
| 76 | TraceBuffer[1] = 321u; |
| 77 | return 2u; |
| 78 | }); |
| 79 | uintptr_t TraceOutput[2] = {}; |
| 80 | EXPECT_EQ(2u, __gwp_asan_get_allocation_trace(&Meta, TraceOutput, 2)); |
| 81 | EXPECT_EQ(TraceOutput[0], 123u); |
| 82 | EXPECT_EQ(TraceOutput[1], 321u); |
| 83 | } |
| 84 | |
| 85 | TEST(Backtrace, ExceedsStorableLength) { |
| 86 | gwp_asan::AllocationMetadata Meta; |
| 87 | Meta.AllocationTrace.RecordBacktrace( |
| 88 | [](uintptr_t *TraceBuffer, size_t Size) -> size_t { |
| 89 | // Need to inintialise the elements that will be packed. |
| 90 | memset(TraceBuffer, 0u, Size * sizeof(*TraceBuffer)); |
| 91 | |
| 92 | // Indicate that there were more frames, and we just didn't have enough |
| 93 | // room to store them. |
| 94 | return Size * 2; |
| 95 | }); |
| 96 | // Retrieve a frame from the collected backtrace, make sure it works E2E. |
| 97 | uintptr_t TraceOutput; |
| 98 | EXPECT_EQ(gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect, |
| 99 | __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1)); |
| 100 | } |
| 101 | |
| 102 | TEST(Backtrace, ExceedsRetrievableAllocLength) { |
| 103 | gwp_asan::AllocationMetadata Meta; |
| 104 | constexpr size_t kNumFramesToStore = 3u; |
| 105 | Meta.AllocationTrace.RecordBacktrace( |
| 106 | [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t { |
| 107 | memset(TraceBuffer, kNumFramesToStore, |
| 108 | kNumFramesToStore * sizeof(*TraceBuffer)); |
| 109 | return kNumFramesToStore; |
| 110 | }); |
| 111 | uintptr_t TraceOutput; |
| 112 | // Ask for one element, get told that there's `kNumFramesToStore` available. |
| 113 | EXPECT_EQ(kNumFramesToStore, |
| 114 | __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1)); |
| 115 | } |
| 116 | |
| 117 | TEST(Backtrace, ExceedsRetrievableDeallocLength) { |
| 118 | gwp_asan::AllocationMetadata Meta; |
| 119 | constexpr size_t kNumFramesToStore = 3u; |
| 120 | Meta.DeallocationTrace.RecordBacktrace( |
| 121 | [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t { |
| 122 | memset(TraceBuffer, kNumFramesToStore, |
| 123 | kNumFramesToStore * sizeof(*TraceBuffer)); |
| 124 | return kNumFramesToStore; |
| 125 | }); |
| 126 | uintptr_t TraceOutput; |
| 127 | // Ask for one element, get told that there's `kNumFramesToStore` available. |
| 128 | EXPECT_EQ(kNumFramesToStore, |
| 129 | __gwp_asan_get_deallocation_trace(&Meta, &TraceOutput, 1)); |
| 130 | } |
| 131 | |