| 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 |