• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CrashGenerationClient(const wchar_t * pipe_name,MINIDUMP_TYPE dump_type,const CustomClientInfo * custom_info)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           custom_info_(),
100           dump_type_(dump_type),
101           crash_event_(NULL),
102           crash_generated_(NULL),
103           server_alive_(NULL),
104           server_process_id_(0),
105           thread_id_(0),
106           exception_pointers_(NULL) {
107   memset(&assert_info_, 0, sizeof(assert_info_));
108   if (custom_info) {
109     custom_info_ = *custom_info;
110   }
111 }
112 
CrashGenerationClient(HANDLE pipe_handle,MINIDUMP_TYPE dump_type,const CustomClientInfo * custom_info)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           custom_info_(),
120           dump_type_(dump_type),
121           crash_event_(NULL),
122           crash_generated_(NULL),
123           server_alive_(NULL),
124           server_process_id_(0),
125           thread_id_(0),
126           exception_pointers_(NULL) {
127   memset(&assert_info_, 0, sizeof(assert_info_));
128   if (custom_info) {
129     custom_info_ = *custom_info;
130   }
131 }
132 
~CrashGenerationClient()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.
Register()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 
RequestUpload(DWORD crash_id)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 
ConnectToServer()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 
RegisterClient(HANDLE pipe)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 
ConnectToPipe(const wchar_t * pipe_name,DWORD pipe_access,DWORD flags_attrs)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 
ValidateResponse(const ProtocolMessage & msg) const309 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 
IsRegistered() const318 bool CrashGenerationClient::IsRegistered() const {
319   return crash_event_ != NULL;
320 }
321 
RequestDump(EXCEPTION_POINTERS * ex_info,MDRawAssertionInfo * assert_info)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 
RequestDump(EXCEPTION_POINTERS * ex_info)340 bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
341   return RequestDump(ex_info, NULL);
342 }
343 
RequestDump(MDRawAssertionInfo * assert_info)344 bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
345   return RequestDump(NULL, assert_info);
346 }
347 
SignalCrashEventAndWait()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 
DuplicatePipeToClientProcess(const wchar_t * pipe_name,HANDLE hProcess)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