Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 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 #ifndef CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ | 5 #ifndef CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ |
| 6 #define CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ | 6 #define CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ |
| 7 | 7 |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "chrome_frame/crash_reporting/vectored_handler.h" | 9 #include "chrome_frame/crash_reporting/vectored_handler.h" |
| 10 #include "chrome_frame/crash_reporting/nt_loader.h" | 10 #include "chrome_frame/crash_reporting/nt_loader.h" |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 76 exceptions_seen_++; | 76 exceptions_seen_++; |
| 77 | 77 |
| 78 // If the exception code is STATUS_STACK_OVERFLOW then proceed as usual - | 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 - | 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 | 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. | 81 // seeing the follow-up STATUS_ACCESS_VIOLATION(s). See bug 32441. |
| 82 if (exceptionCode != STATUS_STACK_OVERFLOW && api_->CheckForStackOverflow()) { | 82 if (exceptionCode != STATUS_STACK_OVERFLOW && api_->CheckForStackOverflow()) { |
| 83 return ExceptionContinueSearch; | 83 return ExceptionContinueSearch; |
| 84 } | 84 } |
| 85 | 85 |
| 86 // Check whether exception address is inbetween | 86 // Check whether exception will be handled by the module that cuased it. |
| 87 // [IsBadReadPtr, IsBadReadPtr + 0xXX] | 87 // This should automatically handle the case of IsBadReadPtr and family. |
| 88 if (api_->ShouldIgnoreException(exceptionInfo)) { | 88 const EXCEPTION_REGISTRATION_RECORD* seh = api_->RtlpGetExceptionList(); |
| 89 if (api_->ShouldIgnoreException(exceptionInfo, seh)) { | |
| 89 return ExceptionContinueSearch; | 90 return ExceptionContinueSearch; |
| 90 } | 91 } |
| 91 | 92 |
| 92 const DWORD exceptionFlags = exceptionInfo->ExceptionRecord->ExceptionFlags; | 93 const DWORD exceptionFlags = exceptionInfo->ExceptionRecord->ExceptionFlags; |
| 93 // I don't think VEH is called on unwind. Just to be safe. | 94 // I don't think VEH is called on unwind. Just to be safe. |
| 94 if (IS_DISPATCHING(exceptionFlags)) { | 95 if (IS_DISPATCHING(exceptionFlags)) { |
| 95 if (ModuleHasInstalledSEHFilter()) | 96 if (ModuleHasInstalledSEHFilter()) |
| 96 return ExceptionContinueSearch; | 97 return ExceptionContinueSearch; |
| 97 | 98 |
| 98 if (api_->IsOurModule(exceptionInfo->ExceptionRecord->ExceptionAddress)) { | 99 if (api_->IsOurModule(exceptionInfo->ExceptionRecord->ExceptionAddress)) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 162 EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() { | 163 EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() { |
| 163 return InternalRtlpGetExceptionList(); | 164 return InternalRtlpGetExceptionList(); |
| 164 } | 165 } |
| 165 | 166 |
| 166 static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip, | 167 static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip, |
| 167 DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) { | 168 DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) { |
| 168 return ::RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, | 169 return ::RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, |
| 169 BackTrace, BackTraceHash); | 170 BackTrace, BackTraceHash); |
| 170 } | 171 } |
| 171 | 172 |
| 172 static bool ShouldIgnoreException(const EXCEPTION_POINTERS* exceptionInfo) { | 173 static bool ShouldIgnoreException(const EXCEPTION_POINTERS* exceptionInfo, |
|
stoyan
2010/12/09 09:58:23
Hm.. if a module had installed SEH filter it does
amit
2010/12/09 17:58:59
True, there's a chance that the module's SEH filte
| |
| 174 const EXCEPTION_REGISTRATION_RECORD* seh_record) { | |
| 173 const void* address = exceptionInfo->ExceptionRecord->ExceptionAddress; | 175 const void* address = exceptionInfo->ExceptionRecord->ExceptionAddress; |
| 174 for (int i = 0; i < kIgnoreEntries; i++) { | 176 const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | |
| 175 const CodeBlock& code_block = IgnoreExceptions[i]; | 177 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS; |
| 176 DCHECK(code_block.code) << "Win32VEHTraits::CodeBlocks not initialized!"; | 178 HMODULE crashing_module = NULL; |
| 177 if ((CodeOffset(code_block.code, code_block.begin_offset) <= address) && | 179 ::GetModuleHandleEx(flags, reinterpret_cast<LPCWSTR>(address), |
|
stoyan
2010/12/09 09:58:23
Beware - IIRC GetModuleHandle obtains loader lock.
amit
2010/12/09 17:58:59
Good point, changed it to use VirtualQuery
| |
| 178 (address < CodeOffset(code_block.code, code_block.end_offset))) { | 180 &crashing_module); |
| 179 return true; | 181 HMODULE top_seh_module = NULL; |
| 180 } | 182 if (EXCEPTION_CHAIN_END != seh_record) { |
| 183 ::GetModuleHandleEx(flags, reinterpret_cast<LPCWSTR>(seh_record->Handler), | |
|
stoyan
2010/12/09 09:58:23
if crashing_module == NULL you don't have to call
amit
2010/12/09 17:58:59
Yupp, added the check for crashing_module and chan
| |
| 184 &top_seh_module); | |
| 181 } | 185 } |
| 182 | 186 |
| 187 // check if the crash address belongs in a module that's expecting | |
| 188 // and handling it. This should address cases like kernel32!IsBadXXX | |
| 189 // and urlmon!IsValidInterface | |
| 190 if (crashing_module && (crashing_module == top_seh_module)) | |
| 191 return true; | |
| 192 | |
| 183 // We don't want to report exceptions that occur during DLL loading, | 193 // We don't want to report exceptions that occur during DLL loading, |
| 184 // as those are captured and ignored by the NT loader. If this thread | 194 // as those are captured and ignored by the NT loader. If this thread |
| 185 // is holding the loader's lock, there's a possiblity that the crash | 195 // is holding the loader's lock, there's a possiblity that the crash |
| 186 // is occurring in a loading DLL, in which case we resolve the | 196 // is occurring in a loading DLL, in which case we resolve the |
| 187 // crash address to a module and check on the module's status. | 197 // crash address to a module and check on the module's status. |
| 188 const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | | 198 if (crashing_module && nt_loader::OwnsLoaderLock()) { |
| 189 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS; | |
| 190 HMODULE crashing_module = NULL; | |
| 191 if (nt_loader::OwnsLoaderLock() && ::GetModuleHandleEx( | |
| 192 flags, reinterpret_cast<LPCWSTR>(address), &crashing_module)) { | |
| 193 nt_loader::LDR_DATA_TABLE_ENTRY* entry = | 199 nt_loader::LDR_DATA_TABLE_ENTRY* entry = |
| 194 nt_loader::GetLoaderEntry(crashing_module); | 200 nt_loader::GetLoaderEntry(crashing_module); |
| 195 | 201 |
| 196 // If: | 202 // If: |
| 197 // 1. we found the entry in question, and | 203 // 1. we found the entry in question, and |
| 198 // 2. the entry is a DLL (has the IMAGE_DLL flag set), and | 204 // 2. the entry is a DLL (has the IMAGE_DLL flag set), and |
| 199 // 3. the DLL has a non-null entrypoint, and | 205 // 3. the DLL has a non-null entrypoint, and |
| 200 // 4. the loader has not tagged it with the process attached called flag | 206 // 4. the loader has not tagged it with the process attached called flag |
| 201 // we conclude that the crash is most likely during the loading of the | 207 // we conclude that the crash is most likely during the loading of the |
| 202 // module and bail on reporting the crash to avoid false positives | 208 // module and bail on reporting the crash to avoid false positives |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 218 void* stack_top = GetStackTopLimit() - kPageSize; | 224 void* stack_top = GetStackTopLimit() - kPageSize; |
| 219 ::VirtualQuery(stack_top, &mi, sizeof(mi)); | 225 ::VirtualQuery(stack_top, &mi, sizeof(mi)); |
| 220 // The above call may result in moving the top of the stack. | 226 // The above call may result in moving the top of the stack. |
| 221 // Check once more. | 227 // Check once more. |
| 222 void* stack_top2 = GetStackTopLimit() - kPageSize; | 228 void* stack_top2 = GetStackTopLimit() - kPageSize; |
| 223 if (stack_top2 != stack_top) | 229 if (stack_top2 != stack_top) |
| 224 ::VirtualQuery(stack_top2, &mi, sizeof(mi)); | 230 ::VirtualQuery(stack_top2, &mi, sizeof(mi)); |
| 225 return !(mi.Protect & PAGE_GUARD); | 231 return !(mi.Protect & PAGE_GUARD); |
| 226 } | 232 } |
| 227 | 233 |
| 228 static void InitializeIgnoredBlocks() { | |
| 229 // Initialize ignored exception list | |
| 230 for (int i = 0; i < kIgnoreEntries; i++) { | |
| 231 CodeBlock& code_block = IgnoreExceptions[i]; | |
| 232 if (!code_block.code) { | |
| 233 HMODULE module = GetModuleHandleA(code_block.module); | |
| 234 DCHECK(module) << "GetModuleHandle error: " << GetLastError(); | |
| 235 code_block.code = GetProcAddress(module, code_block.function); | |
| 236 DCHECK(code_block.code) << "GetProcAddress error: "<< GetLastError(); | |
| 237 } | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 private: | 234 private: |
| 242 static inline const void* CodeOffset(const void* code, int offset) { | 235 static inline const void* CodeOffset(const void* code, int offset) { |
| 243 return reinterpret_cast<const char*>(code) + offset; | 236 return reinterpret_cast<const char*>(code) + offset; |
| 244 } | 237 } |
| 245 | |
| 246 // Block of code to be ignored for exceptions | |
| 247 struct CodeBlock { | |
| 248 char* module; | |
| 249 char* function; | |
| 250 int begin_offset; | |
| 251 int end_offset; | |
| 252 const void* code; | |
| 253 }; | |
| 254 | |
| 255 static const int kIgnoreEntries = 4; | |
| 256 static CodeBlock IgnoreExceptions[kIgnoreEntries]; | |
| 257 }; | 238 }; |
| 258 | 239 |
| 259 DECLSPEC_SELECTANY Win32VEHTraits::CodeBlock | |
| 260 Win32VEHTraits::IgnoreExceptions[kIgnoreEntries] = { | |
| 261 { "kernel32.dll", "IsBadReadPtr", 0, 100, NULL }, | |
| 262 { "kernel32.dll", "IsBadWritePtr", 0, 100, NULL }, | |
| 263 { "kernel32.dll", "IsBadStringPtrA", 0, 100, NULL }, | |
| 264 { "kernel32.dll", "IsBadStringPtrW", 0, 100, NULL }, | |
| 265 }; | |
| 266 | 240 |
| 267 // Use Win32 API; checks for single (current) module. Will call a specified | 241 // Use Win32 API; checks for single (current) module. Will call a specified |
| 268 // CrashHandlerTraits::DumpHandler when taking a dump. | 242 // CrashHandlerTraits::DumpHandler when taking a dump. |
| 269 class CrashHandlerTraits : public Win32VEHTraits, | 243 class CrashHandlerTraits : public Win32VEHTraits, |
| 270 public ModuleOfInterestWithExcludedRegion { | 244 public ModuleOfInterestWithExcludedRegion { |
| 271 public: | 245 public: |
| 272 | 246 |
| 273 typedef bool (*DumpHandler)(EXCEPTION_POINTERS* p); | 247 typedef bool (*DumpHandler)(EXCEPTION_POINTERS* p); |
| 274 | 248 |
| 275 CrashHandlerTraits() : dump_handler_(NULL) {} | 249 CrashHandlerTraits() : dump_handler_(NULL) {} |
| 276 | 250 |
| 277 // Note that breakpad_lock must be held when this is called. | 251 // Note that breakpad_lock must be held when this is called. |
| 278 void Init(const void* veh_segment_start, const void* veh_segment_end, | 252 void Init(const void* veh_segment_start, const void* veh_segment_end, |
| 279 DumpHandler dump_handler) { | 253 DumpHandler dump_handler) { |
| 280 DCHECK(dump_handler); | 254 DCHECK(dump_handler); |
| 281 dump_handler_ = dump_handler; | 255 dump_handler_ = dump_handler; |
| 282 Win32VEHTraits::InitializeIgnoredBlocks(); | |
| 283 ModuleOfInterestWithExcludedRegion::SetCurrentModule(); | 256 ModuleOfInterestWithExcludedRegion::SetCurrentModule(); |
| 284 // Pointers to static (non-extern) functions take the address of the | 257 // Pointers to static (non-extern) functions take the address of the |
| 285 // function's first byte, as opposed to an entry in the compiler generated | 258 // function's first byte, as opposed to an entry in the compiler generated |
| 286 // JMP table. In release builds /OPT:REF wipes away the JMP table, but debug | 259 // JMP table. In release builds /OPT:REF wipes away the JMP table, but debug |
| 287 // builds are not so lucky. | 260 // builds are not so lucky. |
| 288 ModuleOfInterestWithExcludedRegion::SetExcludedRegion(veh_segment_start, | 261 ModuleOfInterestWithExcludedRegion::SetExcludedRegion(veh_segment_start, |
| 289 veh_segment_end); | 262 veh_segment_end); |
| 290 } | 263 } |
| 291 | 264 |
| 292 void Shutdown() { | 265 void Shutdown() { |
| 293 } | 266 } |
| 294 | 267 |
| 295 inline bool WriteDump(EXCEPTION_POINTERS* p) { | 268 inline bool WriteDump(EXCEPTION_POINTERS* p) { |
| 296 if (dump_handler_) { | 269 if (dump_handler_) { |
| 297 return dump_handler_(p); | 270 return dump_handler_(p); |
| 298 } else { | 271 } else { |
| 299 return false; | 272 return false; |
| 300 } | 273 } |
| 301 } | 274 } |
| 302 | 275 |
| 303 private: | 276 private: |
| 304 DumpHandler dump_handler_; | 277 DumpHandler dump_handler_; |
| 305 }; | 278 }; |
| 306 | 279 |
| 307 #endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ | 280 #endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ |
| OLD | NEW |