| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/profiler/win32_stack_frame_unwinder.h" | 5 #include "base/profiler/win32_stack_frame_unwinder.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/containers/hash_tables.h" | |
| 11 #include "base/memory/singleton.h" | |
| 12 #include "base/stl_util.h" | |
| 13 | |
| 14 namespace base { | 10 namespace base { |
| 15 | 11 |
| 16 // Win32UnwindFunctions ------------------------------------------------------- | 12 // Win32UnwindFunctions ------------------------------------------------------- |
| 17 | 13 |
| 18 const HMODULE ModuleHandleTraits::kNonNullModuleForTesting = | 14 const HMODULE ModuleHandleTraits::kNonNullModuleForTesting = |
| 19 reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1)); | 15 reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1)); |
| 20 | 16 |
| 21 // static | 17 // static |
| 22 bool ModuleHandleTraits::CloseHandle(HMODULE handle) { | 18 bool ModuleHandleTraits::CloseHandle(HMODULE handle) { |
| 23 if (handle == kNonNullModuleForTesting) | 19 if (handle == kNonNullModuleForTesting) |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 // managed and ultimately decremented by ScopedModuleHandle. | 93 // managed and ultimately decremented by ScopedModuleHandle. |
| 98 if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 94 if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
| 99 reinterpret_cast<LPCTSTR>(program_counter), | 95 reinterpret_cast<LPCTSTR>(program_counter), |
| 100 &module_handle)) { | 96 &module_handle)) { |
| 101 const DWORD error = ::GetLastError(); | 97 const DWORD error = ::GetLastError(); |
| 102 DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error)); | 98 DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error)); |
| 103 } | 99 } |
| 104 return ScopedModuleHandle(module_handle); | 100 return ScopedModuleHandle(module_handle); |
| 105 } | 101 } |
| 106 | 102 |
| 107 // LeafUnwindBlacklist -------------------------------------------------------- | |
| 108 | |
| 109 // Records modules that are known to have functions that violate the Microsoft | |
| 110 // x64 calling convention and would be dangerous to manually unwind if | |
| 111 // encountered as the last frame on the call stack. Functions like these have | |
| 112 // been observed in injected third party modules that either do not provide | |
| 113 // function unwind information, or do not provide the required function prologue | |
| 114 // and epilogue. The former case was observed in several AV products and the | |
| 115 // latter in a WndProc function associated with Actual Window | |
| 116 // Manager/aimemb64.dll. See https://crbug.com/476422. | |
| 117 class LeafUnwindBlacklist { | |
| 118 public: | |
| 119 static LeafUnwindBlacklist* GetInstance(); | |
| 120 | |
| 121 // Returns true if |module| has been blacklisted. | |
| 122 bool IsBlacklisted(const void* module) const; | |
| 123 | |
| 124 // Records |module| for blacklisting. | |
| 125 void BlacklistModule(const void* module); | |
| 126 | |
| 127 private: | |
| 128 friend struct DefaultSingletonTraits<LeafUnwindBlacklist>; | |
| 129 | |
| 130 LeafUnwindBlacklist(); | |
| 131 ~LeafUnwindBlacklist(); | |
| 132 | |
| 133 // The set of modules known to have functions that violate the Microsoft x64 | |
| 134 // calling convention. | |
| 135 base::hash_set<const void*> blacklisted_modules_; | |
| 136 | |
| 137 DISALLOW_COPY_AND_ASSIGN(LeafUnwindBlacklist); | |
| 138 }; | |
| 139 | |
| 140 // static | |
| 141 LeafUnwindBlacklist* LeafUnwindBlacklist::GetInstance() { | |
| 142 // Leaky for performance reasons. | |
| 143 return Singleton<LeafUnwindBlacklist, | |
| 144 LeakySingletonTraits<LeafUnwindBlacklist>>::get(); | |
| 145 } | |
| 146 | |
| 147 bool LeafUnwindBlacklist::IsBlacklisted(const void* module) const { | |
| 148 return ContainsKey(blacklisted_modules_, module); | |
| 149 } | |
| 150 | |
| 151 void LeafUnwindBlacklist::BlacklistModule(const void* module) { | |
| 152 CHECK(module); | |
| 153 blacklisted_modules_.insert(module); | |
| 154 } | |
| 155 | |
| 156 LeafUnwindBlacklist::LeafUnwindBlacklist() {} | |
| 157 LeafUnwindBlacklist::~LeafUnwindBlacklist() {} | |
| 158 | |
| 159 } // namespace | 103 } // namespace |
| 160 | 104 |
| 161 // Win32StackFrameUnwinder ---------------------------------------------------- | 105 // Win32StackFrameUnwinder ---------------------------------------------------- |
| 162 | 106 |
| 163 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {} | 107 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {} |
| 164 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {} | 108 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {} |
| 165 | 109 |
| 166 Win32StackFrameUnwinder::Win32StackFrameUnwinder() | 110 Win32StackFrameUnwinder::Win32StackFrameUnwinder() |
| 167 : Win32StackFrameUnwinder(make_scoped_ptr(new Win32UnwindFunctions)) { | 111 : Win32StackFrameUnwinder(make_scoped_ptr(new Win32UnwindFunctions)) { |
| 168 } | 112 } |
| 169 | 113 |
| 170 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {} | 114 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {} |
| 171 | 115 |
| 172 bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context, | 116 bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context, |
| 173 ScopedModuleHandle* module) { | 117 ScopedModuleHandle* module) { |
| 174 #ifdef _WIN64 | 118 #ifdef _WIN64 |
| 175 CHECK(!at_top_frame_ || unwind_info_present_for_all_frames_); | |
| 176 | |
| 177 ScopedModuleHandle frame_module = | 119 ScopedModuleHandle frame_module = |
| 178 unwind_functions_->GetModuleForProgramCounter(context->Rip); | 120 unwind_functions_->GetModuleForProgramCounter(context->Rip); |
| 179 if (!frame_module.IsValid()) { | 121 if (!frame_module.IsValid()) { |
| 180 // There's no loaded module containing the instruction pointer. This can be | 122 // There's no loaded module containing the instruction pointer. This can be |
| 181 // due to executing code that is not in a module. In particular, | 123 // due to executing code that is not in a module. In particular, |
| 182 // runtime-generated code associated with third-party injected DLLs | 124 // runtime-generated code associated with third-party injected DLLs |
| 183 // typically is not in a module. It can also be due to the the module having | 125 // typically is not in a module. It can also be due to the the module having |
| 184 // been unloaded since we recorded the stack. In the latter case the | 126 // been unloaded since we recorded the stack. In the latter case the |
| 185 // function unwind information was part of the unloaded module, so it's not | 127 // function unwind information was part of the unloaded module, so it's not |
| 186 // possible to unwind further. | 128 // possible to unwind further. |
| 187 // | 129 // |
| 188 // If a module was found, it's still theoretically possible for the detected | 130 // If a module was found, it's still theoretically possible for the detected |
| 189 // module module to be different than the one that was loaded when the stack | 131 // module module to be different than the one that was loaded when the stack |
| 190 // was copied (i.e. if the module was unloaded and a different module loaded | 132 // was copied (i.e. if the module was unloaded and a different module loaded |
| 191 // in overlapping memory). This likely would cause a crash, but has not been | 133 // in overlapping memory). This likely would cause a crash, but has not been |
| 192 // observed in practice. | 134 // observed in practice. |
| 193 return false; | 135 return false; |
| 194 } | 136 } |
| 195 | 137 |
| 196 ULONG64 image_base; | 138 ULONG64 image_base; |
| 197 // Try to look up unwind metadata for the current function. | 139 // Try to look up unwind metadata for the current function. |
| 198 PRUNTIME_FUNCTION runtime_function = | 140 PRUNTIME_FUNCTION runtime_function = |
| 199 unwind_functions_->LookupFunctionEntry(context->Rip, &image_base); | 141 unwind_functions_->LookupFunctionEntry(context->Rip, &image_base); |
| 200 | 142 |
| 201 if (runtime_function) { | 143 if (runtime_function) { |
| 202 unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function, | 144 unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function, |
| 203 context); | 145 context); |
| 204 at_top_frame_ = false; | 146 at_top_frame_ = false; |
| 205 } else { | 147 } else { |
| 206 // RtlLookupFunctionEntry didn't find unwind information. This could mean | |
| 207 // the code at the instruction pointer is in: | |
| 208 // | |
| 209 // 1. a true leaf function (i.e. a function that neither calls a function, | |
| 210 // nor allocates any stack space itself) in which case the return | |
| 211 // address is at RSP, or | |
| 212 // | |
| 213 // 2. a function that doesn't adhere to the Microsoft x64 calling | |
| 214 // convention, either by not providing the required unwind information, | |
| 215 // or by not having the prologue or epilogue required for unwinding; | |
| 216 // this case has been observed in crash data in injected third party | |
| 217 // DLLs. | |
| 218 // | |
| 219 // In valid code, case 1 can only occur (by definition) as the last frame | |
| 220 // on the stack. This happens in about 5% of observed stacks and can | |
| 221 // easily be unwound by popping RSP and using it as the next frame's | |
| 222 // instruction pointer. | |
| 223 // | |
| 224 // Case 2 can occur anywhere on the stack, and attempting to unwind the | |
| 225 // stack will result in treating whatever value happens to be on the stack | |
| 226 // at RSP as the next frame's instruction pointer. This is certainly wrong | |
| 227 // and very likely to lead to crashing by deferencing invalid pointers in | |
| 228 // the next RtlVirtualUnwind call. | |
| 229 // | |
| 230 // If we see case 2 at a location not the last frame, and all the previous | |
| 231 // frame had valid unwind information, then this is definitely bad code. | |
| 232 // We blacklist the module as untrustable for unwinding if we encounter a | |
| 233 // function in it that doesn't have unwind information. | |
| 234 | |
| 235 if (at_top_frame_) { | 148 if (at_top_frame_) { |
| 236 at_top_frame_ = false; | 149 at_top_frame_ = false; |
| 237 | 150 |
| 238 // We are at the end of the stack. It's very likely that we're in case 1 | 151 // This is a leaf function (i.e. a function that neither calls a function, |
| 239 // since the vast majority of code adheres to the Microsoft x64 calling | 152 // nor allocates any stack space itself) so the return address is at RSP. |
| 240 // convention. But there's a small chance we might be unlucky and be in | 153 context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp); |
| 241 // case 2. If this module is known to have bad code according to the | |
| 242 // leaf unwind blacklist, stop here, otherwise manually unwind. | |
| 243 if (LeafUnwindBlacklist::GetInstance()->IsBlacklisted( | |
| 244 reinterpret_cast<const void*>(image_base))) { | |
| 245 return false; | |
| 246 } | |
| 247 | |
| 248 context->Rip = context->Rsp; | |
| 249 context->Rsp += 8; | 154 context->Rsp += 8; |
| 250 unwind_info_present_for_all_frames_ = false; | |
| 251 } else { | 155 } else { |
| 252 // We're not at the end of the stack. This frame is untrustworthy and we | 156 // In theory we shouldn't get here, as it means we've encountered a |
| 253 // can't safely unwind from here. | 157 // function without unwind information below the top of the stack, which |
| 254 if (!image_base) { | 158 // is forbidden by the Microsoft x64 calling convention. |
| 255 // A null image_base means that the the last unwind produced an invalid | 159 // |
| 256 // instruction pointer. This has been observed where unwind information | 160 // The one known case in Chrome code that executes this path occurs |
| 257 // was present for a function but was inconsistent with the actual | 161 // because of BoringSSL unwind information inconsistent with the actual |
| 258 // function code, in particular in BoringSSL. See | 162 // function code. See https://crbug.com/542919. |
| 259 // https://crbug.com/542919. | 163 // |
| 260 } else if (unwind_info_present_for_all_frames_) { | 164 // Note that dodgy third-party generated code that otherwise would enter |
| 261 // Unwind information was present for all previous frames, so we can | 165 // this path should be caught by the module check above, since the code |
| 262 // be confident this is case 2. Record the module to be blacklisted. | 166 // typically is located outside of a module. |
| 263 LeafUnwindBlacklist::GetInstance()->BlacklistModule( | |
| 264 reinterpret_cast<const void *>(image_base)); | |
| 265 } else { | |
| 266 // We started off on a function without unwind information. It's very | |
| 267 // likely that all frames up to this point have been good, and this | |
| 268 // frame is case 2. But it's possible that the initial frame was case | |
| 269 // 2 but hadn't been blacklisted yet, and we've started to go off into | |
| 270 // the weeds. Since we can't be sure, just bail out without | |
| 271 // blacklisting the module; chances are we'll later encounter the same | |
| 272 // function on a stack with full unwind information. | |
| 273 } | |
| 274 return false; | 167 return false; |
| 275 } | 168 } |
| 276 } | 169 } |
| 277 | 170 |
| 278 module->Set(frame_module.Take()); | 171 module->Set(frame_module.Take()); |
| 279 return true; | 172 return true; |
| 280 #else | 173 #else |
| 281 NOTREACHED(); | 174 NOTREACHED(); |
| 282 return false; | 175 return false; |
| 283 #endif | 176 #endif |
| 284 } | 177 } |
| 285 | 178 |
| 286 Win32StackFrameUnwinder::Win32StackFrameUnwinder( | 179 Win32StackFrameUnwinder::Win32StackFrameUnwinder( |
| 287 scoped_ptr<UnwindFunctions> unwind_functions) | 180 scoped_ptr<UnwindFunctions> unwind_functions) |
| 288 : at_top_frame_(true), | 181 : at_top_frame_(true), |
| 289 unwind_info_present_for_all_frames_(true), | |
| 290 unwind_functions_(std::move(unwind_functions)) {} | 182 unwind_functions_(std::move(unwind_functions)) {} |
| 291 | 183 |
| 292 } // namespace base | 184 } // namespace base |
| OLD | NEW |