summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-31 22:17:07 +0000
committerstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-31 22:17:07 +0000
commit30f9ac3cd37f6a4833be4ce0aefa969dea9a1808 (patch)
treeaf91ba96d5f2170bba3f63fbbe8726dbaf3a2c41
parentb20074de864b34a617630fe8cbcb487d9edcca6e (diff)
downloadchromium_src-30f9ac3cd37f6a4833be4ce0aefa969dea9a1808.zip
chromium_src-30f9ac3cd37f6a4833be4ce0aefa969dea9a1808.tar.gz
chromium_src-30f9ac3cd37f6a4833be4ce0aefa969dea9a1808.tar.bz2
Prevent excessive crash reporting due stack overflow (due exception in SEH filter).
BUG=32441 Review URL: http://codereview.chromium.org/557021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37673 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome_frame/crash_reporting/crash_report.cc110
-rw-r--r--chrome_frame/crash_reporting/crash_report.h86
-rw-r--r--chrome_frame/crash_reporting/crash_reporting.gyp2
-rw-r--r--chrome_frame/crash_reporting/vectored_handler-impl.h166
-rw-r--r--chrome_frame/crash_reporting/vectored_handler.h142
-rw-r--r--chrome_frame/crash_reporting/vectored_handler_unittest.cc313
-rw-r--r--chrome_frame/crash_reporting/veh_test.cc104
-rw-r--r--chrome_frame/crash_reporting/veh_test.h80
8 files changed, 645 insertions, 358 deletions
diff --git a/chrome_frame/crash_reporting/crash_report.cc b/chrome_frame/crash_reporting/crash_report.cc
index 2a1b88b..7c2302b 100644
--- a/chrome_frame/crash_reporting/crash_report.cc
+++ b/chrome_frame/crash_reporting/crash_report.cc
@@ -8,19 +8,96 @@
#include "base/basictypes.h"
#include "breakpad/src/client/windows/handler/exception_handler.h"
-#include "chrome_frame/crash_reporting/vectored_handler.h"
// TODO(joshia): factor out common code with chrome used for crash reporting
const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
-google_breakpad::ExceptionHandler* g_breakpad = NULL;
+static google_breakpad::ExceptionHandler * g_breakpad = NULL;
-Win32VEHTraits::CodeBlock Win32VEHTraits::IgnoreExceptions[kIgnoreEntries] = {
- { "kernel32.dll", "IsBadReadPtr", 0, 100, NULL },
- { "kernel32.dll", "IsBadWritePtr", 0, 100, NULL },
- { "kernel32.dll", "IsBadStringPtrA", 0, 100, NULL },
- { "kernel32.dll", "IsBadStringPtrW", 0, 100, NULL },
+#pragma code_seg(push, ".text$va")
+static void veh_segment_start() {}
+#pragma code_seg(pop)
+
+#pragma code_seg(push, ".text$vz")
+static void veh_segment_end() {}
+#pragma code_seg(pop)
+
+// Place code in .text$veh_m.
+#pragma code_seg(push, ".text$vm")
+#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
+
+// Use Win32 API; use breakpad for dumps; checks for single (current) module.
+class CrashHandlerTraits : public Win32VEHTraits,
+ public ModuleOfInterestWithExcludedRegion {
+ public:
+ CrashHandlerTraits() : breakpad_(NULL) {}
+ void Init(google_breakpad::ExceptionHandler* breakpad) {
+ breakpad_ = breakpad;
+ Win32VEHTraits::InitializeIgnoredBlocks();
+ ModuleOfInterestWithExcludedRegion::SetCurrentModule();
+ // Pointers to static (non-extern) functions take the address of the
+ // function's first byte, as opposed to an entry in the compiler generated
+ // JMP table. In release builds /OPT:REF wipes away the JMP table, but debug
+ // builds are not so lucky.
+ ModuleOfInterestWithExcludedRegion::SetExcludedRegion(&veh_segment_start,
+ &veh_segment_end);
+ }
+
+ void Shutdown() {
+ breakpad_ = 0;
+ }
+
+ inline bool WriteDump(EXCEPTION_POINTERS* p) {
+ return breakpad_->WriteMinidumpForException(p);
+ }
+
+ private:
+ google_breakpad::ExceptionHandler* breakpad_;
+};
+
+class CrashHandler {
+ public:
+ CrashHandler() : veh_id_(NULL), handler_(&crash_api_) {}
+ bool Init(google_breakpad::ExceptionHandler* breakpad);
+ void Shutdown();
+ private:
+ VectoredHandlerT<CrashHandlerTraits> handler_;
+ CrashHandlerTraits crash_api_;
+ void* veh_id_;
+
+ static LONG WINAPI VectoredHandlerEntryPoint(EXCEPTION_POINTERS* exptrs);
};
+static CrashHandler g_crash_handler;
+
+LONG WINAPI CrashHandler::VectoredHandlerEntryPoint(
+ EXCEPTION_POINTERS* exptrs) {
+ return g_crash_handler.handler_.Handler(exptrs);
+}
+#pragma code_seg(pop)
+
+bool CrashHandler::Init(google_breakpad::ExceptionHandler* breakpad) {
+ if (veh_id_)
+ return true;
+
+ void* id = ::AddVectoredExceptionHandler(FALSE, &VectoredHandlerEntryPoint);
+ if (id != NULL) {
+ veh_id_ = id;
+ crash_api_.Init(breakpad);
+ return true;
+ }
+
+ return false;
+}
+
+void CrashHandler::Shutdown() {
+ if (veh_id_) {
+ ::RemoveVectoredExceptionHandler(veh_id_);
+ veh_id_ = NULL;
+ }
+
+ crash_api_.Shutdown();
+}
+
std::wstring GetCrashServerPipeName(const std::wstring& user_sid) {
std::wstring pipe_name = kGoogleUpdatePipeName;
pipe_name += user_sid;
@@ -46,17 +123,16 @@ bool InitializeVectoredCrashReportingWithPipeName(
google_breakpad::ExceptionHandler::HANDLER_PURECALL, dump_type,
pipe_name, client_info);
- if (g_breakpad) {
- // Find current module boundaries.
- const void* start = &__ImageBase;
- const char* s = reinterpret_cast<const char*>(start);
- const IMAGE_NT_HEADERS32* nt = reinterpret_cast<const IMAGE_NT_HEADERS32*>
- (s + __ImageBase.e_lfanew);
- const void* end = s + nt->OptionalHeader.SizeOfImage;
- VectoredHandler::Register(start, end);
+ if (!g_breakpad)
+ return false;
+
+ if (!g_crash_handler.Init(g_breakpad)) {
+ delete g_breakpad;
+ g_breakpad = NULL;
+ return false;
}
- return g_breakpad != NULL;
+ return true;
}
bool InitializeVectoredCrashReporting(
@@ -76,7 +152,7 @@ bool InitializeVectoredCrashReporting(
}
bool ShutdownVectoredCrashReporting() {
- VectoredHandler::Unregister();
+ g_crash_handler.Shutdown();
delete g_breakpad;
g_breakpad = NULL;
return true;
diff --git a/chrome_frame/crash_reporting/crash_report.h b/chrome_frame/crash_reporting/crash_report.h
index 829dce2..d9fd42f 100644
--- a/chrome_frame/crash_reporting/crash_report.h
+++ b/chrome_frame/crash_reporting/crash_report.h
@@ -11,7 +11,6 @@
#include "base/logging.h"
#include "breakpad/src/client/windows/handler/exception_handler.h"
-#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
extern google_breakpad::ExceptionHandler* g_breakpad;
extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -30,89 +29,4 @@ bool InitializeVectoredCrashReportingWithPipeName(
bool ShutdownVectoredCrashReporting();
-namespace {
-__declspec(naked)
-static EXCEPTION_REGISTRATION_RECORD* InternalRtlpGetExceptionList() {
- __asm {
- mov eax, fs:0
- ret
- }
-}
-} // end of namespace
-
-// Class which methods simply forwards to Win32 API and uses breakpad to write
-// a minidump. Used as template (external interface) of VectoredHandlerT<E>.
-class Win32VEHTraits : public VEHTraitsBase {
- public:
- static inline void* Register(PVECTORED_EXCEPTION_HANDLER func,
- const void* module_start, const void* module_end) {
- InitializeIgnoredBlocks();
- VEHTraitsBase::SetModule(module_start, module_end);
- return ::AddVectoredExceptionHandler(1, func);
- }
-
- static inline ULONG Unregister(void* handle) {
- return ::RemoveVectoredExceptionHandler(handle);
- }
-
- static inline bool WriteDump(EXCEPTION_POINTERS* p) {
- return g_breakpad->WriteMinidumpForException(p);
- }
-
- static inline EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() {
- return InternalRtlpGetExceptionList();
- }
-
- static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip,
- DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) {
- return ::RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture,
- BackTrace, BackTraceHash);
- }
-
- static bool ShouldIgnoreException(const EXCEPTION_POINTERS* exceptionInfo) {
- const void* address = exceptionInfo->ExceptionRecord->ExceptionAddress;
- for (int i = 0; i < kIgnoreEntries; i++) {
- const CodeBlock& code_block = IgnoreExceptions[i];
- if (code_block.code &&
- (CodeOffset(code_block.code, code_block.begin_offset) <= address) &&
- (address < CodeOffset(code_block.code, code_block.end_offset))) {
- return true;
- }
- }
-
- return false;
- }
-
- static inline void InitializeIgnoredBlocks() {
- // Initialize ignored exception list
- for (int i = 0; i < kIgnoreEntries; i++) {
- CodeBlock& code_block = IgnoreExceptions[i];
- if (!code_block.code) {
- HMODULE module = GetModuleHandleA(code_block.module);
- DCHECK(module) << "GetModuleHandle error: " << GetLastError();
- code_block.code = GetProcAddress(module, code_block.function);
- DCHECK(code_block.code) << "GetProcAddress error: "<< GetLastError();
- }
- }
- }
-
- static inline const void* CodeOffset(const void* code, int offset) {
- return reinterpret_cast<const char*>(code) + offset;
- }
-
- private:
- // Block of code to be ignored for exceptions
- struct CodeBlock {
- char* module;
- char* function;
- int begin_offset;
- int end_offset;
- const void* code;
- };
-
- static const int kIgnoreEntries = 4;
- static CodeBlock IgnoreExceptions[kIgnoreEntries];
-};
-
-
#endif // CHROME_FRAME_CRASH_REPORTING_CRASH_REPORT_H_
diff --git a/chrome_frame/crash_reporting/crash_reporting.gyp b/chrome_frame/crash_reporting/crash_reporting.gyp
index 6cd712c..020c462 100644
--- a/chrome_frame/crash_reporting/crash_reporting.gyp
+++ b/chrome_frame/crash_reporting/crash_reporting.gyp
@@ -35,6 +35,8 @@
'type': 'executable',
'sources': [
'vectored_handler_unittest.cc',
+ 'veh_test.cc',
+ 'veh_test.h',
],
'dependencies': [
'crash_report',
diff --git a/chrome_frame/crash_reporting/vectored_handler-impl.h b/chrome_frame/crash_reporting/vectored_handler-impl.h
index c6d7d8e..9c0e97d 100644
--- a/chrome_frame/crash_reporting/vectored_handler-impl.h
+++ b/chrome_frame/crash_reporting/vectored_handler-impl.h
@@ -7,16 +7,17 @@
#include "chrome_frame/crash_reporting/vectored_handler.h"
#if defined(_M_IX86)
+#ifndef EXCEPTION_CHAIN_END
+#define EXCEPTION_CHAIN_END ((struct _EXCEPTION_REGISTRATION_RECORD*)-1)
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD* Next;
PVOID Handler;
} EXCEPTION_REGISTRATION_RECORD;
-#define EXCEPTION_CHAIN_END ((struct _EXCEPTION_REGISTRATION_RECORD*)-1)
+#endif // EXCEPTION_CHAIN_END
#else
#error only x86 is supported for now.
#endif
-
// VEH handler flags settings.
// These are grabbed from winnt.h for PocketPC.
// Only EXCEPTION_NONCONTINUABLE in defined in "regular" winnt.h
@@ -36,9 +37,16 @@ typedef struct _EXCEPTION_REGISTRATION_RECORD {
#define IS_TARGET_UNWIND(Flag) ((Flag) & EXCEPTION_TARGET_UNWIND)
// End of grabbed section
-template <class E>
-LONG WINAPI VectoredHandlerT<E>::VectoredHandler(
- EXCEPTION_POINTERS* exceptionInfo) {
+template <typename E>
+VectoredHandlerT<E>::VectoredHandlerT(E* api) : exceptions_seen_(0), api_(api) {
+}
+
+template <typename E>
+VectoredHandlerT<E>::~VectoredHandlerT() {
+}
+
+template <typename E>
+LONG VectoredHandlerT<E>::Handler(EXCEPTION_POINTERS* exceptionInfo) {
// TODO(stoyan): Consider reentrancy
const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode;
@@ -51,7 +59,6 @@ LONG WINAPI VectoredHandlerT<E>::VectoredHandler(
// code of isatty(). Used to name a thread as well.
// RPC_E_DISCONNECTED and Co. - COM IPC non-fatal warnings
// STATUS_BREAKPOINT and Co. - Debugger related breakpoints
-
if ((exceptionCode & ERROR_SEVERITY_ERROR) != ERROR_SEVERITY_ERROR) {
return ExceptionContinueSearch;
}
@@ -63,11 +70,19 @@ LONG WINAPI VectoredHandlerT<E>::VectoredHandler(
return ExceptionContinueSearch;
}
- ++VectoredHandlerT<E>::g_exceptions_seen;
+ exceptions_seen_++;
+
+ // If the exception code is STATUS_STACK_OVERFLOW then proceed as usual -
+ // we want to report it. Otherwise check whether guard page in stack is gone -
+ // i.e. stack overflow has been already observed and most probably we are
+ // seeing the follow-up STATUS_ACCESS_VIOLATION(s). See bug 32441.
+ if (exceptionCode != STATUS_STACK_OVERFLOW && api_->CheckForStackOverflow()) {
+ return ExceptionContinueSearch;
+ }
// Check whether exception address is inbetween
// [IsBadReadPtr, IsBadReadPtr + 0xXX]
- if (E::ShouldIgnoreException(exceptionInfo)) {
+ if (api_->ShouldIgnoreException(exceptionInfo)) {
return ExceptionContinueSearch;
}
@@ -77,40 +92,143 @@ LONG WINAPI VectoredHandlerT<E>::VectoredHandler(
if (ModuleHasInstalledSEHFilter())
return ExceptionContinueSearch;
- if (E::IsOurModule(exceptionInfo->ExceptionRecord->ExceptionAddress)) {
- E::WriteDump(exceptionInfo);
+ if (api_->IsOurModule(exceptionInfo->ExceptionRecord->ExceptionAddress)) {
+ api_->WriteDump(exceptionInfo);
return ExceptionContinueSearch;
}
// See whether our module is somewhere in the call stack.
- void* back_trace[max_back_trace] = {0};
- // Skip RtlCaptureStackBackTrace and VectoredHandler itself.
- DWORD captured = E::RtlCaptureStackBackTrace(2, max_back_trace - 2,
- &back_trace[0], NULL);
+ void* back_trace[api_->max_back_trace] = {0};
+ DWORD captured = api_->RtlCaptureStackBackTrace(0, api_->max_back_trace,
+ &back_trace[0], NULL);
for (DWORD i = 0; i < captured; ++i) {
- if (E::IsOurModule(back_trace[i])) {
- E::WriteDump(exceptionInfo);
- return ExceptionContinueSearch;
- }
+ if (api_->IsOurModule(back_trace[i])) {
+ api_->WriteDump(exceptionInfo);
+ return ExceptionContinueSearch;
+ }
}
}
return ExceptionContinueSearch;
}
-template <class E>
-BOOL VectoredHandlerT<E>::ModuleHasInstalledSEHFilter() {
- EXCEPTION_REGISTRATION_RECORD* RegistrationFrame = E::RtlpGetExceptionList();
+template <typename E>
+bool VectoredHandlerT<E>::ModuleHasInstalledSEHFilter() {
+ const EXCEPTION_REGISTRATION_RECORD* RegistrationFrame =
+ api_->RtlpGetExceptionList();
// TODO(stoyan): Add the stack limits check and some sanity checks like
// decreasing addresses of registration records
while (RegistrationFrame != EXCEPTION_CHAIN_END) {
- if (E::IsOurModule(RegistrationFrame->Handler)) {
- return TRUE;
+ if (api_->IsOurModule(RegistrationFrame->Handler)) {
+ return true;
}
RegistrationFrame = RegistrationFrame->Next;
}
- return FALSE;
+ return false;
}
+
+
+// Here comes the default Windows 32-bit implementation.
+namespace {
+ __declspec(naked)
+ static EXCEPTION_REGISTRATION_RECORD* InternalRtlpGetExceptionList() {
+ __asm {
+ mov eax, fs:0
+ ret
+ }
+ }
+ __declspec(naked)
+ static char* GetStackTopLimit() {
+ __asm {
+ mov eax, fs:8
+ ret
+ }
+ }
+} // end of namespace
+
+// Class which methods simply forwards to Win32 API.
+// Used as template (external interface) of VectoredHandlerT<E>.
+class Win32VEHTraits {
+ public:
+ enum {max_back_trace = 62};
+
+ static inline
+ EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() {
+ return InternalRtlpGetExceptionList();
+ }
+
+ static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip,
+ DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) {
+ return ::RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture,
+ BackTrace, BackTraceHash);
+ }
+
+ static bool ShouldIgnoreException(const EXCEPTION_POINTERS* exceptionInfo) {
+ const void* address = exceptionInfo->ExceptionRecord->ExceptionAddress;
+ for (int i = 0; i < kIgnoreEntries; i++) {
+ const CodeBlock& code_block = IgnoreExceptions[i];
+ DCHECK(code_block.code) << "Win32VEHTraits::CodeBlocks not initialized!";
+ if ((CodeOffset(code_block.code, code_block.begin_offset) <= address) &&
+ (address < CodeOffset(code_block.code, code_block.end_offset))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static bool CheckForStackOverflow() {
+ MEMORY_BASIC_INFORMATION mi;
+ const DWORD kPageSize = 0x1000;
+ void* stack_top = GetStackTopLimit() - kPageSize;
+ ::VirtualQuery(stack_top, &mi, sizeof(mi));
+ // The above call may result in moving the top of the stack.
+ // Check once more.
+ void* stack_top2 = GetStackTopLimit() - kPageSize;
+ if (stack_top2 != stack_top)
+ ::VirtualQuery(stack_top2, &mi, sizeof(mi));
+ return !(mi.Protect & PAGE_GUARD);
+ }
+
+ static void InitializeIgnoredBlocks() {
+ // Initialize ignored exception list
+ for (int i = 0; i < kIgnoreEntries; i++) {
+ CodeBlock& code_block = IgnoreExceptions[i];
+ if (!code_block.code) {
+ HMODULE module = GetModuleHandleA(code_block.module);
+ DCHECK(module) << "GetModuleHandle error: " << GetLastError();
+ code_block.code = GetProcAddress(module, code_block.function);
+ DCHECK(code_block.code) << "GetProcAddress error: "<< GetLastError();
+ }
+ }
+ }
+
+ private:
+ static inline const void* CodeOffset(const void* code, int offset) {
+ return reinterpret_cast<const char*>(code) + offset;
+ }
+
+ // Block of code to be ignored for exceptions
+ struct CodeBlock {
+ char* module;
+ char* function;
+ int begin_offset;
+ int end_offset;
+ const void* code;
+ };
+
+ static const int kIgnoreEntries = 4;
+ static CodeBlock IgnoreExceptions[kIgnoreEntries];
+};
+
+DECLSPEC_SELECTANY Win32VEHTraits::CodeBlock
+Win32VEHTraits::IgnoreExceptions[kIgnoreEntries] = {
+ { "kernel32.dll", "IsBadReadPtr", 0, 100, NULL },
+ { "kernel32.dll", "IsBadWritePtr", 0, 100, NULL },
+ { "kernel32.dll", "IsBadStringPtrA", 0, 100, NULL },
+ { "kernel32.dll", "IsBadStringPtrW", 0, 100, NULL },
+};
+
#endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_IMPL_H_
diff --git a/chrome_frame/crash_reporting/vectored_handler.h b/chrome_frame/crash_reporting/vectored_handler.h
index 7447bba..1466bb5 100644
--- a/chrome_frame/crash_reporting/vectored_handler.h
+++ b/chrome_frame/crash_reporting/vectored_handler.h
@@ -5,87 +5,109 @@
#ifndef CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_H_
#define CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_H_
-// Base class for VectoredHandlerT just to hold some members (independent of
-// template parameter)
-class VectoredHandlerBase {
- public:
- // For RtlCaptureStackBackTrace MSDN says:
- // Windows Server 2003 and Windows XP: The sum of the FramesToSkip and
- // FramesToCapture parameters must be less than 64.
- // In practice (on XPSP2) it has to be less than 63, hence leaving us with
- // max back trace of 62.
- static const DWORD max_back_trace = 62;
- static unsigned long g_exceptions_seen;
- protected:
- static void* g_handler;
-};
-DECLSPEC_SELECTANY void* VectoredHandlerBase::g_handler;
-DECLSPEC_SELECTANY unsigned long VectoredHandlerBase::g_exceptions_seen;
+#if !defined(_M_IX86)
+#error only x86 is supported for now.
+#endif
-// The E class is supposed to provide external/API functions. Using template
-// make testability easier. It shall confirm the following concept/archetype:
-// void* Register(PVECTORED_EXCEPTION_HANDLER,
-// const void* module_start, const void* module_end)
-// Registers Vectored Exception Handler, non-unittest implementation shall call
-// ::AddVectoredExceptionHandler Win32 API
-// ULONG Unregister(void*) - ::RemoveVectoredExceptionHandler Win32 API
-// int IsOurModule(const void* address) -
-// void WriteDump(EXCEPTION_POINTERS*) -
-// WORD RtlCaptureStackBackTrace(..) - same as Win32 API
-// EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() - same as Win32 API
-// You may want to derive own External class by deriving from
-// VEHExternalBase helper (see below).
// Create dump policy:
// 1. Scan SEH chain, if there is a handler/filter that belongs to our
// module - assume we expect this one and hence do nothing here.
// 2. If the address of the exception is in our module - create dump.
// 3. If our module is in somewhere in callstack - create dump.
-template <class E>
-class VectoredHandlerT : public VectoredHandlerBase {
+// The E class is supposed to provide external/API functions. Using template
+// make testability easier. It shall confirm the following concept/archetype:
+//struct E {
+// void WriteDump(EXCEPTION_POINTERS* p) {
+// }
+//
+// // Used mainly to ignore exceptions from IsBadRead/Write/Ptr.
+// bool ShouldIgnoreException(const EXCEPTION_POINTERS* exptr) {
+// return 0;
+// }
+//
+// // Retrieve the SEH list head.
+// EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() {
+// return NULL;
+// }
+//
+// // Get the stack trace as correctly as possible.
+// WORD RtlCaptureStackBackTrace(DWORD FramesToSkip, DWORD FramesToCapture,
+// void** BackTrace, DWORD* BackTraceHash) {
+// return 0;
+// }
+//
+// // Check whether the stack guard page is in place.
+// bool CheckForStackOverflow(EXCEPTION_POINTERS* p) {
+// return 0;
+// }
+//
+// bool IsOurModule(const void* address) {
+// return 0;
+// }
+//};
+// The methods shall be placed in .text$veh_m
+template <typename E>
+class VectoredHandlerT {
public:
- static void* Register(const void* module_start, const void* module_end) {
- g_exceptions_seen = 0;
- g_handler = E::Register(&VectoredHandler, module_start, module_end);
- return g_handler;
- }
+ VectoredHandlerT(E* api);
+ ~VectoredHandlerT();
- static ULONG Unregister() {
- if (g_handler)
- return E::Unregister(g_handler);
- return 0;
+ // TODO(stoyan): Come with better way to skip initial stack frames.
+ FORCEINLINE LONG Handler(EXCEPTION_POINTERS* exceptionInfo);
+ long get_exceptions_seen() const {
+ return exceptions_seen_;
}
- static LONG WINAPI VectoredHandler(EXCEPTION_POINTERS* exceptionInfo);
private:
- static BOOL ModuleHasInstalledSEHFilter();
+ bool ModuleHasInstalledSEHFilter();
+ E* api_;
+ long exceptions_seen_;
};
-// Handy class supposed to act as a base class for classes used as template
-// parameter of VectoredHandlerT<E>
-class VEHTraitsBase {
- public:
- static const void* g_module_start;
- static const void* g_module_end;
+// Maintains start and end address of a single module of interest. If we want
+// do check for multiple modules, this class has to be extended to support a
+// list of modules (DLLs).
+struct ModuleOfInterest {
+ // The callback from VectoredHandlerT::Handler().
+ inline bool IsOurModule(const void* address) {
+ return (start_ <= address && address < end_);
+ }
- static inline int IsOurModule(const void* address) {
- return (g_module_start <= address && address < g_module_end);
+ // Helpers.
+ inline void SetModule(const void* module_start, const void* module_end) {
+ start_ = module_start;
+ end_ = module_end;
}
- static inline void SetModule(const void* module_start,
- const void* module_end) {
- g_module_start = module_start;
- g_module_end = module_end;
+ inline void SetCurrentModule() {
+ // Find current module boundaries.
+ const void* start = &__ImageBase;
+ const char* s = reinterpret_cast<const char*>(start);
+ const IMAGE_NT_HEADERS32* nt = reinterpret_cast<const IMAGE_NT_HEADERS32*>
+ (s + __ImageBase.e_lfanew);
+ const void* end = s + nt->OptionalHeader.SizeOfImage;
+ SetModule(start, end);
}
- static bool ShouldIgnoreException(const EXCEPTION_POINTERS* exceptionInfo) {
- return false;
+ const void* start_;
+ const void* end_;
+};
+
+struct ModuleOfInterestWithExcludedRegion : public ModuleOfInterest {
+ inline bool IsOurModule(const void* address) {
+ return (start_ <= address && address < end_) &&
+ (address < special_region_start_ || special_region_end_ <= address);
}
+
+ inline void SetExcludedRegion(const void* start, const void* end) {
+ special_region_start_ = start;
+ special_region_end_ = end;
+ }
+
+ const void* special_region_start_;
+ const void* special_region_end_;
};
-DECLSPEC_SELECTANY const void* VEHTraitsBase::g_module_start;
-DECLSPEC_SELECTANY const void* VEHTraitsBase::g_module_end;
-class Win32VEHTraits;
-typedef class VectoredHandlerT<Win32VEHTraits> VectoredHandler;
#endif // CHROME_FRAME_CRASH_REPORTING_VECTORED_HANDLER_H_
diff --git a/chrome_frame/crash_reporting/vectored_handler_unittest.cc b/chrome_frame/crash_reporting/vectored_handler_unittest.cc
index 0b87f04..3df4608 100644
--- a/chrome_frame/crash_reporting/vectored_handler_unittest.cc
+++ b/chrome_frame/crash_reporting/vectored_handler_unittest.cc
@@ -6,209 +6,180 @@
#include "base/logging.h"
#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
-#include "chrome_frame/crash_reporting/crash_report.h"
-#include "gmock/gmock.h"
+#include "chrome_frame/crash_reporting/veh_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-// Class that mocks external call from VectoredHandlerT for testing purposes.
-class EMock : public Win32VEHTraits {
+using testing::_;
+
+ACTION_P(StackTraceDump, s) {
+ memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0]));
+ return s->count_;
+}
+namespace {
+class MockApi : public Win32VEHTraits, public ModuleOfInterest {
public:
- static inline bool WriteDump(EXCEPTION_POINTERS* p) {
- g_dump_made = true;
- return true;
+ MockApi() {
+ Win32VEHTraits::InitializeIgnoredBlocks();
}
- static inline void* Register(PVECTORED_EXCEPTION_HANDLER func,
- const void* module_start,
- const void* module_end) {
- InitializeIgnoredBlocks();
- VEHTraitsBase::SetModule(module_start, module_end);
- // Return some arbitrary number, expecting to get the same on Unregister()
- return reinterpret_cast<void*>(4);
- }
+ MOCK_METHOD1(WriteDump, void(const EXCEPTION_POINTERS*));
+ MOCK_METHOD0(RtlpGetExceptionList, const EXCEPTION_REGISTRATION_RECORD*());
+ MOCK_METHOD4(RtlCaptureStackBackTrace, WORD(DWORD FramesToSkip,
+ DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash));
- static inline ULONG Unregister(void* handle) {
- EXPECT_EQ(handle, reinterpret_cast<void*>(4));
- return 1;
+ // Helpers
+ void SetSEH(const SEHChain& sehchain) {
+ EXPECT_CALL(*this, RtlpGetExceptionList())
+ .Times(testing::AnyNumber())
+ .WillRepeatedly(testing::Return(sehchain.chain_));
}
- static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip,
- DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) {
- EXPECT_EQ(2, FramesToSkip);
- EXPECT_LE(FramesToSkip + FramesToCapture,
- VectoredHandlerBase::max_back_trace);
- memcpy(BackTrace, g_stack, g_stack_entries * sizeof(BackTrace[0]));
- return g_stack_entries;
+ void SetStack(const StackHelper& s) {
+ EXPECT_CALL(*this, RtlCaptureStackBackTrace(_, _, _, _))
+ .Times(testing::AnyNumber())
+ .WillRepeatedly(StackTraceDump(&s));
}
- static inline EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() {
- return g_seh_chain;
- }
+ enum {max_back_trace = 15};
+};
+}; // namespace
- // Test helpers
- // Create fake SEH chain of random filters - with and without our module.
- static void SetHaveSEHFilter() {
- SetSEHChain(reinterpret_cast<const char*>(g_module_start) - 0x1000,
- reinterpret_cast<const char*>(g_module_start) + 0x1000,
- reinterpret_cast<const char*>(g_module_end) + 0x7127,
- NULL);
- }
+typedef VectoredHandlerT<MockApi> VectoredHandlerMock;
+TEST(ChromeFrame, ExceptionReport) {
+ MockApi api;
+ VectoredHandlerMock veh(&api);
- static void SetNoSEHFilter() {
- SetSEHChain(reinterpret_cast<const char*>(g_module_start) - 0x1000,
- reinterpret_cast<const char*>(g_module_end) + 0x7127,
- NULL);
- }
+ // Start address of our module.
+ char* s = reinterpret_cast<char*>(0x30000000);
+ char *e = s + 0x10000;
+ api.SetModule(s, e);
- // Create fake stack - with and without our module.
- static void SetOnStack() {
- SetStack(reinterpret_cast<const char*>(g_module_start) - 0x11283,
- reinterpret_cast<const char*>(g_module_start) - 0x278361,
- reinterpret_cast<const char*>(g_module_start) + 0x9171,
- reinterpret_cast<const char*>(g_module_end) + 1231,
- NULL);
- }
+ SEHChain have_seh(s - 0x1000, s + 0x1000, e + 0x7127, NULL);
+ SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
+ StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
+ StackHelper not_on_stack(s - 0x11283, s - 0x278361, e + 1231, NULL);
- static void SetNotOnStack() {
- SetStack(reinterpret_cast<const char*>(g_module_start) - 0x11283,
- reinterpret_cast<const char*>(g_module_start) - 0x278361,
- reinterpret_cast<const char*>(g_module_end) + 1231,
- NULL);
- }
+ char* our_code = s + 0x1111;
+ char* not_our_code = s - 0x5555;
+ char* not_our_code2 = e + 0x5555;
- // Populate stack array
- static void SetStack(const void* p, ...) {
- va_list vl;
- va_start(vl, p);
- g_stack_entries = 0;
- for (; p; ++g_stack_entries) {
- CHECK(g_stack_entries < arraysize(g_stack));
- g_stack[g_stack_entries] = p;
- p = va_arg(vl, const void*);
- }
- }
+ // Exception in our code, but we have SEH filter => no dump.
+ api.SetSEH(have_seh);
+ api.SetStack(on_stack);
+ EXPECT_CALL(api, WriteDump(_)).Times(0);
+ EXPECT_EQ(ExceptionContinueSearch,
+ veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, our_code)));
+ testing::Mock::VerifyAndClearExpectations(&api);
- static void SetSEHChain(const void* p, ...) {
- va_list vl;
- va_start(vl, p);
- int i = 0;
- for (; p; ++i) {
- CHECK(i + 1 < arraysize(g_seh_chain));
- g_seh_chain[i].Handler = const_cast<void*>(p);
- g_seh_chain[i].Next = &g_seh_chain[i + 1];
- p = va_arg(vl, const void*);
- }
-
- g_seh_chain[i].Next = EXCEPTION_CHAIN_END;
- }
+ // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected
+ // from its clients", shall not be caught since it's a warning only.
+ EXPECT_CALL(api, WriteDump(_)).Times(0);
+ EXPECT_EQ(ExceptionContinueSearch,
+ veh.Handler(&ExceptionInfo(RPC_E_DISCONNECTED, our_code)));
+ testing::Mock::VerifyAndClearExpectations(&api);
- static EXCEPTION_REGISTRATION_RECORD g_seh_chain[25];
- static const void* g_stack[VectoredHandlerBase::max_back_trace];
- static WORD g_stack_entries;
- static bool g_dump_made;
-};
+ // Exception, not in our code, we do not have SEH and we are not in stack.
+ api.SetSEH(no_seh);
+ api.SetStack(not_on_stack);
+ EXPECT_CALL(api, WriteDump(_)).Times(0);
+ EXPECT_EQ(ExceptionContinueSearch,
+ veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code)));
+ testing::Mock::VerifyAndClearExpectations(&api);
-EXCEPTION_REGISTRATION_RECORD EMock::g_seh_chain[25];
-const void* EMock::g_stack[VectoredHandlerBase::max_back_trace];
-WORD EMock::g_stack_entries;
-bool EMock::g_dump_made;
+ // Exception, not in our code, no SEH, but we are on the stack.
+ api.SetSEH(no_seh);
+ api.SetStack(on_stack);
+ EXPECT_CALL(api, WriteDump(_)).Times(1);
+ EXPECT_EQ(ExceptionContinueSearch,
+ veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO,
+ not_our_code2)));
+ testing::Mock::VerifyAndClearExpectations(&api);
-typedef VectoredHandlerT<EMock> VectoredHandlerMock;
+ // Exception, in our code, no SEH, not on stack (assume FPO screwed us)
+ api.SetSEH(no_seh);
+ api.SetStack(not_on_stack);
+ EXPECT_CALL(api, WriteDump(_)).Times(1);
+ EXPECT_EQ(ExceptionContinueSearch,
+ veh.Handler(&ExceptionInfo(STATUS_PRIVILEGED_INSTRUCTION, our_code)));
+ testing::Mock::VerifyAndClearExpectations(&api);
-class ExPtrsHelper : public _EXCEPTION_POINTERS {
- public:
- ExPtrsHelper() {
- ExceptionRecord = &er_;
- ContextRecord = &ctx_;
- ZeroMemory(&er_, sizeof(er_));
- ZeroMemory(&ctx_, sizeof(ctx_));
- }
+ // Exception, in IsBadStringPtrA, we are on the stack.
+ api.SetSEH(no_seh);
+ api.SetStack(on_stack);
+ EXPECT_CALL(api, WriteDump(_)).Times(0);
+ char* ignore_address = reinterpret_cast<char*>(GetProcAddress(
+ GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10;
+ EXPECT_EQ(ExceptionContinueSearch,
+ veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
+ testing::Mock::VerifyAndClearExpectations(&api);
+
+ // Exception, in IsBadStringPtrA, we are not on the stack.
+ api.SetSEH(no_seh);
+ api.SetStack(not_on_stack);
+ EXPECT_CALL(api, WriteDump(_)).Times(0);
+ EXPECT_EQ(ExceptionContinueSearch,
+ veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
+ testing::Mock::VerifyAndClearExpectations(&api);
+}
+
+MATCHER_P(ExceptionCodeIs, code, "") {
+ return (arg->ExceptionRecord->ExceptionCode == code);
+}
+
+void OverflowStack() {
+ char tmp[1024 * 2048];
+ ZeroMemory(tmp, sizeof(tmp));
+}
+
+DWORD WINAPI CrashingThread(PVOID tmp) {
+ __try {
+ OverflowStack();
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
- void Set(DWORD code, void* address, DWORD flags) {
- er_.ExceptionCode = code;
- er_.ExceptionAddress = address;
- er_.ExceptionFlags = flags;
}
- EXCEPTION_RECORD er_;
- CONTEXT ctx_;
-};
+ // This will cause STATUS_ACCESS_VIOLATION
+ __try {
+ OverflowStack();
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ }
-TEST(ChromeFrame, ExceptionReport) {
- char* s = reinterpret_cast<char*>(0x30000000);
- char* e = s + 0x10000;
- void* handler = VectoredHandlerMock::Register(s, e);
- char* our_code = s + 0x1111;
- char* not_our_code = s - 0x5555;
- char* not_our_code2 = e + 0x5555;
+ return 0;
+}
- ExPtrsHelper ex;
- // Exception in our code, but we have SEH filter
- ex.Set(STATUS_ACCESS_VIOLATION, our_code, 0);
- EMock::SetHaveSEHFilter();
- EMock::SetOnStack();
- EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex));
- EXPECT_EQ(1, VectoredHandlerMock::g_exceptions_seen);
- EXPECT_FALSE(EMock::g_dump_made);
+static VectoredHandlerMock* g_mock_veh = NULL;
+static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) {
+ return g_mock_veh->Handler(exptrs);
+}
- // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected
- // from its clients", shall not be caught since it's a warning only.
- ex.Set(RPC_E_DISCONNECTED, our_code, 0);
- EMock::SetHaveSEHFilter();
- EMock::SetOnStack();
- EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex));
- EXPECT_EQ(1, VectoredHandlerMock::g_exceptions_seen);
- EXPECT_FALSE(EMock::g_dump_made);
+TEST(ChromeFrame, TrickyStackOverflow) {
+ MockApi api;
+ VectoredHandlerMock veh(&api);
+ // Start address of our module.
+ char* s = reinterpret_cast<char*>(0x30000000);
+ char *e = s + 0x10000;
+ api.SetModule(s, e);
- // Exception, not in our code, we do not have SEH and we are not in stack.
- ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code, 0);
- EMock::SetNoSEHFilter();
- EMock::SetNotOnStack();
- EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex));
- EXPECT_EQ(2, VectoredHandlerMock::g_exceptions_seen);
- EXPECT_FALSE(EMock::g_dump_made);
+ SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
+ StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
+ api.SetSEH(no_seh);
+ api.SetStack(on_stack);
- // Exception, not in our code, no SEH, but we are on the stack.
- ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code2, 0);
- EMock::SetNoSEHFilter();
- EMock::SetOnStack();
- EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex));
- EXPECT_EQ(3, VectoredHandlerMock::g_exceptions_seen);
- EXPECT_TRUE(EMock::g_dump_made);
- EMock::g_dump_made = false;
+ EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW))).Times(1);
+ g_mock_veh = &veh;
+ void* id = ::AddVectoredExceptionHandler(FALSE, VEH);
- // Exception, in our code, no SEH, not on stack (assume FPO screwed us)
- ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, our_code, 0);
- EMock::SetNoSEHFilter();
- EMock::SetNotOnStack();
- EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex));
- EXPECT_EQ(4, VectoredHandlerMock::g_exceptions_seen);
- EXPECT_TRUE(EMock::g_dump_made);
- EMock::g_dump_made = false;
+ DWORD tid;
+ HANDLE h = ::CreateThread(0, 0, CrashingThread, 0, 0, &tid);
+ ::WaitForSingleObject(h, INFINITE);
+ ::CloseHandle(h);
- // Exception, in IsBadStringPtrA, we are on the stack.
- char* ignore_address = reinterpret_cast<char*>(GetProcAddress(
- GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10;
- ex.Set(STATUS_ACCESS_VIOLATION, ignore_address + 10, 0);
- EMock::SetNoSEHFilter();
- EMock::SetOnStack();
- EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex));
- EXPECT_EQ(5, VectoredHandlerMock::g_exceptions_seen);
- EXPECT_FALSE(EMock::g_dump_made);
- EMock::g_dump_made = false;
-
- // Exception, in IsBadStringPtrA, we are not in stack.
- ex.Set(STATUS_ACCESS_VIOLATION, ignore_address + 10, 0);
- EMock::SetNoSEHFilter();
- EMock::SetNotOnStack();
- EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex));
- EXPECT_EQ(6, VectoredHandlerMock::g_exceptions_seen);
- EXPECT_FALSE(EMock::g_dump_made);
- EMock::g_dump_made = false;
-
- VectoredHandlerMock::Unregister();
+ EXPECT_EQ(2, veh.get_exceptions_seen());
+ ::RemoveVectoredExceptionHandler(id);
+ g_mock_veh = NULL;
}
diff --git a/chrome_frame/crash_reporting/veh_test.cc b/chrome_frame/crash_reporting/veh_test.cc
new file mode 100644
index 0000000..7f844e9
--- /dev/null
+++ b/chrome_frame/crash_reporting/veh_test.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <atlbase.h>
+#include "chrome_frame/crash_reporting/veh_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
+
+#pragma code_seg(push, ".m$_0")
+static void ModuleStart() {}
+#pragma code_seg(pop)
+
+#pragma code_seg(push, ".m$_2")
+static void Undetectable(DWORD code) {
+ __try {
+ ::RaiseException(code, 0, 0, NULL);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+
+ }
+};
+#pragma code_seg(pop)
+
+#pragma code_seg(push, ".m$_3")
+static void UndetectableEnd() {}
+#pragma code_seg(pop)
+
+#pragma code_seg(push, ".m$_4")
+static void CatchThis() {
+ __try {
+ ::RaiseException(STATUS_ACCESS_VIOLATION, 0, 0, NULL);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+
+ }
+
+ // this will be detected since we are on the stack!
+ Undetectable(STATUS_ILLEGAL_INSTRUCTION);
+}
+
+#pragma code_seg(pop)
+
+#pragma code_seg(push, ".m$_9")
+static void ModuleEnd() {}
+#pragma code_seg(pop)
+
+using testing::_;
+namespace {
+MATCHER_P(ExceptionCodeIs, code, "") {
+ return (arg->ExceptionRecord->ExceptionCode == code);
+}
+
+class MockApi : public Win32VEHTraits,
+ public ModuleOfInterestWithExcludedRegion {
+ public:
+ MockApi() {
+ Win32VEHTraits::InitializeIgnoredBlocks();
+ ModuleOfInterestWithExcludedRegion::SetModule(&ModuleStart, &ModuleEnd);
+ ModuleOfInterestWithExcludedRegion::SetExcludedRegion(&Undetectable,
+ &UndetectableEnd);
+ }
+
+ MOCK_METHOD1(WriteDump, void(const EXCEPTION_POINTERS*));
+ MOCK_METHOD0(RtlpGetExceptionList, const EXCEPTION_REGISTRATION_RECORD*());
+};
+}; // namespace
+
+typedef VectoredHandlerT<MockApi> VectoredHandlerMock;
+
+static VectoredHandlerMock* g_mock_veh = NULL;
+static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) {
+ return g_mock_veh->Handler(exptrs);
+}
+
+TEST(ChromeFrame, ExceptionExcludedCode) {
+ MockApi api;
+ VectoredHandlerMock veh(&api);
+
+ g_mock_veh = &veh;
+ void* id = ::AddVectoredExceptionHandler(FALSE, VEH);
+
+ EXPECT_CALL(api, RtlpGetExceptionList())
+ .WillRepeatedly(testing::Return(EXCEPTION_CHAIN_END));
+
+ testing::Sequence s;
+
+ EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_ACCESS_VIOLATION)))
+ .Times(1);
+
+ EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_ILLEGAL_INSTRUCTION)))
+ .Times(1);
+
+ CatchThis();
+ EXPECT_EQ(2, veh.get_exceptions_seen());
+
+ // Not detected since we are not on the stack.
+ Undetectable(STATUS_INTEGER_DIVIDE_BY_ZERO);
+ EXPECT_EQ(3, veh.get_exceptions_seen());
+
+ ::RemoveVectoredExceptionHandler(id);
+ g_mock_veh = NULL;
+}
+
+
diff --git a/chrome_frame/crash_reporting/veh_test.h b/chrome_frame/crash_reporting/veh_test.h
new file mode 100644
index 0000000..3f9cd45
--- /dev/null
+++ b/chrome_frame/crash_reporting/veh_test.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef CHROME_FRAME_CRASH_REPORTING_VEH_TEST_H_
+#define CHROME_FRAME_CRASH_REPORTING_VEH_TEST_H_
+
+#include <windows.h>
+#include "base/logging.h"
+
+#ifndef EXCEPTION_CHAIN_END
+#define EXCEPTION_CHAIN_END ((struct _EXCEPTION_REGISTRATION_RECORD*)-1)
+typedef struct _EXCEPTION_REGISTRATION_RECORD {
+ struct _EXCEPTION_REGISTRATION_RECORD* Next;
+ PVOID Handler;
+} EXCEPTION_REGISTRATION_RECORD;
+#endif // EXCEPTION_CHAIN_END
+
+class ExceptionInfo : public _EXCEPTION_POINTERS {
+ public:
+ ExceptionInfo() {
+ Clear();
+ }
+
+ ExceptionInfo(DWORD code, void* address) {
+ Clear();
+ Set(code, address, 0);
+ }
+
+ void Set(DWORD code, void* address, DWORD flags) {
+ er_.ExceptionCode = code;
+ er_.ExceptionAddress = address;
+ er_.ExceptionFlags = flags;
+ ctx_.Eip = reinterpret_cast<DWORD>(address);
+ }
+
+ EXCEPTION_RECORD er_;
+ CONTEXT ctx_;
+ private:
+ void Clear() {
+ ExceptionRecord = &er_;
+ ContextRecord = &ctx_;
+ ZeroMemory(&er_, sizeof(er_));
+ ZeroMemory(&ctx_, sizeof(ctx_));
+ }
+};
+
+struct SEHChain {
+ SEHChain(const void* p, ...) {
+ va_list vl;
+ va_start(vl, p);
+ int i = 0;
+ for (; p; ++i) {
+ CHECK(i + 1 < arraysize(chain_));
+ chain_[i].Handler = const_cast<void*>(p);
+ chain_[i].Next = &chain_[i + 1];
+ p = va_arg(vl, const void*);
+ }
+
+ chain_[i].Next = EXCEPTION_CHAIN_END;
+ }
+
+ EXCEPTION_REGISTRATION_RECORD chain_[25];
+};
+
+struct StackHelper {
+ StackHelper(const void* p, ...) {
+ va_list vl;
+ va_start(vl, p);
+ count_ = 0;
+ for (; p; ++count_) {
+ CHECK(count_ < arraysize(stack_));
+ stack_[count_] = p;
+ p = va_arg(vl, const void*);
+ }
+ }
+ const void* stack_[64];
+ WORD count_;
+};
+
+#endif // CHROME_FRAME_CRASH_REPORTING_VEH_TEST_H_ \ No newline at end of file