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
CrashGenerationClient(const wchar_t * pipe_name,MINIDUMP_TYPE dump_type,const CustomClientInfo * custom_info)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
CrashGenerationClient(HANDLE pipe_handle,MINIDUMP_TYPE dump_type,const CustomClientInfo * custom_info)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
~CrashGenerationClient()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.
Register()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
RequestUpload(DWORD crash_id)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
ConnectToServer()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
RegisterClient(HANDLE pipe)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
ConnectToPipe(const wchar_t * pipe_name,DWORD pipe_access,DWORD flags_attrs)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
ValidateResponse(const ProtocolMessage & msg) const312 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
IsRegistered() const321 bool CrashGenerationClient::IsRegistered() const {
322 return crash_event_ != NULL;
323 }
324
RequestDump(EXCEPTION_POINTERS * ex_info,MDRawAssertionInfo * assert_info)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
RequestDump(EXCEPTION_POINTERS * ex_info)343 bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
344 return RequestDump(ex_info, NULL);
345 }
346
RequestDump(MDRawAssertionInfo * assert_info)347 bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
348 return RequestDump(NULL, assert_info);
349 }
350
SignalCrashEventAndWait()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
DuplicatePipeToClientProcess(const wchar_t * pipe_name,HANDLE hProcess)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
409