1 // Copyright (c) 2008, 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 #include "client/windows/crash_generation/crash_generation_client.h" 31 #include <cassert> 32 #include <utility> 33 #include "client/windows/common/ipc_protocol.h" 34 35 namespace google_breakpad { 36 37 const int kPipeBusyWaitTimeoutMs = 2000; 38 39 #ifdef _DEBUG 40 const DWORD kWaitForServerTimeoutMs = INFINITE; 41 #else 42 const DWORD kWaitForServerTimeoutMs = 15000; 43 #endif 44 45 const int kPipeConnectMaxAttempts = 2; 46 47 const DWORD kPipeDesiredAccess = FILE_READ_DATA | 48 FILE_WRITE_DATA | 49 FILE_WRITE_ATTRIBUTES; 50 51 const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | 52 SECURITY_SQOS_PRESENT; 53 54 const DWORD kPipeMode = PIPE_READMODE_MESSAGE; 55 56 const size_t kWaitEventCount = 2; 57 58 // This function is orphan for production code. It can be used 59 // for debugging to help repro some scenarios like the client 60 // is slow in writing to the pipe after connecting, the client 61 // is slow in reading from the pipe after writing, etc. The parameter 62 // overlapped below is not used and it is present to match the signature 63 // of this function to TransactNamedPipe Win32 API. Uncomment if needed 64 // for debugging. 65 /** 66 static bool TransactNamedPipeDebugHelper(HANDLE pipe, 67 const void* in_buffer, 68 DWORD in_size, 69 void* out_buffer, 70 DWORD out_size, 71 DWORD* bytes_count, 72 LPOVERLAPPED) { 73 // Uncomment the next sleep to create a gap before writing 74 // to pipe. 75 // Sleep(5000); 76 77 if (!WriteFile(pipe, 78 in_buffer, 79 in_size, 80 bytes_count, 81 NULL)) { 82 return false; 83 } 84 85 // Uncomment the next sleep to create a gap between write 86 // and read. 87 // Sleep(5000); 88 89 return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE; 90 } 91 **/ 92 93 CrashGenerationClient::CrashGenerationClient( 94 const wchar_t* pipe_name, 95 MINIDUMP_TYPE dump_type, 96 const CustomClientInfo* custom_info) 97 : pipe_name_(pipe_name), 98 pipe_handle_(NULL), 99 dump_type_(dump_type), 100 thread_id_(0), 101 server_process_id_(0), 102 crash_event_(NULL), 103 crash_generated_(NULL), 104 server_alive_(NULL), 105 exception_pointers_(NULL), 106 custom_info_() { 107 memset(&assert_info_, 0, sizeof(assert_info_)); 108 if (custom_info) { 109 custom_info_ = *custom_info; 110 } 111 } 112 113 CrashGenerationClient::CrashGenerationClient( 114 HANDLE pipe_handle, 115 MINIDUMP_TYPE dump_type, 116 const CustomClientInfo* custom_info) 117 : pipe_name_(), 118 pipe_handle_(pipe_handle), 119 dump_type_(dump_type), 120 thread_id_(0), 121 server_process_id_(0), 122 crash_event_(NULL), 123 crash_generated_(NULL), 124 server_alive_(NULL), 125 exception_pointers_(NULL), 126 custom_info_() { 127 memset(&assert_info_, 0, sizeof(assert_info_)); 128 if (custom_info) { 129 custom_info_ = *custom_info; 130 } 131 } 132 133 CrashGenerationClient::~CrashGenerationClient() { 134 if (crash_event_) { 135 CloseHandle(crash_event_); 136 } 137 138 if (crash_generated_) { 139 CloseHandle(crash_generated_); 140 } 141 142 if (server_alive_) { 143 CloseHandle(server_alive_); 144 } 145 } 146 147 // Performs the registration step with the server process. 148 // The registration step involves communicating with the server 149 // via a named pipe. The client sends the following pieces of 150 // data to the server: 151 // 152 // * Message tag indicating the client is requesting registration. 153 // * Process id of the client process. 154 // * Address of a DWORD variable in the client address space 155 // that will contain the thread id of the client thread that 156 // caused the crash. 157 // * Address of a EXCEPTION_POINTERS* variable in the client 158 // address space that will point to an instance of EXCEPTION_POINTERS 159 // when the crash happens. 160 // * Address of an instance of MDRawAssertionInfo that will contain 161 // relevant information in case of non-exception crashes like assertion 162 // failures and pure calls. 163 // 164 // In return the client expects the following information from the server: 165 // 166 // * Message tag indicating successful registration. 167 // * Server process id. 168 // * Handle to an object that client can signal to request dump 169 // generation from the server. 170 // * Handle to an object that client can wait on after requesting 171 // dump generation for the server to finish dump generation. 172 // * Handle to a mutex object that client can wait on to make sure 173 // server is still alive. 174 // 175 // If any step of the expected behavior mentioned above fails, the 176 // registration step is not considered successful and hence out-of-process 177 // dump generation service is not available. 178 // 179 // Returns true if the registration is successful; false otherwise. 180 bool CrashGenerationClient::Register() { 181 if (IsRegistered()) { 182 return true; 183 } 184 185 HANDLE pipe = ConnectToServer(); 186 if (!pipe) { 187 return false; 188 } 189 190 bool success = RegisterClient(pipe); 191 CloseHandle(pipe); 192 return success; 193 } 194 195 bool CrashGenerationClient::RequestUpload(DWORD crash_id) { 196 HANDLE pipe = ConnectToServer(); 197 if (!pipe) { 198 return false; 199 } 200 201 CustomClientInfo custom_info = {NULL, 0}; 202 ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id, 203 static_cast<MINIDUMP_TYPE>(NULL), NULL, NULL, NULL, 204 custom_info, NULL, NULL, NULL); 205 DWORD bytes_count = 0; 206 bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0; 207 208 CloseHandle(pipe); 209 return success; 210 } 211 212 HANDLE CrashGenerationClient::ConnectToServer() { 213 HANDLE pipe = ConnectToPipe(pipe_name_.c_str(), 214 kPipeDesiredAccess, 215 kPipeFlagsAndAttributes); 216 if (!pipe) { 217 return NULL; 218 } 219 220 DWORD mode = kPipeMode; 221 if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) { 222 CloseHandle(pipe); 223 pipe = NULL; 224 } 225 226 return pipe; 227 } 228 229 bool CrashGenerationClient::RegisterClient(HANDLE pipe) { 230 ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST, 231 GetCurrentProcessId(), 232 dump_type_, 233 &thread_id_, 234 &exception_pointers_, 235 &assert_info_, 236 custom_info_, 237 NULL, 238 NULL, 239 NULL); 240 ProtocolMessage reply; 241 DWORD bytes_count = 0; 242 // The call to TransactNamedPipe below can be changed to a call 243 // to TransactNamedPipeDebugHelper to help repro some scenarios. 244 // For details see comments for TransactNamedPipeDebugHelper. 245 if (!TransactNamedPipe(pipe, 246 &msg, 247 sizeof(msg), 248 &reply, 249 sizeof(ProtocolMessage), 250 &bytes_count, 251 NULL)) { 252 return false; 253 } 254 255 if (!ValidateResponse(reply)) { 256 return false; 257 } 258 259 ProtocolMessage ack_msg; 260 ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK; 261 262 if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) { 263 return false; 264 } 265 crash_event_ = reply.dump_request_handle; 266 crash_generated_ = reply.dump_generated_handle; 267 server_alive_ = reply.server_alive_handle; 268 server_process_id_ = reply.id; 269 270 return true; 271 } 272 273 HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name, 274 DWORD pipe_access, 275 DWORD flags_attrs) { 276 if (pipe_handle_) { 277 HANDLE t = pipe_handle_; 278 pipe_handle_ = NULL; 279 return t; 280 } 281 282 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { 283 HANDLE pipe = CreateFile(pipe_name, 284 pipe_access, 285 0, 286 NULL, 287 OPEN_EXISTING, 288 flags_attrs, 289 NULL); 290 if (pipe != INVALID_HANDLE_VALUE) { 291 return pipe; 292 } 293 294 // Cannot continue retrying if error is something other than 295 // ERROR_PIPE_BUSY. 296 if (GetLastError() != ERROR_PIPE_BUSY) { 297 break; 298 } 299 300 // Cannot continue retrying if wait on pipe fails. 301 if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { 302 break; 303 } 304 } 305 306 return NULL; 307 } 308 309 bool CrashGenerationClient::ValidateResponse( 310 const ProtocolMessage& msg) const { 311 return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) && 312 (msg.id != 0) && 313 (msg.dump_request_handle != NULL) && 314 (msg.dump_generated_handle != NULL) && 315 (msg.server_alive_handle != NULL); 316 } 317 318 bool CrashGenerationClient::IsRegistered() const { 319 return crash_event_ != NULL; 320 } 321 322 bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info, 323 MDRawAssertionInfo* assert_info) { 324 if (!IsRegistered()) { 325 return false; 326 } 327 328 exception_pointers_ = ex_info; 329 thread_id_ = GetCurrentThreadId(); 330 331 if (assert_info) { 332 memcpy(&assert_info_, assert_info, sizeof(assert_info_)); 333 } else { 334 memset(&assert_info_, 0, sizeof(assert_info_)); 335 } 336 337 return SignalCrashEventAndWait(); 338 } 339 340 bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) { 341 return RequestDump(ex_info, NULL); 342 } 343 344 bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) { 345 return RequestDump(NULL, assert_info); 346 } 347 348 bool CrashGenerationClient::SignalCrashEventAndWait() { 349 assert(crash_event_); 350 assert(crash_generated_); 351 assert(server_alive_); 352 353 // Reset the dump generated event before signaling the crash 354 // event so that the server can set the dump generated event 355 // once it is done generating the event. 356 if (!ResetEvent(crash_generated_)) { 357 return false; 358 } 359 360 if (!SetEvent(crash_event_)) { 361 return false; 362 } 363 364 HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_}; 365 366 DWORD result = WaitForMultipleObjects(kWaitEventCount, 367 wait_handles, 368 FALSE, 369 kWaitForServerTimeoutMs); 370 371 // Crash dump was successfully generated only if the server 372 // signaled the crash generated event. 373 return result == WAIT_OBJECT_0; 374 } 375 376 HANDLE CrashGenerationClient::DuplicatePipeToClientProcess(const wchar_t* pipe_name, 377 HANDLE hProcess) { 378 for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { 379 HANDLE local_pipe = CreateFile(pipe_name, kPipeDesiredAccess, 380 0, NULL, OPEN_EXISTING, 381 kPipeFlagsAndAttributes, NULL); 382 if (local_pipe != INVALID_HANDLE_VALUE) { 383 HANDLE remotePipe = INVALID_HANDLE_VALUE; 384 if (DuplicateHandle(GetCurrentProcess(), local_pipe, 385 hProcess, &remotePipe, 0, FALSE, 386 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { 387 return remotePipe; 388 } else { 389 return INVALID_HANDLE_VALUE; 390 } 391 } 392 393 // Cannot continue retrying if the error wasn't a busy pipe. 394 if (GetLastError() != ERROR_PIPE_BUSY) { 395 return INVALID_HANDLE_VALUE; 396 } 397 398 if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { 399 return INVALID_HANDLE_VALUE; 400 } 401 } 402 return INVALID_HANDLE_VALUE; 403 } 404 405 } // namespace google_breakpad 406