1 // Copyright 2010, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 
31 #include "breakpad_googletest_includes.h"
32 #include "client/windows/crash_generation/crash_generation_server.h"
33 #include "client/windows/common/ipc_protocol.h"
34 
35 using testing::_;
36 
37 namespace {
38 
39 const wchar_t kPipeName[] =
40   L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer";
41 
42 const DWORD kPipeDesiredAccess = FILE_READ_DATA |
43                                  FILE_WRITE_DATA |
44                                  FILE_WRITE_ATTRIBUTES;
45 
46 const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
47                                       SECURITY_SQOS_PRESENT;
48 
49 const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
50 
51 #define arraysize(f) (sizeof(f) / sizeof(*f))
52 const google_breakpad::CustomInfoEntry kCustomInfoEntries[] = {
53     google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"),
54     google_breakpad::CustomInfoEntry(L"ver", L"1.0"),
55 };
56 
57 class CrashGenerationServerTest : public ::testing::Test {
58  public:
CrashGenerationServerTest()59   CrashGenerationServerTest()
60       : crash_generation_server_(kPipeName,
61                                  NULL,
62                                  CallOnClientConnected, &mock_callbacks_,
63                                  CallOnClientDumpRequested, &mock_callbacks_,
64                                  CallOnClientExited, &mock_callbacks_,
65                                  CallOnClientUploadRequested, &mock_callbacks_,
66                                  false,
67                                  NULL),
68         thread_id_(0),
69         exception_pointers_(NULL) {
70     memset(&assert_info_, 0, sizeof(assert_info_));
71   }
72 
73  protected:
74   class MockCrashGenerationServerCallbacks {
75    public:
76     MOCK_METHOD1(OnClientConnected,
77                  void(const google_breakpad::ClientInfo* client_info));
78     MOCK_METHOD2(OnClientDumpRequested,
79                  void(const google_breakpad::ClientInfo* client_info,
80                       const std::wstring* file_path));
81     MOCK_METHOD1(OnClientExited,
82                  void(const google_breakpad::ClientInfo* client_info));
83     MOCK_METHOD1(OnClientUploadRequested,
84                  void(const DWORD crash_id));
85   };
86 
87   enum ClientFault {
88     NO_FAULT,
89     CLOSE_AFTER_CONNECT,
90     SEND_INVALID_REGISTRATION,
91     TRUNCATE_REGISTRATION,
92     CLOSE_AFTER_REGISTRATION,
93     RESPONSE_BUFFER_TOO_SMALL,
94     CLOSE_AFTER_RESPONSE,
95     SEND_INVALID_ACK
96   };
97 
SetUp()98   void SetUp() {
99     ASSERT_TRUE(crash_generation_server_.Start());
100   }
101 
FaultyClient(ClientFault fault_type)102   void FaultyClient(ClientFault fault_type) {
103     HANDLE pipe = CreateFile(kPipeName,
104                              kPipeDesiredAccess,
105                              0,
106                              NULL,
107                              OPEN_EXISTING,
108                              kPipeFlagsAndAttributes,
109                              NULL);
110 
111     if (pipe == INVALID_HANDLE_VALUE) {
112       ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError());
113 
114       // Cannot continue retrying if wait on pipe fails.
115       ASSERT_TRUE(WaitNamedPipe(kPipeName, 500));
116 
117       pipe = CreateFile(kPipeName,
118                         kPipeDesiredAccess,
119                         0,
120                         NULL,
121                         OPEN_EXISTING,
122                         kPipeFlagsAndAttributes,
123                         NULL);
124     }
125 
126     ASSERT_NE(pipe, INVALID_HANDLE_VALUE);
127 
128     DWORD mode = kPipeMode;
129     ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL));
130 
131     DoFaultyClient(fault_type, pipe);
132 
133     CloseHandle(pipe);
134   }
135 
DoTestFault(ClientFault fault)136   void DoTestFault(ClientFault fault) {
137     EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0);
138     ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
139     ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
140     ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
141 
142     EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
143 
144     ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
145 
146     // Slight hack. The OnClientConnected is only invoked after the ack is
147     // received by the server. At that point, the FaultyClient call has already
148     // returned. The best way to wait until the server is done handling that is
149     // to send one more ping, whose processing will be blocked by delivery of
150     // the OnClientConnected message.
151     ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
152   }
153 
154   MockCrashGenerationServerCallbacks mock_callbacks_;
155 
156  private:
157   // Depends on the caller to successfully open the pipe before invocation and
158   // to close it immediately afterwards.
DoFaultyClient(ClientFault fault_type,HANDLE pipe)159   void DoFaultyClient(ClientFault fault_type, HANDLE pipe) {
160     if (fault_type == CLOSE_AFTER_CONNECT) {
161       return;
162     }
163 
164     google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries,
165                                                      arraysize(kCustomInfoEntries)};
166 
167     google_breakpad::ProtocolMessage msg(
168       fault_type == SEND_INVALID_REGISTRATION ?
169         google_breakpad::MESSAGE_TAG_NONE :
170         google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST,
171       GetCurrentProcessId(),
172       MiniDumpNormal,
173       &thread_id_,
174       &exception_pointers_,
175       &assert_info_,
176       custom_info,
177       NULL,
178       NULL,
179       NULL);
180 
181     DWORD bytes_count = 0;
182 
183     ASSERT_TRUE(WriteFile(pipe,
184                           &msg,
185                           fault_type == TRUNCATE_REGISTRATION ?
186                             sizeof(msg) / 2 : sizeof(msg),
187                           &bytes_count,
188                           NULL));
189 
190     if (fault_type == CLOSE_AFTER_REGISTRATION) {
191       return;
192     }
193 
194     google_breakpad::ProtocolMessage reply;
195 
196     if (!ReadFile(pipe,
197                   &reply,
198                   fault_type == RESPONSE_BUFFER_TOO_SMALL ?
199                     sizeof(google_breakpad::ProtocolMessage) / 2 :
200                     sizeof(google_breakpad::ProtocolMessage),
201                   &bytes_count,
202                   NULL)) {
203       switch (fault_type) {
204         case TRUNCATE_REGISTRATION:
205         case RESPONSE_BUFFER_TOO_SMALL:
206         case SEND_INVALID_REGISTRATION:
207           return;
208 
209         default:
210           FAIL() << "Unexpectedly failed to register.";
211       }
212     }
213 
214     if (fault_type == CLOSE_AFTER_RESPONSE) {
215       return;
216     }
217 
218     google_breakpad::ProtocolMessage ack_msg;
219     ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK;
220 
221     ASSERT_TRUE(WriteFile(pipe,
222                           &ack_msg,
223                           SEND_INVALID_ACK ?
224                             sizeof(ack_msg) : sizeof(ack_msg) / 2,
225                           &bytes_count,
226                           NULL));
227 
228     return;
229   }
230 
CallOnClientConnected(void * context,const google_breakpad::ClientInfo * client_info)231   static void CallOnClientConnected(
232     void* context, const google_breakpad::ClientInfo* client_info) {
233     static_cast<MockCrashGenerationServerCallbacks*>(context)->
234       OnClientConnected(client_info);
235   }
236 
CallOnClientDumpRequested(void * context,const google_breakpad::ClientInfo * client_info,const std::wstring * file_path)237   static void CallOnClientDumpRequested(
238     void* context,
239     const google_breakpad::ClientInfo* client_info,
240     const std::wstring* file_path) {
241     static_cast<MockCrashGenerationServerCallbacks*>(context)->
242       OnClientDumpRequested(client_info, file_path);
243   }
244 
CallOnClientExited(void * context,const google_breakpad::ClientInfo * client_info)245   static void CallOnClientExited(
246     void* context, const google_breakpad::ClientInfo* client_info) {
247     static_cast<MockCrashGenerationServerCallbacks*>(context)->
248       OnClientExited(client_info);
249   }
250 
CallOnClientUploadRequested(void * context,const DWORD crash_id)251   static void CallOnClientUploadRequested(void* context, const DWORD crash_id) {
252     static_cast<MockCrashGenerationServerCallbacks*>(context)->
253       OnClientUploadRequested(crash_id);
254   }
255 
256   DWORD thread_id_;
257   EXCEPTION_POINTERS* exception_pointers_;
258   MDRawAssertionInfo assert_info_;
259 
260   google_breakpad::CrashGenerationServer crash_generation_server_;
261 };
262 
TEST_F(CrashGenerationServerTest,PingServerTest)263 TEST_F(CrashGenerationServerTest, PingServerTest) {
264   DoTestFault(CLOSE_AFTER_CONNECT);
265 }
266 
TEST_F(CrashGenerationServerTest,InvalidRegistration)267 TEST_F(CrashGenerationServerTest, InvalidRegistration) {
268   DoTestFault(SEND_INVALID_REGISTRATION);
269 }
270 
TEST_F(CrashGenerationServerTest,TruncateRegistration)271 TEST_F(CrashGenerationServerTest, TruncateRegistration) {
272   DoTestFault(TRUNCATE_REGISTRATION);
273 }
274 
TEST_F(CrashGenerationServerTest,CloseAfterRegistration)275 TEST_F(CrashGenerationServerTest, CloseAfterRegistration) {
276   DoTestFault(CLOSE_AFTER_REGISTRATION);
277 }
278 
TEST_F(CrashGenerationServerTest,ResponseBufferTooSmall)279 TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) {
280   DoTestFault(RESPONSE_BUFFER_TOO_SMALL);
281 }
282 
TEST_F(CrashGenerationServerTest,CloseAfterResponse)283 TEST_F(CrashGenerationServerTest, CloseAfterResponse) {
284   DoTestFault(CLOSE_AFTER_RESPONSE);
285 }
286 
287 // It turns out that, as long as you send one byte, the ACK is accepted and
288 // registration succeeds.
TEST_F(CrashGenerationServerTest,SendInvalidAck)289 TEST_F(CrashGenerationServerTest, SendInvalidAck) {
290   EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
291   ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK));
292 
293   // See DoTestFault for an explanation of this line
294   ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
295 
296   EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
297   ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
298 
299   // See DoTestFault for an explanation of this line
300   ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
301 }
302 
303 }  // anonymous namespace
304