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_stacktrace.h" |
14 | |
15 | #include <string.h> |
16 | |
17 | #include <algorithm> |
18 | #include <string> |
19 | |
20 | #include "gmock/gmock.h" |
21 | #include "gtest/gtest.h" |
22 | #include "sanitizer_common/sanitizer_common.h" |
23 | #include "sanitizer_internal_defs.h" |
24 | |
25 | using testing::ContainsRegex; |
26 | using testing::MatchesRegex; |
27 | |
28 | namespace __sanitizer { |
29 | |
30 | class FastUnwindTest : public ::testing::Test { |
31 | protected: |
32 | virtual void SetUp(); |
33 | virtual void TearDown(); |
34 | |
35 | void UnwindFast(); |
36 | |
37 | void *mapping; |
38 | uhwptr *fake_stack; |
39 | const uptr fake_stack_size = 10; |
40 | uhwptr start_pc; |
41 | |
42 | uhwptr fake_bp; |
43 | uhwptr fake_top; |
44 | uhwptr fake_bottom; |
45 | BufferedStackTrace trace; |
46 | |
47 | #if defined(__loongarch__) || defined(__riscv) |
48 | const uptr kFpOffset = 4; |
49 | const uptr kBpOffset = 2; |
50 | #else |
51 | const uptr kFpOffset = 2; |
52 | const uptr kBpOffset = 0; |
53 | #endif |
54 | |
55 | private: |
56 | CommonFlags tmp_flags_; |
57 | }; |
58 | |
59 | static uptr PC(uptr idx) { |
60 | return (1<<20) + idx; |
61 | } |
62 | |
63 | void FastUnwindTest::SetUp() { |
64 | size_t ps = GetPageSize(); |
65 | mapping = MmapOrDie(size: 2 * ps, mem_type: "FastUnwindTest" ); |
66 | MprotectNoAccess(addr: (uptr)mapping, size: ps); |
67 | |
68 | // Unwinder may peek 1 word down from the starting FP. |
69 | fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr)); |
70 | |
71 | // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have |
72 | // even indices. |
73 | for (uptr i = 0; i + 1 < fake_stack_size; i += 2) { |
74 | fake_stack[i] = (uptr)&fake_stack[i + kFpOffset]; // fp |
75 | fake_stack[i+1] = PC(idx: i + 1); // retaddr |
76 | } |
77 | // Mark the last fp point back up to terminate the stack trace. |
78 | fake_stack[RoundDownTo(x: fake_stack_size - 1, boundary: 2)] = (uhwptr)&fake_stack[0]; |
79 | |
80 | // Top is two slots past the end because UnwindFast subtracts two. |
81 | fake_top = (uhwptr)&fake_stack[fake_stack_size + kFpOffset]; |
82 | // Bottom is one slot before the start because UnwindFast uses >. |
83 | fake_bottom = (uhwptr)mapping; |
84 | fake_bp = (uptr)&fake_stack[kBpOffset]; |
85 | start_pc = PC(idx: 0); |
86 | |
87 | tmp_flags_.CopyFrom(other: *common_flags()); |
88 | } |
89 | |
90 | void FastUnwindTest::TearDown() { |
91 | size_t ps = GetPageSize(); |
92 | UnmapOrDie(addr: mapping, size: 2 * ps); |
93 | |
94 | // Restore default flags. |
95 | OverrideCommonFlags(cf: tmp_flags_); |
96 | } |
97 | |
98 | #if SANITIZER_CAN_FAST_UNWIND |
99 | |
100 | #ifdef __sparc__ |
101 | // Fake stacks don't meet SPARC UnwindFast requirements. |
102 | #define SKIP_ON_SPARC(x) DISABLED_##x |
103 | #else |
104 | #define SKIP_ON_SPARC(x) x |
105 | #endif |
106 | |
107 | void FastUnwindTest::UnwindFast() { |
108 | trace.UnwindFast(pc: start_pc, bp: fake_bp, stack_top: fake_top, stack_bottom: fake_bottom, max_depth: kStackTraceMax); |
109 | } |
110 | |
111 | TEST_F(FastUnwindTest, SKIP_ON_SPARC(Basic)) { |
112 | UnwindFast(); |
113 | // Should get all on-stack retaddrs and start_pc. |
114 | EXPECT_EQ(6U, trace.size); |
115 | EXPECT_EQ(start_pc, trace.trace[0]); |
116 | for (uptr i = 1; i <= 5; i++) { |
117 | EXPECT_EQ(PC(idx: i*2 - 1), trace.trace[i]); |
118 | } |
119 | } |
120 | |
121 | // From: https://github.com/google/sanitizers/issues/162 |
122 | TEST_F(FastUnwindTest, SKIP_ON_SPARC(FramePointerLoop)) { |
123 | // Make one fp point to itself. |
124 | fake_stack[4] = (uhwptr)&fake_stack[4]; |
125 | UnwindFast(); |
126 | // Should get all on-stack retaddrs up to the 4th slot and start_pc. |
127 | EXPECT_EQ(4U, trace.size); |
128 | EXPECT_EQ(start_pc, trace.trace[0]); |
129 | for (uptr i = 1; i <= 3; i++) { |
130 | EXPECT_EQ(PC(idx: i*2 - 1), trace.trace[i]); |
131 | } |
132 | } |
133 | |
134 | TEST_F(FastUnwindTest, SKIP_ON_SPARC(MisalignedFramePointer)) { |
135 | // Make one fp misaligned. |
136 | fake_stack[4] += 3; |
137 | UnwindFast(); |
138 | // Should get all on-stack retaddrs up to the 4th slot and start_pc. |
139 | EXPECT_EQ(4U, trace.size); |
140 | EXPECT_EQ(start_pc, trace.trace[0]); |
141 | for (uptr i = 1; i < 4U; i++) { |
142 | EXPECT_EQ(PC(idx: i*2 - 1), trace.trace[i]); |
143 | } |
144 | } |
145 | |
146 | TEST_F(FastUnwindTest, OneFrameStackTrace) { |
147 | trace.Unwind(start_pc, fake_bp, nullptr, true, 1); |
148 | EXPECT_EQ(1U, trace.size); |
149 | EXPECT_EQ(start_pc, trace.trace[0]); |
150 | EXPECT_EQ((uhwptr)&fake_stack[kBpOffset], trace.top_frame_bp); |
151 | } |
152 | |
153 | TEST_F(FastUnwindTest, ZeroFramesStackTrace) { |
154 | trace.Unwind(start_pc, fake_bp, nullptr, true, 0); |
155 | EXPECT_EQ(0U, trace.size); |
156 | EXPECT_EQ(0U, trace.top_frame_bp); |
157 | } |
158 | |
159 | TEST_F(FastUnwindTest, SKIP_ON_SPARC(FPBelowPrevFP)) { |
160 | // The next FP points to unreadable memory inside the stack limits, but below |
161 | // current FP. |
162 | fake_stack[0] = (uhwptr)&fake_stack[-50]; |
163 | fake_stack[1] = PC(1); |
164 | UnwindFast(); |
165 | EXPECT_EQ(2U, trace.size); |
166 | EXPECT_EQ(PC(idx: 0), trace.trace[0]); |
167 | EXPECT_EQ(PC(idx: 1), trace.trace[1]); |
168 | } |
169 | |
170 | TEST_F(FastUnwindTest, SKIP_ON_SPARC(CloseToZeroFrame)) { |
171 | // Make one pc a NULL pointer. |
172 | fake_stack[5] = 0x0; |
173 | UnwindFast(); |
174 | // The stack should be truncated at the NULL pointer (and not include it). |
175 | EXPECT_EQ(3U, trace.size); |
176 | EXPECT_EQ(start_pc, trace.trace[0]); |
177 | for (uptr i = 1; i < 3U; i++) { |
178 | EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); |
179 | } |
180 | } |
181 | |
182 | using StackPrintTest = FastUnwindTest; |
183 | |
184 | TEST_F(StackPrintTest, SKIP_ON_SPARC(ContainsFullTrace)) { |
185 | // Override stack trace format to make testing code independent of default |
186 | // flag values. |
187 | CommonFlags flags; |
188 | flags.CopyFrom(other: *common_flags()); |
189 | flags.stack_trace_format = "#%n %p" ; |
190 | OverrideCommonFlags(cf: flags); |
191 | |
192 | UnwindFast(); |
193 | |
194 | char buf[3000]; |
195 | trace.PrintTo(buf, sizeof(buf)); |
196 | EXPECT_THAT(std::string(buf), |
197 | MatchesRegex("(#[0-9]+ 0x[0-9a-f]+\n){" + |
198 | std::to_string(trace.size) + "}\n" )); |
199 | } |
200 | |
201 | TEST_F(StackPrintTest, SKIP_ON_SPARC(TruncatesContents)) { |
202 | UnwindFast(); |
203 | |
204 | char buf[3000]; |
205 | uptr actual_len = trace.PrintTo(buf, sizeof(buf)); |
206 | ASSERT_LT(actual_len, sizeof(buf)); |
207 | |
208 | char tinybuf[10]; |
209 | trace.PrintTo(tinybuf, sizeof(tinybuf)); |
210 | |
211 | // This the truncation case. |
212 | ASSERT_GT(actual_len, sizeof(tinybuf)); |
213 | |
214 | // The truncated contents should be a prefix of the full contents. |
215 | size_t lastpos = sizeof(tinybuf) - 1; |
216 | EXPECT_EQ(strncmp(s1: buf, s2: tinybuf, n: lastpos), 0); |
217 | EXPECT_EQ(tinybuf[lastpos], '\0'); |
218 | |
219 | // Full bufffer has more contents... |
220 | EXPECT_NE(buf[lastpos], '\0'); |
221 | } |
222 | |
223 | TEST_F(StackPrintTest, SKIP_ON_SPARC(WorksWithEmptyStack)) { |
224 | char buf[3000]; |
225 | trace.PrintTo(buf, sizeof(buf)); |
226 | EXPECT_NE(strstr(haystack: buf, needle: "<empty stack>" ), nullptr); |
227 | } |
228 | |
229 | TEST_F(StackPrintTest, SKIP_ON_SPARC(ReturnsCorrectLength)) { |
230 | UnwindFast(); |
231 | |
232 | char buf[3000]; |
233 | uptr len = trace.PrintTo(buf, sizeof(buf)); |
234 | size_t actual_len = strlen(s: buf); |
235 | ASSERT_LT(len, sizeof(buf)); |
236 | EXPECT_EQ(len, actual_len); |
237 | |
238 | char tinybuf[5]; |
239 | len = trace.PrintTo(tinybuf, sizeof(tinybuf)); |
240 | size_t truncated_len = strlen(s: tinybuf); |
241 | ASSERT_GE(len, sizeof(tinybuf)); |
242 | EXPECT_EQ(len, actual_len); |
243 | EXPECT_EQ(truncated_len, sizeof(tinybuf) - 1); |
244 | } |
245 | |
246 | TEST_F(StackPrintTest, SKIP_ON_SPARC(AcceptsZeroSize)) { |
247 | UnwindFast(); |
248 | char buf[1]; |
249 | EXPECT_GT(trace.PrintTo(buf, 0), 0u); |
250 | } |
251 | |
252 | using StackPrintDeathTest = StackPrintTest; |
253 | |
254 | TEST_F(StackPrintDeathTest, SKIP_ON_SPARC(RequiresNonNullBuffer)) { |
255 | UnwindFast(); |
256 | EXPECT_DEATH(trace.PrintTo(NULL, 100), "" ); |
257 | } |
258 | |
259 | #endif // SANITIZER_CAN_FAST_UNWIND |
260 | |
261 | TEST(SlowUnwindTest, ShortStackTrace) { |
262 | BufferedStackTrace stack; |
263 | uptr pc = StackTrace::GetCurrentPc(); |
264 | uptr bp = GET_CURRENT_FRAME(); |
265 | stack.Unwind(pc, bp, context: nullptr, request_fast: false, /*max_depth=*/0); |
266 | EXPECT_EQ(0U, stack.size); |
267 | EXPECT_EQ(0U, stack.top_frame_bp); |
268 | stack.Unwind(pc, bp, context: nullptr, request_fast: false, /*max_depth=*/1); |
269 | EXPECT_EQ(1U, stack.size); |
270 | EXPECT_EQ(pc, stack.trace[0]); |
271 | EXPECT_EQ(bp, stack.top_frame_bp); |
272 | } |
273 | |
274 | TEST(GetCurrentPc, Basic) { |
275 | // Test that PCs obtained via GET_CURRENT_PC() |
276 | // and StackTrace::GetCurrentPc() are all different |
277 | // and are close to the function start. |
278 | struct Local { |
279 | static NOINLINE void Test() { |
280 | const uptr pcs[] = { |
281 | (uptr)&Local::Test, |
282 | GET_CURRENT_PC(), |
283 | StackTrace::GetCurrentPc(), |
284 | StackTrace::GetCurrentPc(), |
285 | }; |
286 | for (uptr i = 0; i < ARRAY_SIZE(pcs); i++) |
287 | Printf(format: "pc%zu: 0x%zx\n" , i, pcs[i]); |
288 | for (uptr i = 1; i < ARRAY_SIZE(pcs); i++) { |
289 | EXPECT_GT(pcs[i], pcs[0]); |
290 | EXPECT_LT(pcs[i], pcs[0] + 1000); |
291 | for (uptr j = 0; j < i; j++) EXPECT_NE(pcs[i], pcs[j]); |
292 | } |
293 | } |
294 | }; |
295 | Local::Test(); |
296 | } |
297 | |
298 | // Dummy implementation. This should never be called, but is required to link |
299 | // non-optimized builds of this test. |
300 | void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context, |
301 | bool request_fast, u32 max_depth) { |
302 | UNIMPLEMENTED(); |
303 | } |
304 | |
305 | } // namespace __sanitizer |
306 | |