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 dump_type_(dump_type),
100 thread_id_(0),
101 server_process_id_(0),
102 crash_event_(NULL),
103 crash_generated_(NULL),
104 server_alive_(NULL),
105 exception_pointers_(NULL),
106 custom_info_() {
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 dump_type_(dump_type),
120 thread_id_(0),
121 server_process_id_(0),
122 crash_event_(NULL),
123 crash_generated_(NULL),
124 server_alive_(NULL),
125 exception_pointers_(NULL),
126 custom_info_() {
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