xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- sanitizer_stacktrace_test.cpp -------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
103cab2bb3Spatrick //
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick 
133cab2bb3Spatrick #include "sanitizer_common/sanitizer_stacktrace.h"
14d89ec533Spatrick 
15d89ec533Spatrick #include <string.h>
16d89ec533Spatrick 
17d89ec533Spatrick #include <algorithm>
18d89ec533Spatrick #include <string>
19d89ec533Spatrick 
20d89ec533Spatrick #include "gmock/gmock.h"
213cab2bb3Spatrick #include "gtest/gtest.h"
22d89ec533Spatrick #include "sanitizer_common/sanitizer_common.h"
23d89ec533Spatrick #include "sanitizer_internal_defs.h"
24d89ec533Spatrick 
25d89ec533Spatrick using testing::ContainsRegex;
26d89ec533Spatrick using testing::MatchesRegex;
273cab2bb3Spatrick 
283cab2bb3Spatrick namespace __sanitizer {
293cab2bb3Spatrick 
303cab2bb3Spatrick class FastUnwindTest : public ::testing::Test {
313cab2bb3Spatrick  protected:
323cab2bb3Spatrick   virtual void SetUp();
333cab2bb3Spatrick   virtual void TearDown();
343cab2bb3Spatrick 
353cab2bb3Spatrick   void UnwindFast();
363cab2bb3Spatrick 
373cab2bb3Spatrick   void *mapping;
383cab2bb3Spatrick   uhwptr *fake_stack;
393cab2bb3Spatrick   const uptr fake_stack_size = 10;
403cab2bb3Spatrick   uhwptr start_pc;
413cab2bb3Spatrick 
423cab2bb3Spatrick   uhwptr fake_bp;
433cab2bb3Spatrick   uhwptr fake_top;
443cab2bb3Spatrick   uhwptr fake_bottom;
453cab2bb3Spatrick   BufferedStackTrace trace;
46d89ec533Spatrick 
47*810390e3Srobert #if defined(__loongarch__) || defined(__riscv)
48d89ec533Spatrick   const uptr kFpOffset = 4;
49d89ec533Spatrick   const uptr kBpOffset = 2;
50d89ec533Spatrick #else
51d89ec533Spatrick   const uptr kFpOffset = 2;
52d89ec533Spatrick   const uptr kBpOffset = 0;
53d89ec533Spatrick #endif
54d89ec533Spatrick 
55d89ec533Spatrick  private:
56d89ec533Spatrick   CommonFlags tmp_flags_;
573cab2bb3Spatrick };
583cab2bb3Spatrick 
PC(uptr idx)593cab2bb3Spatrick static uptr PC(uptr idx) {
603cab2bb3Spatrick   return (1<<20) + idx;
613cab2bb3Spatrick }
623cab2bb3Spatrick 
SetUp()633cab2bb3Spatrick void FastUnwindTest::SetUp() {
643cab2bb3Spatrick   size_t ps = GetPageSize();
653cab2bb3Spatrick   mapping = MmapOrDie(2 * ps, "FastUnwindTest");
663cab2bb3Spatrick   MprotectNoAccess((uptr)mapping, ps);
673cab2bb3Spatrick 
683cab2bb3Spatrick   // Unwinder may peek 1 word down from the starting FP.
693cab2bb3Spatrick   fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr));
703cab2bb3Spatrick 
713cab2bb3Spatrick   // Fill an array of pointers with fake fp+retaddr pairs.  Frame pointers have
723cab2bb3Spatrick   // even indices.
733cab2bb3Spatrick   for (uptr i = 0; i + 1 < fake_stack_size; i += 2) {
74d89ec533Spatrick     fake_stack[i] = (uptr)&fake_stack[i + kFpOffset];  // fp
753cab2bb3Spatrick     fake_stack[i+1] = PC(i + 1); // retaddr
763cab2bb3Spatrick   }
773cab2bb3Spatrick   // Mark the last fp point back up to terminate the stack trace.
783cab2bb3Spatrick   fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0];
793cab2bb3Spatrick 
803cab2bb3Spatrick   // Top is two slots past the end because UnwindFast subtracts two.
81d89ec533Spatrick   fake_top = (uhwptr)&fake_stack[fake_stack_size + kFpOffset];
823cab2bb3Spatrick   // Bottom is one slot before the start because UnwindFast uses >.
833cab2bb3Spatrick   fake_bottom = (uhwptr)mapping;
84d89ec533Spatrick   fake_bp = (uptr)&fake_stack[kBpOffset];
853cab2bb3Spatrick   start_pc = PC(0);
86d89ec533Spatrick 
87d89ec533Spatrick   tmp_flags_.CopyFrom(*common_flags());
883cab2bb3Spatrick }
893cab2bb3Spatrick 
TearDown()903cab2bb3Spatrick void FastUnwindTest::TearDown() {
913cab2bb3Spatrick   size_t ps = GetPageSize();
923cab2bb3Spatrick   UnmapOrDie(mapping, 2 * ps);
93d89ec533Spatrick 
94d89ec533Spatrick   // Restore default flags.
95d89ec533Spatrick   OverrideCommonFlags(tmp_flags_);
963cab2bb3Spatrick }
973cab2bb3Spatrick 
983cab2bb3Spatrick #if SANITIZER_CAN_FAST_UNWIND
993cab2bb3Spatrick 
100d89ec533Spatrick #ifdef __sparc__
101d89ec533Spatrick // Fake stacks don't meet SPARC UnwindFast requirements.
102d89ec533Spatrick #define SKIP_ON_SPARC(x) DISABLED_##x
103d89ec533Spatrick #else
104d89ec533Spatrick #define SKIP_ON_SPARC(x) x
105d89ec533Spatrick #endif
106d89ec533Spatrick 
UnwindFast()1073cab2bb3Spatrick void FastUnwindTest::UnwindFast() {
1083cab2bb3Spatrick   trace.UnwindFast(start_pc, fake_bp, fake_top, fake_bottom, kStackTraceMax);
1093cab2bb3Spatrick }
1103cab2bb3Spatrick 
TEST_F(FastUnwindTest,SKIP_ON_SPARC (Basic))111d89ec533Spatrick TEST_F(FastUnwindTest, SKIP_ON_SPARC(Basic)) {
1123cab2bb3Spatrick   UnwindFast();
1133cab2bb3Spatrick   // Should get all on-stack retaddrs and start_pc.
1143cab2bb3Spatrick   EXPECT_EQ(6U, trace.size);
1153cab2bb3Spatrick   EXPECT_EQ(start_pc, trace.trace[0]);
1163cab2bb3Spatrick   for (uptr i = 1; i <= 5; i++) {
1173cab2bb3Spatrick     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
1183cab2bb3Spatrick   }
1193cab2bb3Spatrick }
1203cab2bb3Spatrick 
1213cab2bb3Spatrick // From: https://github.com/google/sanitizers/issues/162
TEST_F(FastUnwindTest,SKIP_ON_SPARC (FramePointerLoop))122d89ec533Spatrick TEST_F(FastUnwindTest, SKIP_ON_SPARC(FramePointerLoop)) {
1233cab2bb3Spatrick   // Make one fp point to itself.
1243cab2bb3Spatrick   fake_stack[4] = (uhwptr)&fake_stack[4];
1253cab2bb3Spatrick   UnwindFast();
1263cab2bb3Spatrick   // Should get all on-stack retaddrs up to the 4th slot and start_pc.
1273cab2bb3Spatrick   EXPECT_EQ(4U, trace.size);
1283cab2bb3Spatrick   EXPECT_EQ(start_pc, trace.trace[0]);
1293cab2bb3Spatrick   for (uptr i = 1; i <= 3; i++) {
1303cab2bb3Spatrick     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
1313cab2bb3Spatrick   }
1323cab2bb3Spatrick }
1333cab2bb3Spatrick 
TEST_F(FastUnwindTest,SKIP_ON_SPARC (MisalignedFramePointer))134d89ec533Spatrick TEST_F(FastUnwindTest, SKIP_ON_SPARC(MisalignedFramePointer)) {
1353cab2bb3Spatrick   // Make one fp misaligned.
1363cab2bb3Spatrick   fake_stack[4] += 3;
1373cab2bb3Spatrick   UnwindFast();
1383cab2bb3Spatrick   // Should get all on-stack retaddrs up to the 4th slot and start_pc.
1393cab2bb3Spatrick   EXPECT_EQ(4U, trace.size);
1403cab2bb3Spatrick   EXPECT_EQ(start_pc, trace.trace[0]);
1413cab2bb3Spatrick   for (uptr i = 1; i < 4U; i++) {
1423cab2bb3Spatrick     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
1433cab2bb3Spatrick   }
1443cab2bb3Spatrick }
1453cab2bb3Spatrick 
TEST_F(FastUnwindTest,OneFrameStackTrace)1463cab2bb3Spatrick TEST_F(FastUnwindTest, OneFrameStackTrace) {
1473cab2bb3Spatrick   trace.Unwind(start_pc, fake_bp, nullptr, true, 1);
1483cab2bb3Spatrick   EXPECT_EQ(1U, trace.size);
1493cab2bb3Spatrick   EXPECT_EQ(start_pc, trace.trace[0]);
150d89ec533Spatrick   EXPECT_EQ((uhwptr)&fake_stack[kBpOffset], trace.top_frame_bp);
1513cab2bb3Spatrick }
1523cab2bb3Spatrick 
TEST_F(FastUnwindTest,ZeroFramesStackTrace)1533cab2bb3Spatrick TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
1543cab2bb3Spatrick   trace.Unwind(start_pc, fake_bp, nullptr, true, 0);
1553cab2bb3Spatrick   EXPECT_EQ(0U, trace.size);
1563cab2bb3Spatrick   EXPECT_EQ(0U, trace.top_frame_bp);
1573cab2bb3Spatrick }
1583cab2bb3Spatrick 
TEST_F(FastUnwindTest,SKIP_ON_SPARC (FPBelowPrevFP))159d89ec533Spatrick TEST_F(FastUnwindTest, SKIP_ON_SPARC(FPBelowPrevFP)) {
1603cab2bb3Spatrick   // The next FP points to unreadable memory inside the stack limits, but below
1613cab2bb3Spatrick   // current FP.
1623cab2bb3Spatrick   fake_stack[0] = (uhwptr)&fake_stack[-50];
1633cab2bb3Spatrick   fake_stack[1] = PC(1);
1643cab2bb3Spatrick   UnwindFast();
1653cab2bb3Spatrick   EXPECT_EQ(2U, trace.size);
1663cab2bb3Spatrick   EXPECT_EQ(PC(0), trace.trace[0]);
1673cab2bb3Spatrick   EXPECT_EQ(PC(1), trace.trace[1]);
1683cab2bb3Spatrick }
1693cab2bb3Spatrick 
TEST_F(FastUnwindTest,SKIP_ON_SPARC (CloseToZeroFrame))170d89ec533Spatrick TEST_F(FastUnwindTest, SKIP_ON_SPARC(CloseToZeroFrame)) {
1713cab2bb3Spatrick   // Make one pc a NULL pointer.
1723cab2bb3Spatrick   fake_stack[5] = 0x0;
1733cab2bb3Spatrick   UnwindFast();
1743cab2bb3Spatrick   // The stack should be truncated at the NULL pointer (and not include it).
1753cab2bb3Spatrick   EXPECT_EQ(3U, trace.size);
1763cab2bb3Spatrick   EXPECT_EQ(start_pc, trace.trace[0]);
1773cab2bb3Spatrick   for (uptr i = 1; i < 3U; i++) {
1783cab2bb3Spatrick     EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
1793cab2bb3Spatrick   }
1803cab2bb3Spatrick }
1813cab2bb3Spatrick 
182d89ec533Spatrick using StackPrintTest = FastUnwindTest;
183d89ec533Spatrick 
TEST_F(StackPrintTest,SKIP_ON_SPARC (ContainsFullTrace))184d89ec533Spatrick TEST_F(StackPrintTest, SKIP_ON_SPARC(ContainsFullTrace)) {
185d89ec533Spatrick   // Override stack trace format to make testing code independent of default
186d89ec533Spatrick   // flag values.
187d89ec533Spatrick   CommonFlags flags;
188d89ec533Spatrick   flags.CopyFrom(*common_flags());
189d89ec533Spatrick   flags.stack_trace_format = "#%n %p";
190d89ec533Spatrick   OverrideCommonFlags(flags);
191d89ec533Spatrick 
192d89ec533Spatrick   UnwindFast();
193d89ec533Spatrick 
194d89ec533Spatrick   char buf[3000];
195d89ec533Spatrick   trace.PrintTo(buf, sizeof(buf));
196d89ec533Spatrick   EXPECT_THAT(std::string(buf),
197d89ec533Spatrick               MatchesRegex("(#[0-9]+ 0x[0-9a-f]+\n){" +
198d89ec533Spatrick                            std::to_string(trace.size) + "}\n"));
199d89ec533Spatrick }
200d89ec533Spatrick 
TEST_F(StackPrintTest,SKIP_ON_SPARC (TruncatesContents))201d89ec533Spatrick TEST_F(StackPrintTest, SKIP_ON_SPARC(TruncatesContents)) {
202d89ec533Spatrick   UnwindFast();
203d89ec533Spatrick 
204d89ec533Spatrick   char buf[3000];
205d89ec533Spatrick   uptr actual_len = trace.PrintTo(buf, sizeof(buf));
206d89ec533Spatrick   ASSERT_LT(actual_len, sizeof(buf));
207d89ec533Spatrick 
208d89ec533Spatrick   char tinybuf[10];
209d89ec533Spatrick   trace.PrintTo(tinybuf, sizeof(tinybuf));
210d89ec533Spatrick 
211d89ec533Spatrick   // This the the truncation case.
212d89ec533Spatrick   ASSERT_GT(actual_len, sizeof(tinybuf));
213d89ec533Spatrick 
214d89ec533Spatrick   // The truncated contents should be a prefix of the full contents.
215d89ec533Spatrick   size_t lastpos = sizeof(tinybuf) - 1;
216d89ec533Spatrick   EXPECT_EQ(strncmp(buf, tinybuf, lastpos), 0);
217d89ec533Spatrick   EXPECT_EQ(tinybuf[lastpos], '\0');
218d89ec533Spatrick 
219d89ec533Spatrick   // Full bufffer has more contents...
220d89ec533Spatrick   EXPECT_NE(buf[lastpos], '\0');
221d89ec533Spatrick }
222d89ec533Spatrick 
TEST_F(StackPrintTest,SKIP_ON_SPARC (WorksWithEmptyStack))223d89ec533Spatrick TEST_F(StackPrintTest, SKIP_ON_SPARC(WorksWithEmptyStack)) {
224d89ec533Spatrick   char buf[3000];
225d89ec533Spatrick   trace.PrintTo(buf, sizeof(buf));
226d89ec533Spatrick   EXPECT_NE(strstr(buf, "<empty stack>"), nullptr);
227d89ec533Spatrick }
228d89ec533Spatrick 
TEST_F(StackPrintTest,SKIP_ON_SPARC (ReturnsCorrectLength))229d89ec533Spatrick TEST_F(StackPrintTest, SKIP_ON_SPARC(ReturnsCorrectLength)) {
230d89ec533Spatrick   UnwindFast();
231d89ec533Spatrick 
232d89ec533Spatrick   char buf[3000];
233d89ec533Spatrick   uptr len = trace.PrintTo(buf, sizeof(buf));
234d89ec533Spatrick   size_t actual_len = strlen(buf);
235d89ec533Spatrick   ASSERT_LT(len, sizeof(buf));
236d89ec533Spatrick   EXPECT_EQ(len, actual_len);
237d89ec533Spatrick 
238d89ec533Spatrick   char tinybuf[5];
239d89ec533Spatrick   len = trace.PrintTo(tinybuf, sizeof(tinybuf));
240d89ec533Spatrick   size_t truncated_len = strlen(tinybuf);
241d89ec533Spatrick   ASSERT_GE(len, sizeof(tinybuf));
242d89ec533Spatrick   EXPECT_EQ(len, actual_len);
243d89ec533Spatrick   EXPECT_EQ(truncated_len, sizeof(tinybuf) - 1);
244d89ec533Spatrick }
245d89ec533Spatrick 
TEST_F(StackPrintTest,SKIP_ON_SPARC (AcceptsZeroSize))246d89ec533Spatrick TEST_F(StackPrintTest, SKIP_ON_SPARC(AcceptsZeroSize)) {
247d89ec533Spatrick   UnwindFast();
248d89ec533Spatrick   char buf[1];
249d89ec533Spatrick   EXPECT_GT(trace.PrintTo(buf, 0), 0u);
250d89ec533Spatrick }
251d89ec533Spatrick 
252d89ec533Spatrick using StackPrintDeathTest = StackPrintTest;
253d89ec533Spatrick 
TEST_F(StackPrintDeathTest,SKIP_ON_SPARC (RequiresNonNullBuffer))254d89ec533Spatrick TEST_F(StackPrintDeathTest, SKIP_ON_SPARC(RequiresNonNullBuffer)) {
255d89ec533Spatrick   UnwindFast();
256d89ec533Spatrick   EXPECT_DEATH(trace.PrintTo(NULL, 100), "");
257d89ec533Spatrick }
258d89ec533Spatrick 
2593cab2bb3Spatrick #endif // SANITIZER_CAN_FAST_UNWIND
2603cab2bb3Spatrick 
TEST(SlowUnwindTest,ShortStackTrace)2613cab2bb3Spatrick TEST(SlowUnwindTest, ShortStackTrace) {
2623cab2bb3Spatrick   BufferedStackTrace stack;
2633cab2bb3Spatrick   uptr pc = StackTrace::GetCurrentPc();
2643cab2bb3Spatrick   uptr bp = GET_CURRENT_FRAME();
2653cab2bb3Spatrick   stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/0);
2663cab2bb3Spatrick   EXPECT_EQ(0U, stack.size);
2673cab2bb3Spatrick   EXPECT_EQ(0U, stack.top_frame_bp);
2683cab2bb3Spatrick   stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/1);
2693cab2bb3Spatrick   EXPECT_EQ(1U, stack.size);
2703cab2bb3Spatrick   EXPECT_EQ(pc, stack.trace[0]);
2713cab2bb3Spatrick   EXPECT_EQ(bp, stack.top_frame_bp);
2723cab2bb3Spatrick }
2733cab2bb3Spatrick 
TEST(GetCurrentPc,Basic)274d89ec533Spatrick TEST(GetCurrentPc, Basic) {
275d89ec533Spatrick   // Test that PCs obtained via GET_CURRENT_PC()
276d89ec533Spatrick   // and StackTrace::GetCurrentPc() are all different
277d89ec533Spatrick   // and are close to the function start.
278d89ec533Spatrick   struct Local {
279d89ec533Spatrick     static NOINLINE void Test() {
280d89ec533Spatrick       const uptr pcs[] = {
281d89ec533Spatrick           (uptr)&Local::Test,
282d89ec533Spatrick           GET_CURRENT_PC(),
283d89ec533Spatrick           StackTrace::GetCurrentPc(),
284d89ec533Spatrick           StackTrace::GetCurrentPc(),
285d89ec533Spatrick       };
286d89ec533Spatrick       for (uptr i = 0; i < ARRAY_SIZE(pcs); i++)
287*810390e3Srobert         Printf("pc%zu: 0x%zx\n", i, pcs[i]);
288d89ec533Spatrick       for (uptr i = 1; i < ARRAY_SIZE(pcs); i++) {
289d89ec533Spatrick         EXPECT_GT(pcs[i], pcs[0]);
290d89ec533Spatrick         EXPECT_LT(pcs[i], pcs[0] + 1000);
291d89ec533Spatrick         for (uptr j = 0; j < i; j++) EXPECT_NE(pcs[i], pcs[j]);
292d89ec533Spatrick       }
293d89ec533Spatrick     }
294d89ec533Spatrick   };
295d89ec533Spatrick   Local::Test();
296d89ec533Spatrick }
297d89ec533Spatrick 
298d89ec533Spatrick // Dummy implementation. This should never be called, but is required to link
299d89ec533Spatrick // non-optimized builds of this test.
UnwindImpl(uptr pc,uptr bp,void * context,bool request_fast,u32 max_depth)300d89ec533Spatrick void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context,
301d89ec533Spatrick                                     bool request_fast, u32 max_depth) {
302d89ec533Spatrick   UNIMPLEMENTED();
303d89ec533Spatrick }
304d89ec533Spatrick 
3053cab2bb3Spatrick }  // namespace __sanitizer
306