1 // Copyright 2015 The Chromium Authors. All rights reserved.
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/win32_stack_frame_unwinder.h"
6 
7 #include <windows.h>
8 
9 #include <utility>
10 
11 #include "base/macros.h"
12 #include "base/memory/ptr_util.h"
13 
14 namespace base {
15 
16 // Win32UnwindFunctions -------------------------------------------------------
17 
18 const HMODULE ModuleHandleTraits::kNonNullModuleForTesting =
19     reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1));
20 
21 // static
CloseHandle(HMODULE handle)22 bool ModuleHandleTraits::CloseHandle(HMODULE handle) {
23   if (handle == kNonNullModuleForTesting)
24     return true;
25 
26   return ::FreeLibrary(handle) != 0;
27 }
28 
29 // static
IsHandleValid(HMODULE handle)30 bool ModuleHandleTraits::IsHandleValid(HMODULE handle) {
31   return handle != nullptr;
32 }
33 
34 // static
NullHandle()35 HMODULE ModuleHandleTraits::NullHandle() {
36   return nullptr;
37 }
38 
39 namespace {
40 
41 // Implements the UnwindFunctions interface for the corresponding Win32
42 // functions.
43 class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
44 public:
45   Win32UnwindFunctions();
46   ~Win32UnwindFunctions() override;
47 
48   PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
49                                         PDWORD64 image_base) override;
50 
51   void VirtualUnwind(DWORD64 image_base,
52                      DWORD64 program_counter,
53                      PRUNTIME_FUNCTION runtime_function,
54                      CONTEXT* context) override;
55 
56   ScopedModuleHandle GetModuleForProgramCounter(
57       DWORD64 program_counter) override;
58 
59 private:
60   DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions);
61 };
62 
Win32UnwindFunctions()63 Win32UnwindFunctions::Win32UnwindFunctions() {}
~Win32UnwindFunctions()64 Win32UnwindFunctions::~Win32UnwindFunctions() {}
65 
LookupFunctionEntry(DWORD64 program_counter,PDWORD64 image_base)66 PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry(
67     DWORD64 program_counter,
68     PDWORD64 image_base) {
69 #ifdef _WIN64
70   return ::RtlLookupFunctionEntry(program_counter, image_base, nullptr);
71 #else
72   NOTREACHED();
73   return nullptr;
74 #endif
75 }
76 
VirtualUnwind(DWORD64 image_base,DWORD64 program_counter,PRUNTIME_FUNCTION runtime_function,CONTEXT * context)77 void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base,
78                                          DWORD64 program_counter,
79                                          PRUNTIME_FUNCTION runtime_function,
80                                          CONTEXT* context) {
81 #ifdef _WIN64
82   void* handler_data;
83   ULONG64 establisher_frame;
84   KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
85   ::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter,
86                      runtime_function, context, &handler_data,
87                      &establisher_frame, &nvcontext);
88 #else
89   NOTREACHED();
90 #endif
91 }
92 
GetModuleForProgramCounter(DWORD64 program_counter)93 ScopedModuleHandle Win32UnwindFunctions::GetModuleForProgramCounter(
94     DWORD64 program_counter) {
95   HMODULE module_handle = nullptr;
96   // GetModuleHandleEx() increments the module reference count, which is then
97   // managed and ultimately decremented by ScopedModuleHandle.
98   if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
99                            reinterpret_cast<LPCTSTR>(program_counter),
100                            &module_handle)) {
101     const DWORD error = ::GetLastError();
102     DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error));
103   }
104   return ScopedModuleHandle(module_handle);
105 }
106 
107 }  // namespace
108 
109 // Win32StackFrameUnwinder ----------------------------------------------------
110 
~UnwindFunctions()111 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
UnwindFunctions()112 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
113 
Win32StackFrameUnwinder()114 Win32StackFrameUnwinder::Win32StackFrameUnwinder()
115     : Win32StackFrameUnwinder(WrapUnique(new Win32UnwindFunctions)) {}
116 
~Win32StackFrameUnwinder()117 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
118 
TryUnwind(CONTEXT * context,ScopedModuleHandle * module)119 bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context,
120                                         ScopedModuleHandle* module) {
121 #ifdef _WIN64
122   ScopedModuleHandle frame_module =
123       unwind_functions_->GetModuleForProgramCounter(context->Rip);
124   if (!frame_module.IsValid()) {
125     // There's no loaded module containing the instruction pointer. This can be
126     // due to executing code that is not in a module. In particular,
127     // runtime-generated code associated with third-party injected DLLs
128     // typically is not in a module. It can also be due to the the module having
129     // been unloaded since we recorded the stack.  In the latter case the
130     // function unwind information was part of the unloaded module, so it's not
131     // possible to unwind further.
132     //
133     // If a module was found, it's still theoretically possible for the detected
134     // module module to be different than the one that was loaded when the stack
135     // was copied (i.e. if the module was unloaded and a different module loaded
136     // in overlapping memory). This likely would cause a crash, but has not been
137     // observed in practice.
138     return false;
139   }
140 
141   ULONG64 image_base;
142   // Try to look up unwind metadata for the current function.
143   PRUNTIME_FUNCTION runtime_function =
144       unwind_functions_->LookupFunctionEntry(context->Rip, &image_base);
145 
146   if (runtime_function) {
147     unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function,
148                                      context);
149     at_top_frame_ = false;
150   } else {
151     if (at_top_frame_) {
152       at_top_frame_ = false;
153 
154       // This is a leaf function (i.e. a function that neither calls a function,
155       // nor allocates any stack space itself) so the return address is at RSP.
156       context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
157       context->Rsp += 8;
158     } else {
159       // In theory we shouldn't get here, as it means we've encountered a
160       // function without unwind information below the top of the stack, which
161       // is forbidden by the Microsoft x64 calling convention.
162       //
163       // The one known case in Chrome code that executes this path occurs
164       // because of BoringSSL unwind information inconsistent with the actual
165       // function code. See https://crbug.com/542919.
166       //
167       // Note that dodgy third-party generated code that otherwise would enter
168       // this path should be caught by the module check above, since the code
169       // typically is located outside of a module.
170       return false;
171     }
172   }
173 
174   module->Set(frame_module.Take());
175   return true;
176 #else
177   NOTREACHED();
178   return false;
179 #endif
180 }
181 
Win32StackFrameUnwinder(std::unique_ptr<UnwindFunctions> unwind_functions)182 Win32StackFrameUnwinder::Win32StackFrameUnwinder(
183     std::unique_ptr<UnwindFunctions> unwind_functions)
184     : at_top_frame_(true), unwind_functions_(std::move(unwind_functions)) {}
185 
186 }  // namespace base
187