1 //===-- sanitizer_stacktrace_test.cpp -------------------------------------===// 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 // This file is a part of ThreadSanitizer/AddressSanitizer runtime. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "sanitizer_common/sanitizer_common.h" 14 #include "sanitizer_common/sanitizer_stacktrace.h" 15 #include "gtest/gtest.h" 16 17 namespace __sanitizer { 18 19 class FastUnwindTest : public ::testing::Test { 20 protected: 21 virtual void SetUp(); 22 virtual void TearDown(); 23 24 void UnwindFast(); 25 26 void *mapping; 27 uhwptr *fake_stack; 28 const uptr fake_stack_size = 10; 29 uhwptr start_pc; 30 31 uhwptr fake_bp; 32 uhwptr fake_top; 33 uhwptr fake_bottom; 34 BufferedStackTrace trace; 35 }; 36 37 static uptr PC(uptr idx) { 38 return (1<<20) + idx; 39 } 40 41 void FastUnwindTest::SetUp() { 42 size_t ps = GetPageSize(); 43 mapping = MmapOrDie(2 * ps, "FastUnwindTest"); 44 MprotectNoAccess((uptr)mapping, ps); 45 46 // Unwinder may peek 1 word down from the starting FP. 47 fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr)); 48 49 // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have 50 // even indices. 51 for (uptr i = 0; i + 1 < fake_stack_size; i += 2) { 52 fake_stack[i] = (uptr)&fake_stack[i+2]; // fp 53 fake_stack[i+1] = PC(i + 1); // retaddr 54 } 55 // Mark the last fp point back up to terminate the stack trace. 56 fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0]; 57 58 // Top is two slots past the end because UnwindFast subtracts two. 59 fake_top = (uhwptr)&fake_stack[fake_stack_size + 2]; 60 // Bottom is one slot before the start because UnwindFast uses >. 61 fake_bottom = (uhwptr)mapping; 62 fake_bp = (uptr)&fake_stack[0]; 63 start_pc = PC(0); 64 } 65 66 void FastUnwindTest::TearDown() { 67 size_t ps = GetPageSize(); 68 UnmapOrDie(mapping, 2 * ps); 69 } 70 71 #if SANITIZER_CAN_FAST_UNWIND 72 73 void FastUnwindTest::UnwindFast() { 74 trace.UnwindFast(start_pc, fake_bp, fake_top, fake_bottom, kStackTraceMax); 75 } 76 77 TEST_F(FastUnwindTest, Basic) { 78 UnwindFast(); 79 // Should get all on-stack retaddrs and start_pc. 80 EXPECT_EQ(6U, trace.size); 81 EXPECT_EQ(start_pc, trace.trace[0]); 82 for (uptr i = 1; i <= 5; i++) { 83 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); 84 } 85 } 86 87 // From: https://github.com/google/sanitizers/issues/162 88 TEST_F(FastUnwindTest, FramePointerLoop) { 89 // Make one fp point to itself. 90 fake_stack[4] = (uhwptr)&fake_stack[4]; 91 UnwindFast(); 92 // Should get all on-stack retaddrs up to the 4th slot and start_pc. 93 EXPECT_EQ(4U, trace.size); 94 EXPECT_EQ(start_pc, trace.trace[0]); 95 for (uptr i = 1; i <= 3; i++) { 96 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); 97 } 98 } 99 100 TEST_F(FastUnwindTest, MisalignedFramePointer) { 101 // Make one fp misaligned. 102 fake_stack[4] += 3; 103 UnwindFast(); 104 // Should get all on-stack retaddrs up to the 4th slot and start_pc. 105 EXPECT_EQ(4U, trace.size); 106 EXPECT_EQ(start_pc, trace.trace[0]); 107 for (uptr i = 1; i < 4U; i++) { 108 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); 109 } 110 } 111 112 TEST_F(FastUnwindTest, OneFrameStackTrace) { 113 trace.Unwind(start_pc, fake_bp, nullptr, true, 1); 114 EXPECT_EQ(1U, trace.size); 115 EXPECT_EQ(start_pc, trace.trace[0]); 116 EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp); 117 } 118 119 TEST_F(FastUnwindTest, ZeroFramesStackTrace) { 120 trace.Unwind(start_pc, fake_bp, nullptr, true, 0); 121 EXPECT_EQ(0U, trace.size); 122 EXPECT_EQ(0U, trace.top_frame_bp); 123 } 124 125 TEST_F(FastUnwindTest, FPBelowPrevFP) { 126 // The next FP points to unreadable memory inside the stack limits, but below 127 // current FP. 128 fake_stack[0] = (uhwptr)&fake_stack[-50]; 129 fake_stack[1] = PC(1); 130 UnwindFast(); 131 EXPECT_EQ(2U, trace.size); 132 EXPECT_EQ(PC(0), trace.trace[0]); 133 EXPECT_EQ(PC(1), trace.trace[1]); 134 } 135 136 TEST_F(FastUnwindTest, CloseToZeroFrame) { 137 // Make one pc a NULL pointer. 138 fake_stack[5] = 0x0; 139 UnwindFast(); 140 // The stack should be truncated at the NULL pointer (and not include it). 141 EXPECT_EQ(3U, trace.size); 142 EXPECT_EQ(start_pc, trace.trace[0]); 143 for (uptr i = 1; i < 3U; i++) { 144 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); 145 } 146 } 147 148 #endif // SANITIZER_CAN_FAST_UNWIND 149 150 TEST(SlowUnwindTest, ShortStackTrace) { 151 BufferedStackTrace stack; 152 uptr pc = StackTrace::GetCurrentPc(); 153 uptr bp = GET_CURRENT_FRAME(); 154 stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/0); 155 EXPECT_EQ(0U, stack.size); 156 EXPECT_EQ(0U, stack.top_frame_bp); 157 stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/1); 158 EXPECT_EQ(1U, stack.size); 159 EXPECT_EQ(pc, stack.trace[0]); 160 EXPECT_EQ(bp, stack.top_frame_bp); 161 } 162 163 } // namespace __sanitizer 164