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           dump_type_(dump_type),
99           thread_id_(0),
100           server_process_id_(0),
101           crash_event_(NULL),
102           crash_generated_(NULL),
103           server_alive_(NULL),
104           exception_pointers_(NULL),
105           custom_info_() {
106   memset(&assert_info_, 0, sizeof(assert_info_));
107   if (custom_info) {
108     custom_info_ = *custom_info;
109   }
110 }
111 
~CrashGenerationClient()112 CrashGenerationClient::~CrashGenerationClient() {
113   if (crash_event_) {
114     CloseHandle(crash_event_);
115   }
116 
117   if (crash_generated_) {
118     CloseHandle(crash_generated_);
119   }
120 
121   if (server_alive_) {
122     CloseHandle(server_alive_);
123   }
124 }
125 
126 // Performs the registration step with the server process.
127 // The registration step involves communicating with the server
128 // via a named pipe. The client sends the following pieces of
129 // data to the server:
130 //
131 // * Message tag indicating the client is requesting registration.
132 // * Process id of the client process.
133 // * Address of a DWORD variable in the client address space
134 //   that will contain the thread id of the client thread that
135 //   caused the crash.
136 // * Address of a EXCEPTION_POINTERS* variable in the client
137 //   address space that will point to an instance of EXCEPTION_POINTERS
138 //   when the crash happens.
139 // * Address of an instance of MDRawAssertionInfo that will contain
140 //   relevant information in case of non-exception crashes like assertion
141 //   failures and pure calls.
142 //
143 // In return the client expects the following information from the server:
144 //
145 // * Message tag indicating successful registration.
146 // * Server process id.
147 // * Handle to an object that client can signal to request dump
148 //   generation from the server.
149 // * Handle to an object that client can wait on after requesting
150 //   dump generation for the server to finish dump generation.
151 // * Handle to a mutex object that client can wait on to make sure
152 //   server is still alive.
153 //
154 // If any step of the expected behavior mentioned above fails, the
155 // registration step is not considered successful and hence out-of-process
156 // dump generation service is not available.
157 //
158 // Returns true if the registration is successful; false otherwise.
Register()159 bool CrashGenerationClient::Register() {
160   HANDLE pipe = ConnectToServer();
161   if (!pipe) {
162     return false;
163   }
164 
165   bool success = RegisterClient(pipe);
166   CloseHandle(pipe);
167   return success;
168 }
169 
ConnectToServer()170 HANDLE CrashGenerationClient::ConnectToServer() {
171   HANDLE pipe = ConnectToPipe(pipe_name_.c_str(),
172                               kPipeDesiredAccess,
173                               kPipeFlagsAndAttributes);
174   if (!pipe) {
175     return NULL;
176   }
177 
178   DWORD mode = kPipeMode;
179   if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) {
180     CloseHandle(pipe);
181     pipe = NULL;
182   }
183 
184   return pipe;
185 }
186 
RegisterClient(HANDLE pipe)187 bool CrashGenerationClient::RegisterClient(HANDLE pipe) {
188   ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST,
189                       GetCurrentProcessId(),
190                       dump_type_,
191                       &thread_id_,
192                       &exception_pointers_,
193                       &assert_info_,
194                       custom_info_,
195                       NULL,
196                       NULL,
197                       NULL);
198   ProtocolMessage reply;
199   DWORD bytes_count = 0;
200   // The call to TransactNamedPipe below can be changed to a call
201   // to TransactNamedPipeDebugHelper to help repro some scenarios.
202   // For details see comments for TransactNamedPipeDebugHelper.
203   if (!TransactNamedPipe(pipe,
204                          &msg,
205                          sizeof(msg),
206                          &reply,
207                          sizeof(ProtocolMessage),
208                          &bytes_count,
209                          NULL)) {
210     return false;
211   }
212 
213   if (!ValidateResponse(reply)) {
214     return false;
215   }
216 
217   ProtocolMessage ack_msg;
218   ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK;
219 
220   if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) {
221     return false;
222   }
223   crash_event_ = reply.dump_request_handle;
224   crash_generated_ = reply.dump_generated_handle;
225   server_alive_ = reply.server_alive_handle;
226   server_process_id_ = reply.pid;
227 
228   return true;
229 }
230 
ConnectToPipe(const wchar_t * pipe_name,DWORD pipe_access,DWORD flags_attrs)231 HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name,
232                                             DWORD pipe_access,
233                                             DWORD flags_attrs) {
234   for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
235     HANDLE pipe = CreateFile(pipe_name,
236                              pipe_access,
237                              0,
238                              NULL,
239                              OPEN_EXISTING,
240                              flags_attrs,
241                              NULL);
242     if (pipe != INVALID_HANDLE_VALUE) {
243       return pipe;
244     }
245 
246     // Cannot continue retrying if error is something other than
247     // ERROR_PIPE_BUSY.
248     if (GetLastError() != ERROR_PIPE_BUSY) {
249       break;
250     }
251 
252     // Cannot continue retrying if wait on pipe fails.
253     if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
254       break;
255     }
256   }
257 
258   return NULL;
259 }
260 
ValidateResponse(const ProtocolMessage & msg) const261 bool CrashGenerationClient::ValidateResponse(
262     const ProtocolMessage& msg) const {
263   return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) &&
264          (msg.pid != 0) &&
265          (msg.dump_request_handle != NULL) &&
266          (msg.dump_generated_handle != NULL) &&
267          (msg.server_alive_handle != NULL);
268 }
269 
IsRegistered() const270 bool CrashGenerationClient::IsRegistered() const {
271   return crash_event_ != NULL;
272 }
273 
RequestDump(EXCEPTION_POINTERS * ex_info)274 bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
275   if (!IsRegistered()) {
276     return false;
277   }
278 
279   exception_pointers_ = ex_info;
280   thread_id_ = GetCurrentThreadId();
281 
282   assert_info_.line = 0;
283   assert_info_.type = 0;
284   assert_info_.expression[0] = 0;
285   assert_info_.file[0] = 0;
286   assert_info_.function[0] = 0;
287 
288   return SignalCrashEventAndWait();
289 }
290 
RequestDump(MDRawAssertionInfo * assert_info)291 bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
292   if (!IsRegistered()) {
293     return false;
294   }
295 
296   exception_pointers_ = NULL;
297 
298   if (assert_info) {
299     memcpy(&assert_info_, assert_info, sizeof(assert_info_));
300   } else {
301     memset(&assert_info_, 0, sizeof(assert_info_));
302   }
303 
304   thread_id_ = GetCurrentThreadId();
305 
306   return SignalCrashEventAndWait();
307 }
308 
SignalCrashEventAndWait()309 bool CrashGenerationClient::SignalCrashEventAndWait() {
310   assert(crash_event_);
311   assert(crash_generated_);
312   assert(server_alive_);
313 
314   // Reset the dump generated event before signaling the crash
315   // event so that the server can set the dump generated event
316   // once it is done generating the event.
317   if (!ResetEvent(crash_generated_)) {
318     return false;
319   }
320 
321   if (!SetEvent(crash_event_)) {
322     return false;
323   }
324 
325   HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_};
326 
327   DWORD result = WaitForMultipleObjects(kWaitEventCount,
328                                         wait_handles,
329                                         FALSE,
330                                         kWaitForServerTimeoutMs);
331 
332   // Crash dump was successfully generated only if the server
333   // signaled the crash generated event.
334   return result == WAIT_OBJECT_0;
335 }
336 
337 }  // namespace google_breakpad
338