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_ 290