Home | History | Annotate | Download | only in crash_reporting
      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(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 
    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 }
    229