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 |