xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
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