Index: base/profiler/win32_stack_frame_unwinder.cc |
diff --git a/base/profiler/win32_stack_frame_unwinder.cc b/base/profiler/win32_stack_frame_unwinder.cc |
index 74075cbec5f0e0bfb41d692e621b5382b2b81659..0e7f5b252e294280ff77819f78760ce597b396af 100644 |
--- a/base/profiler/win32_stack_frame_unwinder.cc |
+++ b/base/profiler/win32_stack_frame_unwinder.cc |
@@ -7,10 +7,6 @@ |
#include <windows.h> |
#include <utility> |
-#include "base/containers/hash_tables.h" |
-#include "base/memory/singleton.h" |
-#include "base/stl_util.h" |
- |
namespace base { |
// Win32UnwindFunctions ------------------------------------------------------- |
@@ -104,58 +100,6 @@ ScopedModuleHandle Win32UnwindFunctions::GetModuleForProgramCounter( |
return ScopedModuleHandle(module_handle); |
} |
-// LeafUnwindBlacklist -------------------------------------------------------- |
- |
-// Records modules that are known to have functions that violate the Microsoft |
-// x64 calling convention and would be dangerous to manually unwind if |
-// encountered as the last frame on the call stack. Functions like these have |
-// been observed in injected third party modules that either do not provide |
-// function unwind information, or do not provide the required function prologue |
-// and epilogue. The former case was observed in several AV products and the |
-// latter in a WndProc function associated with Actual Window |
-// Manager/aimemb64.dll. See https://crbug.com/476422. |
-class LeafUnwindBlacklist { |
- public: |
- static LeafUnwindBlacklist* GetInstance(); |
- |
- // Returns true if |module| has been blacklisted. |
- bool IsBlacklisted(const void* module) const; |
- |
- // Records |module| for blacklisting. |
- void BlacklistModule(const void* module); |
- |
- private: |
- friend struct DefaultSingletonTraits<LeafUnwindBlacklist>; |
- |
- LeafUnwindBlacklist(); |
- ~LeafUnwindBlacklist(); |
- |
- // The set of modules known to have functions that violate the Microsoft x64 |
- // calling convention. |
- base::hash_set<const void*> blacklisted_modules_; |
- |
- DISALLOW_COPY_AND_ASSIGN(LeafUnwindBlacklist); |
-}; |
- |
-// static |
-LeafUnwindBlacklist* LeafUnwindBlacklist::GetInstance() { |
- // Leaky for performance reasons. |
- return Singleton<LeafUnwindBlacklist, |
- LeakySingletonTraits<LeafUnwindBlacklist>>::get(); |
-} |
- |
-bool LeafUnwindBlacklist::IsBlacklisted(const void* module) const { |
- return ContainsKey(blacklisted_modules_, module); |
-} |
- |
-void LeafUnwindBlacklist::BlacklistModule(const void* module) { |
- CHECK(module); |
- blacklisted_modules_.insert(module); |
-} |
- |
-LeafUnwindBlacklist::LeafUnwindBlacklist() {} |
-LeafUnwindBlacklist::~LeafUnwindBlacklist() {} |
- |
} // namespace |
// Win32StackFrameUnwinder ---------------------------------------------------- |
@@ -172,8 +116,6 @@ Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {} |
bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context, |
ScopedModuleHandle* module) { |
#ifdef _WIN64 |
- CHECK(!at_top_frame_ || unwind_info_present_for_all_frames_); |
- |
ScopedModuleHandle frame_module = |
unwind_functions_->GetModuleForProgramCounter(context->Rip); |
if (!frame_module.IsValid()) { |
@@ -203,74 +145,25 @@ bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context, |
context); |
at_top_frame_ = false; |
} else { |
- // RtlLookupFunctionEntry didn't find unwind information. This could mean |
- // the code at the instruction pointer is in: |
- // |
- // 1. a true leaf function (i.e. a function that neither calls a function, |
- // nor allocates any stack space itself) in which case the return |
- // address is at RSP, or |
- // |
- // 2. a function that doesn't adhere to the Microsoft x64 calling |
- // convention, either by not providing the required unwind information, |
- // or by not having the prologue or epilogue required for unwinding; |
- // this case has been observed in crash data in injected third party |
- // DLLs. |
- // |
- // In valid code, case 1 can only occur (by definition) as the last frame |
- // on the stack. This happens in about 5% of observed stacks and can |
- // easily be unwound by popping RSP and using it as the next frame's |
- // instruction pointer. |
- // |
- // Case 2 can occur anywhere on the stack, and attempting to unwind the |
- // stack will result in treating whatever value happens to be on the stack |
- // at RSP as the next frame's instruction pointer. This is certainly wrong |
- // and very likely to lead to crashing by deferencing invalid pointers in |
- // the next RtlVirtualUnwind call. |
- // |
- // If we see case 2 at a location not the last frame, and all the previous |
- // frame had valid unwind information, then this is definitely bad code. |
- // We blacklist the module as untrustable for unwinding if we encounter a |
- // function in it that doesn't have unwind information. |
- |
if (at_top_frame_) { |
at_top_frame_ = false; |
- // We are at the end of the stack. It's very likely that we're in case 1 |
- // since the vast majority of code adheres to the Microsoft x64 calling |
- // convention. But there's a small chance we might be unlucky and be in |
- // case 2. If this module is known to have bad code according to the |
- // leaf unwind blacklist, stop here, otherwise manually unwind. |
- if (LeafUnwindBlacklist::GetInstance()->IsBlacklisted( |
- reinterpret_cast<const void*>(image_base))) { |
- return false; |
- } |
- |
- context->Rip = context->Rsp; |
+ // This is a leaf function (i.e. a function that neither calls a function, |
+ // nor allocates any stack space itself) so the return address is at RSP. |
+ context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp); |
context->Rsp += 8; |
- unwind_info_present_for_all_frames_ = false; |
} else { |
- // We're not at the end of the stack. This frame is untrustworthy and we |
- // can't safely unwind from here. |
- if (!image_base) { |
- // A null image_base means that the the last unwind produced an invalid |
- // instruction pointer. This has been observed where unwind information |
- // was present for a function but was inconsistent with the actual |
- // function code, in particular in BoringSSL. See |
- // https://crbug.com/542919. |
- } else if (unwind_info_present_for_all_frames_) { |
- // Unwind information was present for all previous frames, so we can |
- // be confident this is case 2. Record the module to be blacklisted. |
- LeafUnwindBlacklist::GetInstance()->BlacklistModule( |
- reinterpret_cast<const void *>(image_base)); |
- } else { |
- // We started off on a function without unwind information. It's very |
- // likely that all frames up to this point have been good, and this |
- // frame is case 2. But it's possible that the initial frame was case |
- // 2 but hadn't been blacklisted yet, and we've started to go off into |
- // the weeds. Since we can't be sure, just bail out without |
- // blacklisting the module; chances are we'll later encounter the same |
- // function on a stack with full unwind information. |
- } |
+ // In theory we shouldn't get here, as it means we've encountered a |
+ // function without unwind information below the top of the stack, which |
+ // is forbidden by the Microsoft x64 calling convention. |
+ // |
+ // The one known case in Chrome code that executes this path occurs |
+ // because of BoringSSL unwind information inconsistent with the actual |
+ // function code. See https://crbug.com/542919. |
+ // |
+ // Note that dodgy third-party generated code that otherwise would enter |
+ // this path should be caught by the module check above, since the code |
+ // typically is located outside of a module. |
return false; |
} |
} |
@@ -286,7 +179,6 @@ bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context, |
Win32StackFrameUnwinder::Win32StackFrameUnwinder( |
scoped_ptr<UnwindFunctions> unwind_functions) |
: at_top_frame_(true), |
- unwind_info_present_for_all_frames_(true), |
unwind_functions_(std::move(unwind_functions)) {} |
} // namespace base |