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 17using testing::_; 18 19ACTION_P(StackTraceDump, s) { 20 memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0])); 21 return s->count_; 22} 23namespace { 24class 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 50typedef VectoredHandlerT<MockApi> VectoredHandlerMock; 51static VectoredHandlerMock* g_mock_veh = NULL; 52static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) { 53 return g_mock_veh->Handler(exptrs); 54} 55 56TEST(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(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(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(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 175MATCHER_P(ExceptionCodeIs, code, "") { 176 return (arg->ExceptionRecord->ExceptionCode == code); 177} 178 179DECLSPEC_NOINLINE static void OverflowStack() { 180 char tmp[1024 * 2048] = {0}; 181 GetSystemInfo(reinterpret_cast<SYSTEM_INFO*>(&tmp)); 182} 183 184DWORD 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 201TEST(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} 229