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