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