OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include <atlbase.h> | |
6 | |
7 #include "base/environment.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/strings/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" | |
16 | |
17 using testing::_; | |
18 | |
19 ACTION_P(StackTraceDump, s) { | |
20 memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0])); | |
21 return s->count_; | |
22 } | |
23 namespace { | |
24 class MockApi : public Win32VEHTraits, public ModuleOfInterest { | |
25 public: | |
26 MockApi() {} | |
27 | |
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)); | |
32 | |
33 // Helpers | |
34 void SetSEH(const SEHChain& sehchain) { | |
35 EXPECT_CALL(*this, RtlpGetExceptionList()) | |
36 .Times(testing::AnyNumber()) | |
37 .WillRepeatedly(testing::Return(sehchain.chain_)); | |
38 } | |
39 | |
40 void SetStack(const StackHelper& s) { | |
41 EXPECT_CALL(*this, RtlCaptureStackBackTrace(_, _, _, _)) | |
42 .Times(testing::AnyNumber()) | |
43 .WillRepeatedly(StackTraceDump(&s)); | |
44 } | |
45 | |
46 enum {max_back_trace = 15}; | |
47 }; | |
48 }; // namespace | |
49 | |
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); | |
54 } | |
55 | |
56 TEST(ChromeFrame, ExceptionReport) { | |
57 MockApi api; | |
58 VectoredHandlerMock veh(&api); | |
59 | |
60 // Start address of our module. | |
61 char* s = reinterpret_cast<char*>(&__ImageBase); | |
62 char *e = s + 0x10000; | |
63 api.SetModule(s, e); | |
64 | |
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); | |
69 | |
70 char* our_code = s + 0x1111; | |
71 char* not_our_code = s - 0x5555; | |
72 char* not_our_code2 = e + 0x5555; | |
73 | |
74 // Exception in our code, but we have SEH filter => no dump. | |
75 api.SetSEH(have_seh); | |
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); | |
81 | |
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); | |
89 | |
90 // Exception, not in our code, we do not have SEH and we are not in stack. | |
91 api.SetSEH(no_seh); | |
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); | |
97 | |
98 // Exception, not in our code, no SEH, but we are on the stack. | |
99 api.SetSEH(no_seh); | |
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, | |
104 not_our_code2))); | |
105 testing::Mock::VerifyAndClearExpectations(&api); | |
106 | |
107 // Exception, in our code, no SEH, not on stack (assume FPO screwed us) | |
108 api.SetSEH(no_seh); | |
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); | |
114 | |
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); | |
125 | |
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); | |
133 | |
134 // Exception in a loading module, we are on the stack. | |
135 // Make sure we don't report it. | |
136 api.SetSEH(no_seh); | |
137 api.SetStack(on_stack); | |
138 EXPECT_CALL(api, WriteDump(_)).Times(0); | |
139 | |
140 g_mock_veh = &veh; | |
141 void* id = ::AddVectoredExceptionHandler(FALSE, VEH); | |
142 | |
143 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
144 EXPECT_TRUE(env->SetVar(base::WideToUTF8(kCrashOnLoadMode).c_str(), "1")); | |
145 long exceptions_seen = veh.get_exceptions_seen(); | |
146 HMODULE module = ::LoadLibrary(kCrashDllName); | |
147 EXPECT_EQ(NULL, module); | |
148 | |
149 testing::Mock::VerifyAndClearExpectations(&api); | |
150 EXPECT_EQ(exceptions_seen + 1, veh.get_exceptions_seen()); | |
151 EXPECT_TRUE(env->UnSetVar(base::WideToUTF8(kCrashOnLoadMode).c_str())); | |
152 | |
153 // Exception in an unloading module, we are on the stack/ | |
154 // Make sure we report it. | |
155 EXPECT_TRUE(env->SetVar(base::WideToUTF8(kCrashOnUnloadMode).c_str(), "1")); | |
156 | |
157 module = ::LoadLibrary(kCrashDllName); | |
158 | |
159 api.SetSEH(no_seh); | |
160 api.SetStack(on_stack); | |
161 EXPECT_CALL(api, WriteDump(_)).Times(1); | |
162 EXPECT_TRUE(module != NULL); | |
163 exceptions_seen = veh.get_exceptions_seen(); | |
164 | |
165 // Crash on unloading. | |
166 ::FreeLibrary(module); | |
167 | |
168 EXPECT_EQ(exceptions_seen + 1, veh.get_exceptions_seen()); | |
169 testing::Mock::VerifyAndClearExpectations(&api); | |
170 | |
171 ::RemoveVectoredExceptionHandler(id); | |
172 g_mock_veh = NULL; | |
173 } | |
174 | |
175 MATCHER_P(ExceptionCodeIs, code, "") { | |
176 return (arg->ExceptionRecord->ExceptionCode == code); | |
177 } | |
178 | |
179 DECLSPEC_NOINLINE static void OverflowStack() { | |
180 char tmp[1024 * 2048] = {0}; | |
181 GetSystemInfo(reinterpret_cast<SYSTEM_INFO*>(&tmp)); | |
182 } | |
183 | |
184 DWORD WINAPI CrashingThread(PVOID tmp) { | |
185 __try { | |
186 OverflowStack(); | |
187 } __except(EXCEPTION_EXECUTE_HANDLER) { | |
188 | |
189 } | |
190 | |
191 // This will cause STATUS_ACCESS_VIOLATION | |
192 __try { | |
193 OverflowStack(); | |
194 } __except(EXCEPTION_EXECUTE_HANDLER) { | |
195 | |
196 } | |
197 | |
198 return 0; | |
199 } | |
200 | |
201 TEST(ChromeFrame, TrickyStackOverflow) { | |
202 MockApi api; | |
203 VectoredHandlerMock veh(&api); | |
204 | |
205 // Start address of our module. | |
206 char* s = reinterpret_cast<char*>(0x30000000); | |
207 char *e = s + 0x10000; | |
208 api.SetModule(s, e); | |
209 | |
210 SEHChain no_seh(s - 0x1000, e + 0x7127, NULL); | |
211 StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL); | |
212 api.SetSEH(no_seh); | |
213 api.SetStack(on_stack); | |
214 | |
215 EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW))).Times(1); | |
216 | |
217 g_mock_veh = &veh; | |
218 void* id = ::AddVectoredExceptionHandler(FALSE, VEH); | |
219 | |
220 DWORD tid; | |
221 HANDLE h = ::CreateThread(0, 0, CrashingThread, 0, 0, &tid); | |
222 ::WaitForSingleObject(h, INFINITE); | |
223 ::CloseHandle(h); | |
224 | |
225 EXPECT_EQ(2, veh.get_exceptions_seen()); | |
226 ::RemoveVectoredExceptionHandler(id); | |
227 g_mock_veh = NULL; | |
228 } | |
OLD | NEW |