1//===-- sanitizer_stacktrace_test.cc --------------------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
11//
12//===----------------------------------------------------------------------===//
13
14#include "sanitizer_common/sanitizer_common.h"
15#include "sanitizer_common/sanitizer_stacktrace.h"
16#include "gtest/gtest.h"
17
18namespace __sanitizer {
19
20class FastUnwindTest : public ::testing::Test {
21 protected:
22  virtual void SetUp();
23  virtual void TearDown();
24  bool TryFastUnwind(uptr max_depth) {
25    if (!StackTrace::WillUseFastUnwind(true))
26      return false;
27    trace.Unwind(max_depth, start_pc, (uptr)&fake_stack[0], 0, fake_top,
28                 fake_bottom, true);
29    return true;
30  }
31
32  void *mapping;
33  uhwptr *fake_stack;
34  const uptr fake_stack_size = 10;
35  uhwptr start_pc;
36  uhwptr fake_top;
37  uhwptr fake_bottom;
38  BufferedStackTrace trace;
39};
40
41static uptr PC(uptr idx) {
42  return (1<<20) + idx;
43}
44
45void FastUnwindTest::SetUp() {
46  size_t ps = GetPageSize();
47  mapping = MmapOrDie(2 * ps, "FastUnwindTest");
48  MprotectNoAccess((uptr)mapping, ps);
49
50  // Unwinder may peek 1 word down from the starting FP.
51  fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr));
52
53  // Fill an array of pointers with fake fp+retaddr pairs.  Frame pointers have
54  // even indices.
55  for (uptr i = 0; i + 1 < fake_stack_size; i += 2) {
56    fake_stack[i] = (uptr)&fake_stack[i+2];  // fp
57    fake_stack[i+1] = PC(i + 1); // retaddr
58  }
59  // Mark the last fp point back up to terminate the stack trace.
60  fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0];
61
62  // Top is two slots past the end because FastUnwindStack subtracts two.
63  fake_top = (uhwptr)&fake_stack[fake_stack_size + 2];
64  // Bottom is one slot before the start because FastUnwindStack uses >.
65  fake_bottom = (uhwptr)mapping;
66  start_pc = PC(0);
67}
68
69void FastUnwindTest::TearDown() {
70  size_t ps = GetPageSize();
71  UnmapOrDie(mapping, 2 * ps);
72}
73
74TEST_F(FastUnwindTest, Basic) {
75  if (!TryFastUnwind(kStackTraceMax))
76    return;
77  // Should get all on-stack retaddrs and start_pc.
78  EXPECT_EQ(6U, trace.size);
79  EXPECT_EQ(start_pc, trace.trace[0]);
80  for (uptr i = 1; i <= 5; i++) {
81    EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
82  }
83}
84
85// From: https://github.com/google/sanitizers/issues/162
86TEST_F(FastUnwindTest, FramePointerLoop) {
87  // Make one fp point to itself.
88  fake_stack[4] = (uhwptr)&fake_stack[4];
89  if (!TryFastUnwind(kStackTraceMax))
90    return;
91  // Should get all on-stack retaddrs up to the 4th slot and start_pc.
92  EXPECT_EQ(4U, trace.size);
93  EXPECT_EQ(start_pc, trace.trace[0]);
94  for (uptr i = 1; i <= 3; i++) {
95    EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
96  }
97}
98
99TEST_F(FastUnwindTest, MisalignedFramePointer) {
100  // Make one fp misaligned.
101  fake_stack[4] += 3;
102  if (!TryFastUnwind(kStackTraceMax))
103    return;
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
112TEST_F(FastUnwindTest, OneFrameStackTrace) {
113  if (!TryFastUnwind(1))
114    return;
115  EXPECT_EQ(1U, trace.size);
116  EXPECT_EQ(start_pc, trace.trace[0]);
117  EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp);
118}
119
120TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
121  if (!TryFastUnwind(0))
122    return;
123  EXPECT_EQ(0U, trace.size);
124  EXPECT_EQ(0U, trace.top_frame_bp);
125}
126
127TEST_F(FastUnwindTest, FPBelowPrevFP) {
128  // The next FP points to unreadable memory inside the stack limits, but below
129  // current FP.
130  fake_stack[0] = (uhwptr)&fake_stack[-50];
131  fake_stack[1] = PC(1);
132  if (!TryFastUnwind(3))
133    return;
134  EXPECT_EQ(2U, trace.size);
135  EXPECT_EQ(PC(0), trace.trace[0]);
136  EXPECT_EQ(PC(1), trace.trace[1]);
137}
138
139TEST_F(FastUnwindTest, CloseToZeroFrame) {
140  // Make one pc a NULL pointer.
141  fake_stack[5] = 0x0;
142  if (!TryFastUnwind(kStackTraceMax))
143    return;
144  // The stack should be truncated at the NULL pointer (and not include it).
145  EXPECT_EQ(3U, trace.size);
146  EXPECT_EQ(start_pc, trace.trace[0]);
147  for (uptr i = 1; i < 3U; i++) {
148    EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
149  }
150}
151
152TEST(SlowUnwindTest, ShortStackTrace) {
153  if (StackTrace::WillUseFastUnwind(false))
154    return;
155  BufferedStackTrace stack;
156  uptr pc = StackTrace::GetCurrentPc();
157  uptr bp = GET_CURRENT_FRAME();
158  stack.Unwind(0, pc, bp, 0, 0, 0, false);
159  EXPECT_EQ(0U, stack.size);
160  EXPECT_EQ(0U, stack.top_frame_bp);
161  stack.Unwind(1, pc, bp, 0, 0, 0, false);
162  EXPECT_EQ(1U, stack.size);
163  EXPECT_EQ(pc, stack.trace[0]);
164  EXPECT_EQ(bp, stack.top_frame_bp);
165}
166
167}  // namespace __sanitizer
168