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.
5 #ifndef CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_
6 #define CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_
8 #include "base/logging.h"
9 #include "chrome_frame/crash_reporting/vectored_handler.h"
10 #include "chrome_frame/crash_reporting/nt_loader.h"
13 #error only x86 is supported for now.
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
;
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
34 #define EXCEPTION_UNWIND (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | \
35 EXCEPTION_TARGET_UNWIND | EXCEPTION_COLLIDED_UNWIND)
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
44 VectoredHandlerT
<E
>::VectoredHandlerT(E
* api
) : exceptions_seen_(0), api_(api
) {
48 VectoredHandlerT
<E
>::~VectoredHandlerT() {
52 LONG VectoredHandlerT
<E
>::Handler(EXCEPTION_POINTERS
* exceptionInfo
) {
53 // TODO(stoyan): Consider reentrancy
54 const DWORD exceptionCode
= exceptionInfo
->ExceptionRecord
->ExceptionCode
;
56 // Not interested in non-error exceptions. In this category falls exceptions
58 // 0x40010006 - OutputDebugStringA. Seen when no debugger is attached
59 // (otherwise debugger swallows the exception and prints
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
;
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
;
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
;
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
;
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
;
99 if (api_
->IsOurModule(exceptionInfo
->ExceptionRecord
->ExceptionAddress
)) {
100 api_
->WriteDump(exceptionInfo
);
101 return ExceptionContinueSearch
;
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
;
116 return ExceptionContinueSearch
;
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
)) {
130 RegistrationFrame
= RegistrationFrame
->Next
;
137 // Here comes the default Windows 32-bit implementation.
140 static EXCEPTION_REGISTRATION_RECORD
* InternalRtlpGetExceptionList() {
148 static char* GetStackTopLimit() {
154 } // end of namespace
156 // Class which methods simply forwards to Win32 API.
157 // Used as template (external interface) of VectoredHandlerT<E>.
158 class Win32VEHTraits
{
160 enum {max_back_trace
= 62};
163 EXCEPTION_REGISTRATION_RECORD
* RtlpGetExceptionList() {
164 return InternalRtlpGetExceptionList();
167 static inline WORD
RtlCaptureStackBackTrace(DWORD FramesToSkip
,
168 DWORD FramesToCapture
, void** BackTrace
, DWORD
* BackTraceHash
) {
169 return ::RtlCaptureStackBackTrace(FramesToSkip
, FramesToCapture
,
170 BackTrace
, BackTraceHash
);
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
)
182 HMODULE top_seh_module
= NULL
;
183 if (EXCEPTION_CHAIN_END
!= seh_record
)
184 top_seh_module
= GetModuleHandleFromAddress(seh_record
->Handler
);
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
)
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
);
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.
211 (entry
->Flags
& LDRP_IMAGE_DLL
) != 0 &&
212 (entry
->EntryPoint
!= NULL
) &&
213 (entry
->Flags
& LDRP_PROCESS_ATTACH_CALLED
) == 0)
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.
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
);
234 static inline const void* CodeOffset(const void* code
, int offset
) {
235 return reinterpret_cast<const char*>(code
) + offset
;
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
);
245 return module_at_address
;
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
{
256 typedef bool (*DumpHandler
)(EXCEPTION_POINTERS
* p
);
258 CrashHandlerTraits() : dump_handler_(NULL
) {}
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
,
277 inline bool WriteDump(EXCEPTION_POINTERS
* p
) {
279 return dump_handler_(p
);
286 DumpHandler dump_handler_
;
289 #endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_