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