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