Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Side by Side Diff: profiler/win32_stack_frame_unwinder.cc

Issue 2043183002: Update to Chromium //base at Chromium commit 01cb97b2e09618bbc3a60c7348f0a844eea20547. (Closed) Base URL: https://github.com/domokit/base.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « profiler/win32_stack_frame_unwinder.h ('k') | profiler/win32_stack_frame_unwinder_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "base/containers/hash_tables.h"
8 #include "base/memory/singleton.h"
9 #include "base/stl_util.h"
10
11 namespace base {
12
13 // LeafUnwindBlacklist --------------------------------------------------------
14
15 namespace {
16
17 // Records modules that are known to have functions that violate the Microsoft
18 // x64 calling convention and would be dangerous to manually unwind if
19 // encountered as the last frame on the call stack. Functions like these have
20 // been observed in injected third party modules that either do not provide
21 // function unwind information, or do not provide the required function prologue
22 // and epilogue. The former case was observed in several AV products and the
23 // latter in a WndProc function associated with Actual Window
24 // Manager/aimemb64.dll. See https://crbug.com/476422.
25 class LeafUnwindBlacklist {
26 public:
27 static LeafUnwindBlacklist* GetInstance();
28
29 // This function does not allocate memory and is safe to call between
30 // SuspendThread and ResumeThread.
31 bool IsBlacklisted(const void* module) const;
32
33 // Allocates memory. Must be invoked only after ResumeThread, otherwise we
34 // risk deadlocking on a heap lock held by a suspended thread.
35 void AddModuleToBlacklist(const void* module);
36
37 private:
38 friend struct DefaultSingletonTraits<LeafUnwindBlacklist>;
39
40 LeafUnwindBlacklist();
41 ~LeafUnwindBlacklist();
42
43 // The set of modules known to have functions that violate the Microsoft x64
44 // calling convention.
45 base::hash_set<const void*> blacklisted_modules_;
46
47 DISALLOW_COPY_AND_ASSIGN(LeafUnwindBlacklist);
48 };
49
50 // static
51 LeafUnwindBlacklist* LeafUnwindBlacklist::GetInstance() {
52 // Leaky for shutdown performance.
53 return Singleton<LeafUnwindBlacklist,
54 LeakySingletonTraits<LeafUnwindBlacklist>>::get();
55 }
56
57 bool LeafUnwindBlacklist::IsBlacklisted(const void* module) const {
58 return ContainsKey(blacklisted_modules_, module);
59 }
60
61 void LeafUnwindBlacklist::AddModuleToBlacklist(const void* module) {
62 CHECK(module);
63 blacklisted_modules_.insert(module);
64 }
65
66 LeafUnwindBlacklist::LeafUnwindBlacklist() {}
67 LeafUnwindBlacklist::~LeafUnwindBlacklist() {}
68
69 } // namespace
70
71 // Win32StackFrameUnwinder ----------------------------------------------------
72
73 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
74 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
75
76 Win32StackFrameUnwinder::Win32UnwindFunctions::Win32UnwindFunctions() {}
77
78 PRUNTIME_FUNCTION
79 Win32StackFrameUnwinder::Win32UnwindFunctions::LookupFunctionEntry(
80 DWORD64 program_counter,
81 PDWORD64 image_base) {
82 #ifdef _WIN64
83 return RtlLookupFunctionEntry(program_counter, image_base, nullptr);
84 #else
85 NOTREACHED();
86 return nullptr;
87 #endif
88 }
89
90 void Win32StackFrameUnwinder::Win32UnwindFunctions::VirtualUnwind(
91 DWORD64 image_base,
92 DWORD64 program_counter,
93 PRUNTIME_FUNCTION runtime_function,
94 CONTEXT* context) {
95 #ifdef _WIN64
96 void* handler_data;
97 ULONG64 establisher_frame;
98 KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
99 RtlVirtualUnwind(0, image_base, program_counter, runtime_function, context,
100 &handler_data, &establisher_frame, &nvcontext);
101 #else
102 NOTREACHED();
103 #endif
104 }
105
106 Win32StackFrameUnwinder::Win32StackFrameUnwinder()
107 : Win32StackFrameUnwinder(&win32_unwind_functions_) {}
108
109 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {
110 if (pending_blacklisted_module_) {
111 LeafUnwindBlacklist::GetInstance()->AddModuleToBlacklist(
112 pending_blacklisted_module_);
113 }
114 }
115
116 bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context) {
117 #ifdef _WIN64
118 CHECK(!at_top_frame_ || unwind_info_present_for_all_frames_);
119 CHECK(!pending_blacklisted_module_);
120
121 ULONG64 image_base;
122 // Try to look up unwind metadata for the current function.
123 PRUNTIME_FUNCTION runtime_function =
124 unwind_functions_->LookupFunctionEntry(context->Rip, &image_base);
125
126 if (runtime_function) {
127 unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function,
128 context);
129 at_top_frame_ = false;
130 } else {
131 // RtlLookupFunctionEntry didn't find unwind information. This could mean
132 // the code at the instruction pointer is in:
133 //
134 // 1. a true leaf function (i.e. a function that neither calls a function,
135 // nor allocates any stack space itself) in which case the return
136 // address is at RSP, or
137 //
138 // 2. a function that doesn't adhere to the Microsoft x64 calling
139 // convention, either by not providing the required unwind information,
140 // or by not having the prologue or epilogue required for unwinding;
141 // this case has been observed in crash data in injected third party
142 // DLLs.
143 //
144 // In valid code, case 1 can only occur (by definition) as the last frame
145 // on the stack. This happens in about 5% of observed stacks and can
146 // easily be unwound by popping RSP and using it as the next frame's
147 // instruction pointer.
148 //
149 // Case 2 can occur anywhere on the stack, and attempting to unwind the
150 // stack will result in treating whatever value happens to be on the stack
151 // at RSP as the next frame's instruction pointer. This is certainly wrong
152 // and very likely to lead to crashing by deferencing invalid pointers in
153 // the next RtlVirtualUnwind call.
154 //
155 // If we see case 2 at a location not the last frame, and all the previous
156 // frame had valid unwind information, then this is definitely bad code.
157 // We blacklist the module as untrustable for unwinding if we encounter a
158 // function in it that doesn't have unwind information.
159
160 if (at_top_frame_) {
161 at_top_frame_ = false;
162
163 // We are at the end of the stack. It's very likely that we're in case 1
164 // since the vast majority of code adheres to the Microsoft x64 calling
165 // convention. But there's a small chance we might be unlucky and be in
166 // case 2. If this module is known to have bad code according to the
167 // leaf unwind blacklist, stop here, otherwise manually unwind.
168 if (LeafUnwindBlacklist::GetInstance()->IsBlacklisted(
169 reinterpret_cast<const void*>(image_base))) {
170 return false;
171 }
172
173 context->Rip = context->Rsp;
174 context->Rsp += 8;
175 unwind_info_present_for_all_frames_ = false;
176 } else {
177 // We're not at the end of the stack. This frame is untrustworthy and we
178 // can't safely unwind from here.
179 if (unwind_info_present_for_all_frames_) {
180 // Unwind information was present for all previous frames, so we can
181 // be confident this is case 2. Record the module to be blacklisted.
182 pending_blacklisted_module_ = reinterpret_cast<const void*>(image_base);
183 } else {
184 // We started off on a function without unwind information. It's very
185 // likely that all frames up to this point have been good, and this
186 // frame is case 2. But it's possible that the initial frame was case
187 // 2 but hadn't been blacklisted yet, and we've started to go off into
188 // the weeds. Since we can't be sure, just bail out without
189 // blacklisting the module; chances are we'll later encounter the same
190 // function on a stack with full unwind information.
191 }
192 return false;
193 }
194 }
195
196 return true;
197 #else
198 NOTREACHED();
199 return false;
200 #endif
201 }
202
203 Win32StackFrameUnwinder::Win32StackFrameUnwinder(
204 UnwindFunctions* unwind_functions)
205 : at_top_frame_(true),
206 unwind_info_present_for_all_frames_(true),
207 pending_blacklisted_module_(nullptr),
208 unwind_functions_(unwind_functions) {}
209
210 } // namespace base
OLDNEW
« no previous file with comments | « profiler/win32_stack_frame_unwinder.h ('k') | profiler/win32_stack_frame_unwinder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698