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, |
| 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 = GetModuleHandleFromAddress(address); |
177 if ((CodeOffset(code_block.code, code_block.begin_offset) <= address) && | 179 if (!crashing_module) |
178 (address < CodeOffset(code_block.code, code_block.end_offset))) { | 180 return false; |
179 return true; | 181 |
180 } | 182 HMODULE top_seh_module = NULL; |
181 } | 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; |
182 | 191 |
183 // We don't want to report exceptions that occur during DLL loading, | 192 // 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 | 193 // 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 | 194 // 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 | 195 // 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. | 196 // crash address to a module and check on the module's status. |
188 const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | | 197 if (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 = | 198 nt_loader::LDR_DATA_TABLE_ENTRY* entry = |
194 nt_loader::GetLoaderEntry(crashing_module); | 199 nt_loader::GetLoaderEntry(crashing_module); |
195 | 200 |
196 // If: | 201 // If: |
197 // 1. we found the entry in question, and | 202 // 1. we found the entry in question, and |
198 // 2. the entry is a DLL (has the IMAGE_DLL flag set), and | 203 // 2. the entry is a DLL (has the IMAGE_DLL flag set), and |
199 // 3. the DLL has a non-null entrypoint, and | 204 // 3. the DLL has a non-null entrypoint, and |
200 // 4. the loader has not tagged it with the process attached called flag | 205 // 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 | 206 // 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 | 207 // module and bail on reporting the crash to avoid false positives |
203 // during crashes that occur during module loads, such as e.g. when | 208 // during crashes that occur during module loads, such as e.g. when |
204 // the hook manager attempts to load buggy window hook DLLs. | 209 // the hook manager attempts to load buggy window hook DLLs. |
205 if (entry && | 210 if (entry && |
206 (entry->Flags & LDRP_IMAGE_DLL) != 0 && | 211 (entry->Flags & LDRP_IMAGE_DLL) != 0 && |
207 (entry->EntryPoint != NULL) && | 212 (entry->EntryPoint != NULL) && |
208 (entry->Flags & LDRP_PROCESS_ATTACH_CALLED) == 0) | 213 (entry->Flags & LDRP_PROCESS_ATTACH_CALLED) == 0) |
209 return true; | 214 return true; |
210 } | 215 } |
211 | 216 |
212 return false; | 217 return false; |
213 } | 218 } |
214 | 219 |
215 static bool CheckForStackOverflow() { | 220 static bool CheckForStackOverflow() { |
216 MEMORY_BASIC_INFORMATION mi; | 221 MEMORY_BASIC_INFORMATION mi = {0}; |
217 const DWORD kPageSize = 0x1000; | 222 const DWORD kPageSize = 0x1000; |
218 void* stack_top = GetStackTopLimit() - kPageSize; | 223 void* stack_top = GetStackTopLimit() - kPageSize; |
219 ::VirtualQuery(stack_top, &mi, sizeof(mi)); | 224 ::VirtualQuery(stack_top, &mi, sizeof(mi)); |
220 // The above call may result in moving the top of the stack. | 225 // The above call may result in moving the top of the stack. |
221 // Check once more. | 226 // Check once more. |
222 void* stack_top2 = GetStackTopLimit() - kPageSize; | 227 void* stack_top2 = GetStackTopLimit() - kPageSize; |
223 if (stack_top2 != stack_top) | 228 if (stack_top2 != stack_top) |
224 ::VirtualQuery(stack_top2, &mi, sizeof(mi)); | 229 ::VirtualQuery(stack_top2, &mi, sizeof(mi)); |
225 return !(mi.Protect & PAGE_GUARD); | 230 return !(mi.Protect & PAGE_GUARD); |
226 } | 231 } |
227 | 232 |
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: | 233 private: |
242 static inline const void* CodeOffset(const void* code, int offset) { | 234 static inline const void* CodeOffset(const void* code, int offset) { |
243 return reinterpret_cast<const char*>(code) + offset; | 235 return reinterpret_cast<const char*>(code) + offset; |
244 } | 236 } |
245 | 237 |
246 // Block of code to be ignored for exceptions | 238 static HMODULE GetModuleHandleFromAddress(const void* p) { |
247 struct CodeBlock { | 239 HMODULE module_at_address = NULL; |
248 char* module; | 240 MEMORY_BASIC_INFORMATION mi = {0}; |
249 char* function; | 241 if (::VirtualQuery(p, &mi, sizeof(mi)) && (mi.Type & MEM_IMAGE)) { |
250 int begin_offset; | 242 module_at_address = reinterpret_cast<HMODULE>(mi.AllocationBase); |
251 int end_offset; | 243 } |
252 const void* code; | |
253 }; | |
254 | 244 |
255 static const int kIgnoreEntries = 4; | 245 return module_at_address; |
256 static CodeBlock IgnoreExceptions[kIgnoreEntries]; | 246 } |
257 }; | 247 }; |
258 | 248 |
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 | 249 |
267 // Use Win32 API; checks for single (current) module. Will call a specified | 250 // Use Win32 API; checks for single (current) module. Will call a specified |
268 // CrashHandlerTraits::DumpHandler when taking a dump. | 251 // CrashHandlerTraits::DumpHandler when taking a dump. |
269 class CrashHandlerTraits : public Win32VEHTraits, | 252 class CrashHandlerTraits : public Win32VEHTraits, |
270 public ModuleOfInterestWithExcludedRegion { | 253 public ModuleOfInterestWithExcludedRegion { |
271 public: | 254 public: |
272 | 255 |
273 typedef bool (*DumpHandler)(EXCEPTION_POINTERS* p); | 256 typedef bool (*DumpHandler)(EXCEPTION_POINTERS* p); |
274 | 257 |
275 CrashHandlerTraits() : dump_handler_(NULL) {} | 258 CrashHandlerTraits() : dump_handler_(NULL) {} |
276 | 259 |
277 // Note that breakpad_lock must be held when this is called. | 260 // Note that breakpad_lock must be held when this is called. |
278 void Init(const void* veh_segment_start, const void* veh_segment_end, | 261 void Init(const void* veh_segment_start, const void* veh_segment_end, |
279 DumpHandler dump_handler) { | 262 DumpHandler dump_handler) { |
280 DCHECK(dump_handler); | 263 DCHECK(dump_handler); |
281 dump_handler_ = dump_handler; | 264 dump_handler_ = dump_handler; |
282 Win32VEHTraits::InitializeIgnoredBlocks(); | |
283 ModuleOfInterestWithExcludedRegion::SetCurrentModule(); | 265 ModuleOfInterestWithExcludedRegion::SetCurrentModule(); |
284 // Pointers to static (non-extern) functions take the address of the | 266 // 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 | 267 // 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 | 268 // JMP table. In release builds /OPT:REF wipes away the JMP table, but debug |
287 // builds are not so lucky. | 269 // builds are not so lucky. |
288 ModuleOfInterestWithExcludedRegion::SetExcludedRegion(veh_segment_start, | 270 ModuleOfInterestWithExcludedRegion::SetExcludedRegion(veh_segment_start, |
289 veh_segment_end); | 271 veh_segment_end); |
290 } | 272 } |
291 | 273 |
292 void Shutdown() { | 274 void Shutdown() { |
293 } | 275 } |
294 | 276 |
295 inline bool WriteDump(EXCEPTION_POINTERS* p) { | 277 inline bool WriteDump(EXCEPTION_POINTERS* p) { |
296 if (dump_handler_) { | 278 if (dump_handler_) { |
297 return dump_handler_(p); | 279 return dump_handler_(p); |
298 } else { | 280 } else { |
299 return false; | 281 return false; |
300 } | 282 } |
301 } | 283 } |
302 | 284 |
303 private: | 285 private: |
304 DumpHandler dump_handler_; | 286 DumpHandler dump_handler_; |
305 }; | 287 }; |
306 | 288 |
307 #endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ | 289 #endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_ |
OLD | NEW |