• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/profiler/frame_pointer_unwinder.h"
11 
12 #include "base/check_op.h"
13 #include "base/compiler_specific.h"
14 #include "base/notreached.h"
15 #include "base/numerics/clamped_math.h"
16 #include "base/profiler/module_cache.h"
17 #include "build/build_config.h"
18 
19 #if BUILDFLAG(IS_APPLE)
20 #include <pthread/stack_np.h>
21 #endif
22 
23 namespace {
24 
25 // Given a frame pointer, returns the frame pointer of the calling stack
26 // frame and places the return address of the calling stack frame into
27 // `return_address`. Shim around `pthread_stack_frame_decode_np` where
28 // available since it handles pointer authentication on supported platforms.
29 // NB: The caller *must* ensure that there are 2+ uintptr_t's worth of memory at
30 // `frame_pointer`.
DecodeFrame(uintptr_t frame_pointer,uintptr_t * return_address)31 uintptr_t DecodeFrame(uintptr_t frame_pointer, uintptr_t* return_address) {
32 #if BUILDFLAG(IS_APPLE)
33   if (__builtin_available(iOS 12, *)) {
34     return pthread_stack_frame_decode_np(frame_pointer, return_address);
35   }
36 #endif
37   const uintptr_t* fp = reinterpret_cast<uintptr_t*>(frame_pointer);
38 
39   // MSAN does not consider the frame pointers and return addresses to have
40   // have been initialized in the normal sense, but they are actually
41   // initialized.
42   MSAN_UNPOISON(fp, sizeof(uintptr_t) * 2);
43 
44   uintptr_t next_frame = *fp;
45   *return_address = *(fp + 1);
46   return next_frame;
47 }
48 
49 }  // namespace
50 
51 namespace base {
52 
FramePointerUnwinder(CanUnwindFromDelegate can_unwind_from_delegate)53 FramePointerUnwinder::FramePointerUnwinder(
54     CanUnwindFromDelegate can_unwind_from_delegate)
55     : can_unwind_from_delegate_(can_unwind_from_delegate) {}
56 
57 FramePointerUnwinder::~FramePointerUnwinder() = default;
58 
CanUnwindFrom(const Frame & current_frame) const59 bool FramePointerUnwinder::CanUnwindFrom(const Frame& current_frame) const {
60   if (can_unwind_from_delegate_) {
61     return can_unwind_from_delegate_.Run(current_frame);
62   }
63 
64   return current_frame.module && current_frame.module->IsNative();
65 }
66 
TryUnwind(UnwinderStateCapture * capture_state,RegisterContext * thread_context,uintptr_t stack_top,std::vector<Frame> * stack)67 UnwindResult FramePointerUnwinder::TryUnwind(
68     UnwinderStateCapture* capture_state,
69     RegisterContext* thread_context,
70     uintptr_t stack_top,
71     std::vector<Frame>* stack) {
72   // We expect the frame corresponding to the |thread_context| register state to
73   // exist within |stack|.
74   DCHECK_GT(stack->size(), 0u);
75 #if defined(ARCH_CPU_ARM64)
76   constexpr uintptr_t align_mask = 0x1;
77 #elif defined(ARCH_CPU_X86_64)
78   constexpr uintptr_t align_mask = 0xf;
79 #endif
80 
81   uintptr_t next_frame = RegisterContextFramePointer(thread_context);
82   uintptr_t frame_lower_bound = RegisterContextStackPointer(thread_context);
83   const auto is_fp_valid = [&](uintptr_t fp) {
84     // Ensure there's space on the stack to read two values: the caller's
85     // frame pointer and the return address.
86     return next_frame >= frame_lower_bound &&
87            ClampAdd(next_frame, sizeof(uintptr_t) * 2) <= stack_top &&
88            (next_frame & align_mask) == 0;
89   };
90   if (!is_fp_valid(next_frame))
91     return UnwindResult::kAborted;
92 
93   for (;;) {
94     if (!stack->back().module) {
95       return UnwindResult::kAborted;
96     }
97     if (!stack->back().module->IsNative()) {
98       // This is a non-native module associated with the auxiliary unwinder
99       // (e.g. corresponding to a frame in V8 generated code). Report as
100       // UNRECOGNIZED_FRAME to allow that unwinder to unwind the frame.
101       return UnwindResult::kUnrecognizedFrame;
102     }
103     uintptr_t retaddr;
104     uintptr_t frame = next_frame;
105     next_frame = DecodeFrame(frame, &retaddr);
106     frame_lower_bound = frame + 1;
107     // If `next_frame` is 0, we've hit the root and `retaddr` isn't useful.
108     // Bail without recording the frame.
109     if (next_frame == 0)
110       return UnwindResult::kCompleted;
111     const ModuleCache::Module* module =
112         module_cache()->GetModuleForAddress(retaddr);
113     // V8 doesn't conform to the x86_64 ABI re: stack alignment. For V8 frames,
114     // let the V8 unwinder determine whether the FP is valid or not.
115     bool is_non_native_module = module && !module->IsNative();
116     // If the FP doesn't look correct, don't record this frame.
117     if (!is_non_native_module && !is_fp_valid(next_frame))
118       return UnwindResult::kAborted;
119 
120     RegisterContextFramePointer(thread_context) = next_frame;
121     RegisterContextInstructionPointer(thread_context) = retaddr;
122     RegisterContextStackPointer(thread_context) = frame + sizeof(uintptr_t) * 2;
123     stack->emplace_back(retaddr, module);
124   }
125 
126   NOTREACHED();
127 }
128 
129 }  // namespace base
130