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 #if defined(__riscv)
37   const uptr kFpOffset = 4;
38   const uptr kBpOffset = 2;
39 #else
40   const uptr kFpOffset = 2;
41   const uptr kBpOffset = 0;
42 #endif
43 };
44 
45 static uptr PC(uptr idx) {
46   return (1<<20) + idx;
47 }
48 
49 void FastUnwindTest::SetUp() {
50   size_t ps = GetPageSize();
51   mapping = MmapOrDie(2 * ps, "FastUnwindTest");
52   MprotectNoAccess((uptr)mapping, ps);
53 
54   // Unwinder may peek 1 word down from the starting FP.
55   fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr));
56 
57   // Fill an array of pointers with fake fp+retaddr pairs.  Frame pointers have
58   // even indices.
59   for (uptr i = 0; i + 1 < fake_stack_size; i += 2) {
60     fake_stack[i] = (uptr)&fake_stack[i + kFpOffset];  // fp
61     fake_stack[i+1] = PC(i + 1); // retaddr
62   }
63   // Mark the last fp point back up to terminate the stack trace.
64   fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0];
65 
66   // Top is two slots past the end because UnwindFast subtracts two.
67   fake_top = (uhwptr)&fake_stack[fake_stack_size + kFpOffset];
68   // Bottom is one slot before the start because UnwindFast uses >.
69   fake_bottom = (uhwptr)mapping;
70   fake_bp = (uptr)&fake_stack[kBpOffset];
71   start_pc = PC(0);
72 }
73 
74 void FastUnwindTest::TearDown() {
75   size_t ps = GetPageSize();
76   UnmapOrDie(mapping, 2 * ps);
77 }
78 
79 #if SANITIZER_CAN_FAST_UNWIND
80 
81 #ifdef __sparc__
82 // Fake stacks don't meet SPARC UnwindFast requirements.
83 #define SKIP_ON_SPARC(x) DISABLED_##x
84 #else
85 #define SKIP_ON_SPARC(x) x
86 #endif
87 
88 void FastUnwindTest::UnwindFast() {
89   trace.UnwindFast(start_pc, fake_bp, fake_top, fake_bottom, kStackTraceMax);
90 }
91 
92 TEST_F(FastUnwindTest, SKIP_ON_SPARC(Basic)) {
93   UnwindFast();
94   // Should get all on-stack retaddrs and start_pc.
95   EXPECT_EQ(6U, trace.size);
96   EXPECT_EQ(start_pc, trace.trace[0]);
97   for (uptr i = 1; i <= 5; i++) {
98     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
99   }
100 }
101 
102 // From: https://github.com/google/sanitizers/issues/162
103 TEST_F(FastUnwindTest, SKIP_ON_SPARC(FramePointerLoop)) {
104   // Make one fp point to itself.
105   fake_stack[4] = (uhwptr)&fake_stack[4];
106   UnwindFast();
107   // Should get all on-stack retaddrs up to the 4th slot and start_pc.
108   EXPECT_EQ(4U, trace.size);
109   EXPECT_EQ(start_pc, trace.trace[0]);
110   for (uptr i = 1; i <= 3; i++) {
111     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
112   }
113 }
114 
115 TEST_F(FastUnwindTest, SKIP_ON_SPARC(MisalignedFramePointer)) {
116   // Make one fp misaligned.
117   fake_stack[4] += 3;
118   UnwindFast();
119   // Should get all on-stack retaddrs up to the 4th slot and start_pc.
120   EXPECT_EQ(4U, trace.size);
121   EXPECT_EQ(start_pc, trace.trace[0]);
122   for (uptr i = 1; i < 4U; i++) {
123     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
124   }
125 }
126 
127 TEST_F(FastUnwindTest, OneFrameStackTrace) {
128   trace.Unwind(start_pc, fake_bp, nullptr, true, 1);
129   EXPECT_EQ(1U, trace.size);
130   EXPECT_EQ(start_pc, trace.trace[0]);
131   EXPECT_EQ((uhwptr)&fake_stack[kBpOffset], trace.top_frame_bp);
132 }
133 
134 TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
135   trace.Unwind(start_pc, fake_bp, nullptr, true, 0);
136   EXPECT_EQ(0U, trace.size);
137   EXPECT_EQ(0U, trace.top_frame_bp);
138 }
139 
140 TEST_F(FastUnwindTest, SKIP_ON_SPARC(FPBelowPrevFP)) {
141   // The next FP points to unreadable memory inside the stack limits, but below
142   // current FP.
143   fake_stack[0] = (uhwptr)&fake_stack[-50];
144   fake_stack[1] = PC(1);
145   UnwindFast();
146   EXPECT_EQ(2U, trace.size);
147   EXPECT_EQ(PC(0), trace.trace[0]);
148   EXPECT_EQ(PC(1), trace.trace[1]);
149 }
150 
151 TEST_F(FastUnwindTest, SKIP_ON_SPARC(CloseToZeroFrame)) {
152   // Make one pc a NULL pointer.
153   fake_stack[5] = 0x0;
154   UnwindFast();
155   // The stack should be truncated at the NULL pointer (and not include it).
156   EXPECT_EQ(3U, trace.size);
157   EXPECT_EQ(start_pc, trace.trace[0]);
158   for (uptr i = 1; i < 3U; i++) {
159     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
160   }
161 }
162 
163 #endif // SANITIZER_CAN_FAST_UNWIND
164 
165 TEST(SlowUnwindTest, ShortStackTrace) {
166   BufferedStackTrace stack;
167   uptr pc = StackTrace::GetCurrentPc();
168   uptr bp = GET_CURRENT_FRAME();
169   stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/0);
170   EXPECT_EQ(0U, stack.size);
171   EXPECT_EQ(0U, stack.top_frame_bp);
172   stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/1);
173   EXPECT_EQ(1U, stack.size);
174   EXPECT_EQ(pc, stack.trace[0]);
175   EXPECT_EQ(bp, stack.top_frame_bp);
176 }
177 
178 }  // namespace __sanitizer
179