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