/ src / client / windows / crash_generation / crash_generation_client.cc
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