| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 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 #ifndef CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ | |
| 6 #define CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ | |
| 7 | |
| 8 #include "base/logging.h" | |
| 9 #include "chrome_frame/crash_reporting/vectored_handler.h" | |
| 10 #include "chrome_frame/crash_reporting/nt_loader.h" | |
| 11 | |
| 12 #if !defined(_M_IX86) | |
| 13 #error only x86 is supported for now. | |
| 14 #endif | |
| 15 | |
| 16 #ifndef EXCEPTION_CHAIN_END | |
| 17 #define EXCEPTION_CHAIN_END ((struct _EXCEPTION_REGISTRATION_RECORD*)-1) | |
| 18 #if !defined(_WIN32_WINNT_WIN8) | |
| 19 typedef struct _EXCEPTION_REGISTRATION_RECORD { | |
| 20 struct _EXCEPTION_REGISTRATION_RECORD* Next; | |
| 21 PVOID Handler; | |
| 22 } EXCEPTION_REGISTRATION_RECORD; | |
| 23 // VEH handler flags settings. | |
| 24 // These are grabbed from winnt.h for PocketPC. | |
| 25 // Only EXCEPTION_NONCONTINUABLE in defined in "regular" winnt.h | |
| 26 // #define EXCEPTION_NONCONTINUABLE 0x1 // Noncontinuable exception | |
| 27 #define EXCEPTION_UNWINDING 0x2 // Unwind is in progress | |
| 28 #define EXCEPTION_EXIT_UNWIND 0x4 // Exit unwind is in progress | |
| 29 #define EXCEPTION_STACK_INVALID 0x8 // Stack out of limits or unaligned | |
| 30 #define EXCEPTION_NESTED_CALL 0x10 // Nested exception handler call | |
| 31 #define EXCEPTION_TARGET_UNWIND 0x20 // Target unwind in progress | |
| 32 #define EXCEPTION_COLLIDED_UNWIND 0x40 // Collided exception handler call | |
| 33 | |
| 34 #define EXCEPTION_UNWIND (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | \ | |
| 35 EXCEPTION_TARGET_UNWIND | EXCEPTION_COLLIDED_UNWIND) | |
| 36 | |
| 37 #define IS_UNWINDING(Flag) (((Flag) & EXCEPTION_UNWIND) != 0) | |
| 38 #define IS_DISPATCHING(Flag) (((Flag) & EXCEPTION_UNWIND) == 0) | |
| 39 #define IS_TARGET_UNWIND(Flag) ((Flag) & EXCEPTION_TARGET_UNWIND) | |
| 40 #endif // !defined(_WIN32_WINNT_WIN8) | |
| 41 #endif // EXCEPTION_CHAIN_END | |
| 42 | |
| 43 template <typename E> | |
| 44 VectoredHandlerT<E>::VectoredHandlerT(E* api) : exceptions_seen_(0), api_(api) { | |
| 45 } | |
| 46 | |
| 47 template <typename E> | |
| 48 VectoredHandlerT<E>::~VectoredHandlerT() { | |
| 49 } | |
| 50 | |
| 51 template <typename E> | |
| 52 LONG VectoredHandlerT<E>::Handler(EXCEPTION_POINTERS* exceptionInfo) { | |
| 53 // TODO(stoyan): Consider reentrancy | |
| 54 const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; | |
| 55 | |
| 56 // Not interested in non-error exceptions. In this category falls exceptions | |
| 57 // like: | |
| 58 // 0x40010006 - OutputDebugStringA. Seen when no debugger is attached | |
| 59 // (otherwise debugger swallows the exception and prints | |
| 60 // the string). | |
| 61 // 0x406D1388 - DebuggerProbe. Used by debug CRT - for example see source | |
| 62 // code of isatty(). Used to name a thread as well. | |
| 63 // RPC_E_DISCONNECTED and Co. - COM IPC non-fatal warnings | |
| 64 // STATUS_BREAKPOINT and Co. - Debugger related breakpoints | |
| 65 if ((exceptionCode & ERROR_SEVERITY_ERROR) != ERROR_SEVERITY_ERROR) { | |
| 66 return ExceptionContinueSearch; | |
| 67 } | |
| 68 | |
| 69 // Ignore custom exception codes. | |
| 70 // MSXML likes to raise 0xE0000001 while parsing. | |
| 71 // Note the C++ SEH (0xE06D7363) also fails in that range. | |
| 72 if (exceptionCode & APPLICATION_ERROR_MASK) { | |
| 73 return ExceptionContinueSearch; | |
| 74 } | |
| 75 | |
| 76 exceptions_seen_++; | |
| 77 | |
| 78 // If the exception code is STATUS_STACK_OVERFLOW then proceed as usual - | |
| 79 // we want to report it. Otherwise check whether guard page in stack is gone - | |
| 80 // i.e. stack overflow has been already observed and most probably we are | |
| 81 // seeing the follow-up STATUS_ACCESS_VIOLATION(s). See bug 32441. | |
| 82 if (exceptionCode != STATUS_STACK_OVERFLOW && api_->CheckForStackOverflow()) { | |
| 83 return ExceptionContinueSearch; | |
| 84 } | |
| 85 | |
| 86 // Check whether exception will be handled by the module that cuased it. | |
| 87 // This should automatically handle the case of IsBadReadPtr and family. | |
| 88 const EXCEPTION_REGISTRATION_RECORD* seh = api_->RtlpGetExceptionList(); | |
| 89 if (api_->ShouldIgnoreException(exceptionInfo, seh)) { | |
| 90 return ExceptionContinueSearch; | |
| 91 } | |
| 92 | |
| 93 const DWORD exceptionFlags = exceptionInfo->ExceptionRecord->ExceptionFlags; | |
| 94 // I don't think VEH is called on unwind. Just to be safe. | |
| 95 if (IS_DISPATCHING(exceptionFlags)) { | |
| 96 if (ModuleHasInstalledSEHFilter()) | |
| 97 return ExceptionContinueSearch; | |
| 98 | |
| 99 if (api_->IsOurModule(exceptionInfo->ExceptionRecord->ExceptionAddress)) { | |
| 100 api_->WriteDump(exceptionInfo); | |
| 101 return ExceptionContinueSearch; | |
| 102 } | |
| 103 | |
| 104 // See whether our module is somewhere in the call stack. | |
| 105 void* back_trace[E::max_back_trace] = {0}; | |
| 106 DWORD captured = api_->RtlCaptureStackBackTrace(0, api_->max_back_trace, | |
| 107 &back_trace[0], NULL); | |
| 108 for (DWORD i = 0; i < captured; ++i) { | |
| 109 if (api_->IsOurModule(back_trace[i])) { | |
| 110 api_->WriteDump(exceptionInfo); | |
| 111 return ExceptionContinueSearch; | |
| 112 } | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 return ExceptionContinueSearch; | |
| 117 } | |
| 118 | |
| 119 template <typename E> | |
| 120 bool VectoredHandlerT<E>::ModuleHasInstalledSEHFilter() { | |
| 121 const EXCEPTION_REGISTRATION_RECORD* RegistrationFrame = | |
| 122 api_->RtlpGetExceptionList(); | |
| 123 // TODO(stoyan): Add the stack limits check and some sanity checks like | |
| 124 // decreasing addresses of registration records | |
| 125 while (RegistrationFrame != EXCEPTION_CHAIN_END) { | |
| 126 if (api_->IsOurModule(RegistrationFrame->Handler)) { | |
| 127 return true; | |
| 128 } | |
| 129 | |
| 130 RegistrationFrame = RegistrationFrame->Next; | |
| 131 } | |
| 132 | |
| 133 return false; | |
| 134 } | |
| 135 | |
| 136 | |
| 137 // Here comes the default Windows 32-bit implementation. | |
| 138 namespace { | |
| 139 __declspec(naked) | |
| 140 static EXCEPTION_REGISTRATION_RECORD* InternalRtlpGetExceptionList() { | |
| 141 __asm { | |
| 142 mov eax, fs:0 | |
| 143 ret | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 __declspec(naked) | |
| 148 static char* GetStackTopLimit() { | |
| 149 __asm { | |
| 150 mov eax, fs:8 | |
| 151 ret | |
| 152 } | |
| 153 } | |
| 154 } // end of namespace | |
| 155 | |
| 156 // Class which methods simply forwards to Win32 API. | |
| 157 // Used as template (external interface) of VectoredHandlerT<E>. | |
| 158 class Win32VEHTraits { | |
| 159 public: | |
| 160 enum {max_back_trace = 62}; | |
| 161 | |
| 162 static inline | |
| 163 EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() { | |
| 164 return InternalRtlpGetExceptionList(); | |
| 165 } | |
| 166 | |
| 167 static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip, | |
| 168 DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) { | |
| 169 return ::RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, | |
| 170 BackTrace, BackTraceHash); | |
| 171 } | |
| 172 | |
| 173 static bool ShouldIgnoreException(const EXCEPTION_POINTERS* exceptionInfo, | |
| 174 const EXCEPTION_REGISTRATION_RECORD* seh_record) { | |
| 175 const void* address = exceptionInfo->ExceptionRecord->ExceptionAddress; | |
| 176 const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | | |
| 177 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS; | |
| 178 HMODULE crashing_module = GetModuleHandleFromAddress(address); | |
| 179 if (!crashing_module) | |
| 180 return false; | |
| 181 | |
| 182 HMODULE top_seh_module = NULL; | |
| 183 if (EXCEPTION_CHAIN_END != seh_record) | |
| 184 top_seh_module = GetModuleHandleFromAddress(seh_record->Handler); | |
| 185 | |
| 186 // check if the crash address belongs in a module that's expecting | |
| 187 // and handling it. This should address cases like kernel32!IsBadXXX | |
| 188 // and urlmon!IsValidInterface | |
| 189 if (crashing_module == top_seh_module) | |
| 190 return true; | |
| 191 | |
| 192 // We don't want to report exceptions that occur during DLL loading, | |
| 193 // as those are captured and ignored by the NT loader. If this thread | |
| 194 // is holding the loader's lock, there's a possiblity that the crash | |
| 195 // is occurring in a loading DLL, in which case we resolve the | |
| 196 // crash address to a module and check on the module's status. | |
| 197 if (nt_loader::OwnsLoaderLock()) { | |
| 198 nt_loader::LDR_DATA_TABLE_ENTRY* entry = | |
| 199 nt_loader::GetLoaderEntry(crashing_module); | |
| 200 | |
| 201 // If: | |
| 202 // 1. we found the entry in question, and | |
| 203 // 2. the entry is a DLL (has the IMAGE_DLL flag set), and | |
| 204 // 3. the DLL has a non-null entrypoint, and | |
| 205 // 4. the loader has not tagged it with the process attached called flag | |
| 206 // we conclude that the crash is most likely during the loading of the | |
| 207 // module and bail on reporting the crash to avoid false positives | |
| 208 // during crashes that occur during module loads, such as e.g. when | |
| 209 // the hook manager attempts to load buggy window hook DLLs. | |
| 210 if (entry && | |
| 211 (entry->Flags & LDRP_IMAGE_DLL) != 0 && | |
| 212 (entry->EntryPoint != NULL) && | |
| 213 (entry->Flags & LDRP_PROCESS_ATTACH_CALLED) == 0) | |
| 214 return true; | |
| 215 } | |
| 216 | |
| 217 return false; | |
| 218 } | |
| 219 | |
| 220 static bool CheckForStackOverflow() { | |
| 221 MEMORY_BASIC_INFORMATION mi = {0}; | |
| 222 const DWORD kPageSize = 0x1000; | |
| 223 void* stack_top = GetStackTopLimit() - kPageSize; | |
| 224 ::VirtualQuery(stack_top, &mi, sizeof(mi)); | |
| 225 // The above call may result in moving the top of the stack. | |
| 226 // Check once more. | |
| 227 void* stack_top2 = GetStackTopLimit() - kPageSize; | |
| 228 if (stack_top2 != stack_top) | |
| 229 ::VirtualQuery(stack_top2, &mi, sizeof(mi)); | |
| 230 return !(mi.Protect & PAGE_GUARD); | |
| 231 } | |
| 232 | |
| 233 private: | |
| 234 static inline const void* CodeOffset(const void* code, int offset) { | |
| 235 return reinterpret_cast<const char*>(code) + offset; | |
| 236 } | |
| 237 | |
| 238 static HMODULE GetModuleHandleFromAddress(const void* p) { | |
| 239 HMODULE module_at_address = NULL; | |
| 240 MEMORY_BASIC_INFORMATION mi = {0}; | |
| 241 if (::VirtualQuery(p, &mi, sizeof(mi)) && (mi.Type & MEM_IMAGE)) { | |
| 242 module_at_address = reinterpret_cast<HMODULE>(mi.AllocationBase); | |
| 243 } | |
| 244 | |
| 245 return module_at_address; | |
| 246 } | |
| 247 }; | |
| 248 | |
| 249 | |
| 250 // Use Win32 API; checks for single (current) module. Will call a specified | |
| 251 // CrashHandlerTraits::DumpHandler when taking a dump. | |
| 252 class CrashHandlerTraits : public Win32VEHTraits, | |
| 253 public ModuleOfInterestWithExcludedRegion { | |
| 254 public: | |
| 255 | |
| 256 typedef bool (*DumpHandler)(EXCEPTION_POINTERS* p); | |
| 257 | |
| 258 CrashHandlerTraits() : dump_handler_(NULL) {} | |
| 259 | |
| 260 // Note that breakpad_lock must be held when this is called. | |
| 261 void Init(const void* veh_segment_start, const void* veh_segment_end, | |
| 262 DumpHandler dump_handler) { | |
| 263 DCHECK(dump_handler); | |
| 264 dump_handler_ = dump_handler; | |
| 265 ModuleOfInterestWithExcludedRegion::SetCurrentModule(); | |
| 266 // Pointers to static (non-extern) functions take the address of the | |
| 267 // function's first byte, as opposed to an entry in the compiler generated | |
| 268 // JMP table. In release builds /OPT:REF wipes away the JMP table, but debug | |
| 269 // builds are not so lucky. | |
| 270 ModuleOfInterestWithExcludedRegion::SetExcludedRegion(veh_segment_start, | |
| 271 veh_segment_end); | |
| 272 } | |
| 273 | |
| 274 void Shutdown() { | |
| 275 } | |
| 276 | |
| 277 inline bool WriteDump(EXCEPTION_POINTERS* p) { | |
| 278 if (dump_handler_) { | |
| 279 return dump_handler_(p); | |
| 280 } else { | |
| 281 return false; | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 private: | |
| 286 DumpHandler dump_handler_; | |
| 287 }; | |
| 288 | |
| 289 #endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ | |
| OLD | NEW |