1 // Copyright (c) 2011 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.
7 #include "base/environment.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome_frame/crash_reporting/crash_dll.h"
11 #include "chrome_frame/crash_reporting/nt_loader.h"
12 #include "chrome_frame/crash_reporting/vectored_handler-impl.h"
13 #include "chrome_frame/crash_reporting/veh_test.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
19 ACTION_P(StackTraceDump
, s
) {
20 memcpy(arg2
, s
->stack_
, s
->count_
* sizeof(s
->stack_
[0]));
24 class MockApi
: public Win32VEHTraits
, public ModuleOfInterest
{
28 MOCK_METHOD1(WriteDump
, void(const EXCEPTION_POINTERS
*));
29 MOCK_METHOD0(RtlpGetExceptionList
, const EXCEPTION_REGISTRATION_RECORD
*());
30 MOCK_METHOD4(RtlCaptureStackBackTrace
, WORD(DWORD FramesToSkip
,
31 DWORD FramesToCapture
, void** BackTrace
, DWORD
* BackTraceHash
));
34 void SetSEH(const SEHChain
& sehchain
) {
35 EXPECT_CALL(*this, RtlpGetExceptionList())
36 .Times(testing::AnyNumber())
37 .WillRepeatedly(testing::Return(sehchain
.chain_
));
40 void SetStack(const StackHelper
& s
) {
41 EXPECT_CALL(*this, RtlCaptureStackBackTrace(_
, _
, _
, _
))
42 .Times(testing::AnyNumber())
43 .WillRepeatedly(StackTraceDump(&s
));
46 enum {max_back_trace
= 15};
50 typedef VectoredHandlerT
<MockApi
> VectoredHandlerMock
;
51 static VectoredHandlerMock
* g_mock_veh
= NULL
;
52 static LONG WINAPI
VEH(EXCEPTION_POINTERS
* exptrs
) {
53 return g_mock_veh
->Handler(exptrs
);
56 TEST(ChromeFrame
, ExceptionReport
) {
58 VectoredHandlerMock
veh(&api
);
60 // Start address of our module.
61 char* s
= reinterpret_cast<char*>(&__ImageBase
);
62 char *e
= s
+ 0x10000;
65 SEHChain
have_seh(s
- 0x1000, s
+ 0x1000, e
+ 0x7127, NULL
);
66 SEHChain
no_seh(s
- 0x1000, e
+ 0x7127, NULL
);
67 StackHelper
on_stack(s
- 0x11283, s
- 0x278361, s
+ 0x9171, e
+ 1231, NULL
);
68 StackHelper
not_on_stack(s
- 0x11283, s
- 0x278361, e
+ 1231, NULL
);
70 char* our_code
= s
+ 0x1111;
71 char* not_our_code
= s
- 0x5555;
72 char* not_our_code2
= e
+ 0x5555;
74 // Exception in our code, but we have SEH filter => no dump.
76 api
.SetStack(on_stack
);
77 EXPECT_CALL(api
, WriteDump(_
)).Times(0);
78 EXPECT_EQ(ExceptionContinueSearch
,
79 veh
.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION
, our_code
)));
80 testing::Mock::VerifyAndClearExpectations(&api
);
82 // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected
83 // from its clients", shall not be caught since it's a warning only.
84 EXPECT_CALL(api
, WriteDump(_
)).Times(0);
85 EXPECT_CALL(api
, RtlpGetExceptionList()).Times(0);
86 EXPECT_EQ(ExceptionContinueSearch
,
87 veh
.Handler(&ExceptionInfo(RPC_E_DISCONNECTED
, our_code
)));
88 testing::Mock::VerifyAndClearExpectations(&api
);
90 // Exception, not in our code, we do not have SEH and we are not in stack.
92 api
.SetStack(not_on_stack
);
93 EXPECT_CALL(api
, WriteDump(_
)).Times(0);
94 EXPECT_EQ(ExceptionContinueSearch
,
95 veh
.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO
, not_our_code
)));
96 testing::Mock::VerifyAndClearExpectations(&api
);
98 // Exception, not in our code, no SEH, but we are on the stack.
100 api
.SetStack(on_stack
);
101 EXPECT_CALL(api
, WriteDump(_
)).Times(1);
102 EXPECT_EQ(ExceptionContinueSearch
,
103 veh
.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO
,
105 testing::Mock::VerifyAndClearExpectations(&api
);
107 // Exception, in our code, no SEH, not on stack (assume FPO screwed us)
109 api
.SetStack(not_on_stack
);
110 EXPECT_CALL(api
, WriteDump(_
)).Times(1);
111 EXPECT_EQ(ExceptionContinueSearch
,
112 veh
.Handler(&ExceptionInfo(STATUS_PRIVILEGED_INSTRUCTION
, our_code
)));
113 testing::Mock::VerifyAndClearExpectations(&api
);
115 // Exception, in IsBadStringPtrA, we are on the stack.
116 char* is_bad_ptr
= reinterpret_cast<char*>(GetProcAddress(
117 GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA"));
118 SEHChain
kernel32_seh(is_bad_ptr
, is_bad_ptr
+ 0x100, NULL
);
119 api
.SetSEH(kernel32_seh
);
120 api
.SetStack(on_stack
);
121 EXPECT_CALL(api
, WriteDump(_
)).Times(0);
122 EXPECT_EQ(ExceptionContinueSearch
,
123 veh
.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION
, is_bad_ptr
)));
124 testing::Mock::VerifyAndClearExpectations(&api
);
126 // Exception, in IsBadStringPtrA, we are not on the stack.
127 api
.SetSEH(kernel32_seh
);
128 api
.SetStack(not_on_stack
);
129 EXPECT_CALL(api
, WriteDump(_
)).Times(0);
130 EXPECT_EQ(ExceptionContinueSearch
,
131 veh
.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION
, is_bad_ptr
)));
132 testing::Mock::VerifyAndClearExpectations(&api
);
134 // Exception in a loading module, we are on the stack.
135 // Make sure we don't report it.
137 api
.SetStack(on_stack
);
138 EXPECT_CALL(api
, WriteDump(_
)).Times(0);
141 void* id
= ::AddVectoredExceptionHandler(FALSE
, VEH
);
143 scoped_ptr
<base::Environment
> env(base::Environment::Create());
144 EXPECT_TRUE(env
->SetVar(WideToUTF8(kCrashOnLoadMode
).c_str(), "1"));
145 long exceptions_seen
= veh
.get_exceptions_seen();
146 HMODULE module
= ::LoadLibrary(kCrashDllName
);
147 EXPECT_EQ(NULL
, module
);
149 testing::Mock::VerifyAndClearExpectations(&api
);
150 EXPECT_EQ(exceptions_seen
+ 1, veh
.get_exceptions_seen());
151 EXPECT_TRUE(env
->UnSetVar(WideToUTF8(kCrashOnLoadMode
).c_str()));
153 // Exception in an unloading module, we are on the stack/
154 // Make sure we report it.
155 EXPECT_TRUE(env
->SetVar(WideToUTF8(kCrashOnUnloadMode
).c_str(), "1"));
157 module
= ::LoadLibrary(kCrashDllName
);
160 api
.SetStack(on_stack
);
161 EXPECT_CALL(api
, WriteDump(_
)).Times(1);
162 EXPECT_TRUE(module
!= NULL
);
163 exceptions_seen
= veh
.get_exceptions_seen();
165 // Crash on unloading.
166 ::FreeLibrary(module
);
168 EXPECT_EQ(exceptions_seen
+ 1, veh
.get_exceptions_seen());
169 testing::Mock::VerifyAndClearExpectations(&api
);
171 ::RemoveVectoredExceptionHandler(id
);
175 MATCHER_P(ExceptionCodeIs
, code
, "") {
176 return (arg
->ExceptionRecord
->ExceptionCode
== code
);
179 DECLSPEC_NOINLINE
static void OverflowStack() {
180 char tmp
[1024 * 2048] = {0};
181 GetSystemInfo(reinterpret_cast<SYSTEM_INFO
*>(&tmp
));
184 DWORD WINAPI
CrashingThread(PVOID tmp
) {
187 } __except(EXCEPTION_EXECUTE_HANDLER
) {
191 // This will cause STATUS_ACCESS_VIOLATION
194 } __except(EXCEPTION_EXECUTE_HANDLER
) {
201 TEST(ChromeFrame
, TrickyStackOverflow
) {
203 VectoredHandlerMock
veh(&api
);
205 // Start address of our module.
206 char* s
= reinterpret_cast<char*>(0x30000000);
207 char *e
= s
+ 0x10000;
210 SEHChain
no_seh(s
- 0x1000, e
+ 0x7127, NULL
);
211 StackHelper
on_stack(s
- 0x11283, s
- 0x278361, s
+ 0x9171, e
+ 1231, NULL
);
213 api
.SetStack(on_stack
);
215 EXPECT_CALL(api
, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW
))).Times(1);
218 void* id
= ::AddVectoredExceptionHandler(FALSE
, VEH
);
221 HANDLE h
= ::CreateThread(0, 0, CrashingThread
, 0, 0, &tid
);
222 ::WaitForSingleObject(h
, INFINITE
);
225 EXPECT_EQ(2, veh
.get_exceptions_seen());
226 ::RemoveVectoredExceptionHandler(id
);