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 "testing/gtest/include/gtest/gtest.h" 32#include "testing/include/gmock/gmock.h" 33 34#include "client/windows/crash_generation/crash_generation_server.h" 35#include "client/windows/common/ipc_protocol.h" 36 37using testing::_; 38 39namespace { 40 41const wchar_t kPipeName[] = 42 L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer"; 43 44const DWORD kPipeDesiredAccess = FILE_READ_DATA | 45 FILE_WRITE_DATA | 46 FILE_WRITE_ATTRIBUTES; 47 48const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | 49 SECURITY_SQOS_PRESENT; 50 51const DWORD kPipeMode = PIPE_READMODE_MESSAGE; 52 53int kCustomInfoCount = 2; 54 55google_breakpad::CustomInfoEntry kCustomInfoEntries[] = { 56 google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"), 57 google_breakpad::CustomInfoEntry(L"ver", L"1.0"), 58}; 59 60class CrashGenerationServerTest : public ::testing::Test { 61 public: 62 CrashGenerationServerTest() 63 : crash_generation_server_(kPipeName, 64 NULL, 65 CallOnClientConnected, &mock_callbacks_, 66 CallOnClientDumpRequested, &mock_callbacks_, 67 CallOnClientExited, &mock_callbacks_, 68 CallOnClientUploadRequested, &mock_callbacks_, 69 false, 70 NULL), 71 thread_id_(0), 72 exception_pointers_(NULL) { 73 memset(&assert_info_, 0, sizeof(assert_info_)); 74 } 75 76 protected: 77 class MockCrashGenerationServerCallbacks { 78 public: 79 MOCK_METHOD1(OnClientConnected, 80 void(const google_breakpad::ClientInfo* client_info)); 81 MOCK_METHOD2(OnClientDumpRequested, 82 void(const google_breakpad::ClientInfo* client_info, 83 const std::wstring* file_path)); 84 MOCK_METHOD1(OnClientExited, 85 void(const google_breakpad::ClientInfo* client_info)); 86 MOCK_METHOD1(OnClientUploadRequested, 87 void(const DWORD crash_id)); 88 }; 89 90 enum ClientFault { 91 NO_FAULT, 92 CLOSE_AFTER_CONNECT, 93 SEND_INVALID_REGISTRATION, 94 TRUNCATE_REGISTRATION, 95 CLOSE_AFTER_REGISTRATION, 96 RESPONSE_BUFFER_TOO_SMALL, 97 CLOSE_AFTER_RESPONSE, 98 SEND_INVALID_ACK 99 }; 100 101 void SetUp() { 102 ASSERT_TRUE(crash_generation_server_.Start()); 103 } 104 105 void FaultyClient(ClientFault fault_type) { 106 HANDLE pipe = CreateFile(kPipeName, 107 kPipeDesiredAccess, 108 0, 109 NULL, 110 OPEN_EXISTING, 111 kPipeFlagsAndAttributes, 112 NULL); 113 114 if (pipe == INVALID_HANDLE_VALUE) { 115 ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError()); 116 117 // Cannot continue retrying if wait on pipe fails. 118 ASSERT_TRUE(WaitNamedPipe(kPipeName, 500)); 119 120 pipe = CreateFile(kPipeName, 121 kPipeDesiredAccess, 122 0, 123 NULL, 124 OPEN_EXISTING, 125 kPipeFlagsAndAttributes, 126 NULL); 127 } 128 129 ASSERT_NE(pipe, INVALID_HANDLE_VALUE); 130 131 DWORD mode = kPipeMode; 132 ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL)); 133 134 DoFaultyClient(fault_type, pipe); 135 136 CloseHandle(pipe); 137 } 138 139 void DoTestFault(ClientFault fault) { 140 EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0); 141 ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); 142 ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); 143 ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); 144 145 EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); 146 147 ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT)); 148 149 // Slight hack. The OnClientConnected is only invoked after the ack is 150 // received by the server. At that point, the FaultyClient call has already 151 // returned. The best way to wait until the server is done handling that is 152 // to send one more ping, whose processing will be blocked by delivery of 153 // the OnClientConnected message. 154 ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); 155 } 156 157 MockCrashGenerationServerCallbacks mock_callbacks_; 158 159 private: 160 // Depends on the caller to successfully open the pipe before invocation and 161 // to close it immediately afterwards. 162 void DoFaultyClient(ClientFault fault_type, HANDLE pipe) { 163 if (fault_type == CLOSE_AFTER_CONNECT) { 164 return; 165 } 166 167 google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries, 168 kCustomInfoCount}; 169 170 google_breakpad::ProtocolMessage msg( 171 fault_type == SEND_INVALID_REGISTRATION ? 172 google_breakpad::MESSAGE_TAG_NONE : 173 google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST, 174 GetCurrentProcessId(), 175 MiniDumpNormal, 176 &thread_id_, 177 &exception_pointers_, 178 &assert_info_, 179 custom_info, 180 NULL, 181 NULL, 182 NULL); 183 184 DWORD bytes_count = 0; 185 186 ASSERT_TRUE(WriteFile(pipe, 187 &msg, 188 fault_type == TRUNCATE_REGISTRATION ? 189 sizeof(msg) / 2 : sizeof(msg), 190 &bytes_count, 191 NULL)); 192 193 if (fault_type == CLOSE_AFTER_REGISTRATION) { 194 return; 195 } 196 197 google_breakpad::ProtocolMessage reply; 198 199 if (!ReadFile(pipe, 200 &reply, 201 fault_type == RESPONSE_BUFFER_TOO_SMALL ? 202 sizeof(google_breakpad::ProtocolMessage) / 2 : 203 sizeof(google_breakpad::ProtocolMessage), 204 &bytes_count, 205 NULL)) { 206 switch (fault_type) { 207 case TRUNCATE_REGISTRATION: 208 case RESPONSE_BUFFER_TOO_SMALL: 209 case SEND_INVALID_REGISTRATION: 210 return; 211 212 default: 213 FAIL() << "Unexpectedly failed to register."; 214 } 215 } 216 217 if (fault_type == CLOSE_AFTER_RESPONSE) { 218 return; 219 } 220 221 google_breakpad::ProtocolMessage ack_msg; 222 ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK; 223 224 ASSERT_TRUE(WriteFile(pipe, 225 &ack_msg, 226 SEND_INVALID_ACK ? 227 sizeof(ack_msg) : sizeof(ack_msg) / 2, 228 &bytes_count, 229 NULL)); 230 231 return; 232 } 233 234 static void CallOnClientConnected( 235 void* context, const google_breakpad::ClientInfo* client_info) { 236 static_cast<MockCrashGenerationServerCallbacks*>(context)-> 237 OnClientConnected(client_info); 238 } 239 240 static void CallOnClientDumpRequested( 241 void* context, 242 const google_breakpad::ClientInfo* client_info, 243 const std::wstring* file_path) { 244 static_cast<MockCrashGenerationServerCallbacks*>(context)-> 245 OnClientDumpRequested(client_info, file_path); 246 } 247 248 static void CallOnClientExited( 249 void* context, const google_breakpad::ClientInfo* client_info) { 250 static_cast<MockCrashGenerationServerCallbacks*>(context)-> 251 OnClientExited(client_info); 252 } 253 254 static void CallOnClientUploadRequested(void* context, const DWORD crash_id) { 255 static_cast<MockCrashGenerationServerCallbacks*>(context)-> 256 OnClientUploadRequested(crash_id); 257 } 258 259 DWORD thread_id_; 260 EXCEPTION_POINTERS* exception_pointers_; 261 MDRawAssertionInfo assert_info_; 262 263 google_breakpad::CrashGenerationServer crash_generation_server_; 264}; 265 266TEST_F(CrashGenerationServerTest, PingServerTest) { 267 DoTestFault(CLOSE_AFTER_CONNECT); 268} 269 270TEST_F(CrashGenerationServerTest, InvalidRegistration) { 271 DoTestFault(SEND_INVALID_REGISTRATION); 272} 273 274TEST_F(CrashGenerationServerTest, TruncateRegistration) { 275 DoTestFault(TRUNCATE_REGISTRATION); 276} 277 278TEST_F(CrashGenerationServerTest, CloseAfterRegistration) { 279 DoTestFault(CLOSE_AFTER_REGISTRATION); 280} 281 282TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) { 283 DoTestFault(RESPONSE_BUFFER_TOO_SMALL); 284} 285 286TEST_F(CrashGenerationServerTest, CloseAfterResponse) { 287 DoTestFault(CLOSE_AFTER_RESPONSE); 288} 289 290// It turns out that, as long as you send one byte, the ACK is accepted and 291// registration succeeds. 292TEST_F(CrashGenerationServerTest, SendInvalidAck) { 293 EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); 294 ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK)); 295 296 // See DoTestFault for an explanation of this line 297 ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); 298 299 EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); 300 ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT)); 301 302 // See DoTestFault for an explanation of this line 303 ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); 304} 305 306} // anonymous namespace 307