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 #ifdef __sparc__
74 // Fake stacks don't meet SPARC UnwindFast requirements.
75 #define SKIP_ON_SPARC(x) DISABLED_##x
76 #else
77 #define SKIP_ON_SPARC(x) x
78 #endif
79 
80 void FastUnwindTest::UnwindFast() {
81   trace.UnwindFast(start_pc, fake_bp, fake_top, fake_bottom, kStackTraceMax);
82 }
83 
84 TEST_F(FastUnwindTest, SKIP_ON_SPARC(Basic)) {
85   UnwindFast();
86   // Should get all on-stack retaddrs and start_pc.
87   EXPECT_EQ(6U, trace.size);
88   EXPECT_EQ(start_pc, trace.trace[0]);
89   for (uptr i = 1; i <= 5; i++) {
90     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
91   }
92 }
93 
94 // From: https://github.com/google/sanitizers/issues/162
95 TEST_F(FastUnwindTest, SKIP_ON_SPARC(FramePointerLoop)) {
96   // Make one fp point to itself.
97   fake_stack[4] = (uhwptr)&fake_stack[4];
98   UnwindFast();
99   // Should get all on-stack retaddrs up to the 4th slot and start_pc.
100   EXPECT_EQ(4U, trace.size);
101   EXPECT_EQ(start_pc, trace.trace[0]);
102   for (uptr i = 1; i <= 3; i++) {
103     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
104   }
105 }
106 
107 TEST_F(FastUnwindTest, SKIP_ON_SPARC(MisalignedFramePointer)) {
108   // Make one fp misaligned.
109   fake_stack[4] += 3;
110   UnwindFast();
111   // Should get all on-stack retaddrs up to the 4th slot and start_pc.
112   EXPECT_EQ(4U, trace.size);
113   EXPECT_EQ(start_pc, trace.trace[0]);
114   for (uptr i = 1; i < 4U; i++) {
115     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
116   }
117 }
118 
119 TEST_F(FastUnwindTest, OneFrameStackTrace) {
120   trace.Unwind(start_pc, fake_bp, nullptr, true, 1);
121   EXPECT_EQ(1U, trace.size);
122   EXPECT_EQ(start_pc, trace.trace[0]);
123   EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp);
124 }
125 
126 TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
127   trace.Unwind(start_pc, fake_bp, nullptr, true, 0);
128   EXPECT_EQ(0U, trace.size);
129   EXPECT_EQ(0U, trace.top_frame_bp);
130 }
131 
132 TEST_F(FastUnwindTest, SKIP_ON_SPARC(FPBelowPrevFP)) {
133   // The next FP points to unreadable memory inside the stack limits, but below
134   // current FP.
135   fake_stack[0] = (uhwptr)&fake_stack[-50];
136   fake_stack[1] = PC(1);
137   UnwindFast();
138   EXPECT_EQ(2U, trace.size);
139   EXPECT_EQ(PC(0), trace.trace[0]);
140   EXPECT_EQ(PC(1), trace.trace[1]);
141 }
142 
143 TEST_F(FastUnwindTest, SKIP_ON_SPARC(CloseToZeroFrame)) {
144   // Make one pc a NULL pointer.
145   fake_stack[5] = 0x0;
146   UnwindFast();
147   // The stack should be truncated at the NULL pointer (and not include it).
148   EXPECT_EQ(3U, trace.size);
149   EXPECT_EQ(start_pc, trace.trace[0]);
150   for (uptr i = 1; i < 3U; i++) {
151     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
152   }
153 }
154 
155 #endif // SANITIZER_CAN_FAST_UNWIND
156 
157 TEST(SlowUnwindTest, ShortStackTrace) {
158   BufferedStackTrace stack;
159   uptr pc = StackTrace::GetCurrentPc();
160   uptr bp = GET_CURRENT_FRAME();
161   stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/0);
162   EXPECT_EQ(0U, stack.size);
163   EXPECT_EQ(0U, stack.top_frame_bp);
164   stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/1);
165   EXPECT_EQ(1U, stack.size);
166   EXPECT_EQ(pc, stack.trace[0]);
167   EXPECT_EQ(bp, stack.top_frame_bp);
168 }
169 
170 }  // namespace __sanitizer
171