OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "handler/win/registration_server.h" | |
16 | |
17 #include <string.h> | |
18 #include <vector> | |
19 | |
20 #include "base/logging.h" | |
21 #include "base/memory/scoped_ptr.h" | |
22 #include "base/strings/utf_string_conversions.h" | |
23 #include "client/registration_protocol_win.h" | |
24 #include "util/stdlib/pointer_container.h" | |
25 | |
26 namespace crashpad { | |
27 | |
28 namespace { | |
29 | |
30 // Invokes GetNamedPipeClientProcessId if available (Vista+). Returns true if | |
31 // the method is unavailable or completes successfully. Returns false if an | |
32 // error occurs. | |
33 // If the method is unavailable process_id will be set to 0. | |
34 bool TryGetNamedPipeClientProcessId(HANDLE pipe, DWORD* process_id) { | |
35 bool result = false; | |
36 | |
37 HMODULE kernel_dll = LoadLibrary(L"kernel32.dll"); | |
38 if (kernel_dll) { | |
39 decltype(GetNamedPipeClientProcessId)* proc = | |
40 reinterpret_cast<decltype(GetNamedPipeClientProcessId)*>( | |
41 GetProcAddress(kernel_dll, "GetNamedPipeClientProcessId")); | |
42 if (!proc) { | |
43 *process_id = 0; | |
44 result = true; | |
45 } else { | |
46 result = proc(pipe, process_id); | |
47 } | |
48 FreeLibrary(kernel_dll); | |
49 } | |
50 return result; | |
51 } | |
52 | |
53 // Implements the state and state transitions for a single pipe instance. | |
54 class PipeState { | |
55 public: | |
56 // Instantiates an instance that will operate on |pipe| and handle requests | |
57 // using |delegate|. The client must call Initialize() before clients may | |
58 // connect to this pipe. | |
59 PipeState(ScopedFileHANDLE pipe, RegistrationServer::Delegate* delegate); | |
60 ~PipeState(); | |
61 | |
62 // Places the pipe in the running state, ready to receive and process client | |
63 // connections. Returns true if successful, in which case the client must | |
64 // observe completion_event() and invoke OnCompletion() whenever it is | |
65 // signaled. | |
66 // Before destroying a running PipeState instance you must invoke Stop() and | |
67 // then wait for completion_event() to be signaled one last time. | |
68 bool Initialize(); | |
69 | |
70 // Cancels any pending asynchronous operations. After invoking this method you | |
71 // must wait for completion_event() to be signaled before destroying the | |
72 // instance. | |
73 void Stop(); | |
74 | |
75 // Returns an event handle that will be signaled whenever an asynchronous | |
76 // operation associated with this instance completes. | |
77 HANDLE completion_event() { return event_.get(); } | |
78 | |
79 // Must be called by the client whenever completion_event() is signaled. | |
80 // Returns true if the pipe is still in the running state. Otherwise, a | |
81 // permanent failure has occurred and the instance may be immediately | |
82 // destroyed. | |
83 bool OnCompletion(); | |
84 | |
85 private: | |
86 typedef bool (PipeState::*AsyncCompletionHandler)(DWORD bytes_transferred); | |
87 | |
88 // State transition handlers. Return true if the pipe is still valid. | |
89 | |
90 bool OnConnectComplete(DWORD /* bytes_transferred */); | |
91 bool OnReadComplete(DWORD bytes_transferred); | |
92 bool OnWriteComplete(DWORD bytes_transferred); | |
93 | |
94 // Pipe operations. Return true if the pipe is still valid. | |
95 | |
96 // Prepares the pipe to accept a new client connecion. | |
97 bool IssueConnect(); | |
98 // Reads into |request_|. | |
99 bool IssueRead(); | |
100 // Writes from |response_|. | |
101 bool IssueWrite(); | |
102 // Processes |request_| using |delegate_| and stores the result in | |
103 // |response_|. | |
104 bool HandleRequest(); | |
105 // Closes the active connection and invokes IssueConnect(). | |
106 bool ResetConnection(); | |
107 | |
108 RegistrationRequest request_; | |
109 RegistrationResponse response_; | |
110 // The state transition handler to be invoked when the active asynchronous | |
111 // operation completes. | |
112 AsyncCompletionHandler completion_handler_; | |
113 OVERLAPPED overlapped_; | |
114 ScopedKernelHANDLE event_; | |
115 ScopedFileHANDLE pipe_; | |
116 RegistrationServer::Delegate* delegate_; | |
117 | |
118 DISALLOW_COPY_AND_ASSIGN(PipeState); | |
119 }; | |
120 | |
121 PipeState::PipeState(ScopedFileHANDLE pipe, | |
122 RegistrationServer::Delegate* delegate) | |
123 : request_(), | |
124 response_(), | |
125 completion_handler_(nullptr), | |
126 overlapped_(), | |
127 event_(), | |
128 pipe_(pipe.Pass()), | |
129 delegate_(delegate) { | |
130 } | |
131 | |
132 PipeState::~PipeState() { | |
133 } | |
134 | |
135 bool PipeState::Initialize() { | |
136 DCHECK(!event_.is_valid()); | |
137 DCHECK(pipe_.is_valid()); | |
138 | |
139 event_.reset(CreateEvent(nullptr, true, false, nullptr)); | |
140 | |
141 if (!event_.is_valid()) { | |
142 PLOG(ERROR) << "CreateEvent"; | |
143 } else { | |
144 overlapped_.hEvent = event_.get(); | |
145 if (IssueConnect()) | |
146 return true; | |
147 } | |
148 | |
149 overlapped_.hEvent = nullptr; | |
150 event_.reset(); | |
151 pipe_.reset(); | |
152 completion_handler_ = nullptr; | |
153 | |
154 return false; | |
155 } | |
156 | |
157 void PipeState::Stop() { | |
158 DCHECK(pipe_.is_valid()); | |
159 if (!CancelIo(pipe_.get())) | |
160 PLOG(FATAL) << "CancelIo"; | |
161 } | |
162 | |
163 bool PipeState::OnCompletion() { | |
164 AsyncCompletionHandler completion_handler = completion_handler_; | |
165 completion_handler_ = nullptr; | |
166 | |
167 DWORD bytes_transferred = 0; | |
168 BOOL success = GetOverlappedResult(pipe_.get(), | |
169 &overlapped_, | |
170 &bytes_transferred, | |
171 false); // Do not wait. | |
172 if (!success) | |
173 PLOG(ERROR) << "GetOverlappedResult"; | |
174 | |
175 bool still_running = false; | |
176 if (!ResetEvent(event_.get())) { | |
177 PLOG(ERROR) << "ResetEvent"; | |
178 } else if (!completion_handler) { | |
179 NOTREACHED(); | |
180 still_running = ResetConnection(); | |
181 } else if (!success) { | |
182 still_running = ResetConnection(); | |
183 } else { | |
184 still_running = (this->*completion_handler)(bytes_transferred); | |
185 } | |
186 | |
187 if (!still_running) { | |
188 overlapped_.hEvent = nullptr; | |
189 event_.reset(); | |
190 pipe_.reset(); | |
191 completion_handler_ = nullptr; | |
192 } else { | |
193 DCHECK(completion_handler_); | |
194 } | |
195 | |
196 return still_running; | |
197 } | |
198 | |
199 bool PipeState::OnConnectComplete(DWORD /* bytes_transferred */) { | |
200 return IssueRead(); | |
201 } | |
202 | |
203 bool PipeState::OnReadComplete(DWORD bytes_transferred) { | |
204 if (bytes_transferred != sizeof(request_)) { | |
205 LOG(ERROR) << "Invalid message size: " << bytes_transferred; | |
206 return ResetConnection(); | |
207 } else { | |
208 return HandleRequest(); | |
209 } | |
210 } | |
211 | |
212 bool PipeState::OnWriteComplete(DWORD bytes_transferred) { | |
213 if (bytes_transferred != sizeof(response_)) { | |
214 LOG(ERROR) << "Incomplete write operation. Bytes written: " | |
215 << bytes_transferred; | |
216 } | |
217 return ResetConnection(); | |
218 } | |
219 | |
220 bool PipeState::IssueConnect() { | |
221 if (ConnectNamedPipe(pipe_.get(), &overlapped_)) { | |
222 return OnConnectComplete(0); // bytes_transferred (ignored) | |
223 } else { | |
224 DWORD result = GetLastError(); | |
225 if (result == ERROR_PIPE_CONNECTED) { | |
226 return OnConnectComplete(0); // bytes_transferred (ignored) | |
227 } else if (result == ERROR_IO_PENDING) { | |
228 completion_handler_ = &PipeState::OnConnectComplete; | |
229 return true; | |
230 } else { | |
231 PLOG(ERROR) << "ConnectNamedPipe"; | |
232 return false; | |
233 } | |
234 } | |
235 } | |
236 | |
237 bool PipeState::IssueRead() { | |
238 DWORD bytes_read = 0; | |
239 if (ReadFile(pipe_.get(), | |
240 &request_, | |
241 sizeof(request_), | |
242 &bytes_read, | |
243 &overlapped_)) { | |
244 return OnReadComplete(bytes_read); | |
245 } else if (GetLastError() == ERROR_IO_PENDING) { | |
246 completion_handler_ = &PipeState::OnReadComplete; | |
247 return true; | |
248 } else { | |
249 PLOG(ERROR) << "ReadFile"; | |
250 return ResetConnection(); | |
251 } | |
252 } | |
253 | |
254 bool PipeState::IssueWrite() { | |
255 DWORD bytes_written = 0; | |
256 if (WriteFile(pipe_.get(), | |
257 &response_, | |
258 sizeof(response_), | |
259 &bytes_written, | |
260 &overlapped_)) { | |
261 return OnWriteComplete(bytes_written); | |
262 } else if (GetLastError() == ERROR_IO_PENDING) { | |
263 completion_handler_ = &PipeState::OnWriteComplete; | |
264 return true; | |
265 } else { | |
266 PLOG(ERROR) << "WriteFile"; | |
267 return ResetConnection(); | |
268 } | |
269 } | |
270 | |
271 bool PipeState::HandleRequest() { | |
272 // On Vista+ we can verify that the client is who they claim to be, thus | |
273 // preventing arbitrary processes from having us duplicate handles into other | |
274 // processes. | |
275 DWORD real_client_process_id = 0; | |
276 if (TryGetNamedPipeClientProcessId(pipe_.get(), &real_client_process_id)) { | |
277 if (real_client_process_id != 0 && | |
278 real_client_process_id != request_.client_process_id) { | |
279 LOG(ERROR) << "Client process ID from request (" | |
280 << request_.client_process_id | |
281 << ") does not match pipe client process ID (" | |
282 << real_client_process_id << ")."; | |
283 return ResetConnection(); | |
284 } | |
285 } | |
286 | |
287 ScopedKernelHANDLE client_process( | |
288 OpenProcess(PROCESS_ALL_ACCESS, false, request_.client_process_id)); | |
289 if (!client_process.is_valid()) { | |
290 if (ImpersonateNamedPipeClient(pipe_.get())) { | |
291 client_process.reset( | |
292 OpenProcess(PROCESS_ALL_ACCESS, false, request_.client_process_id)); | |
293 RevertToSelf(); | |
294 } | |
295 } | |
296 | |
297 if (!client_process.is_valid()) { | |
298 LOG(ERROR) << "Failed to open client process."; | |
299 return ResetConnection(); | |
300 } | |
301 | |
302 memset(&response_, 0, sizeof(response_)); | |
303 | |
304 HANDLE request_report_event = nullptr; | |
305 HANDLE report_complete_event = nullptr; | |
306 | |
307 if (!delegate_->RegisterClient(client_process.Pass(), | |
308 request_.crashpad_info_address, | |
309 &request_report_event, | |
310 &report_complete_event)) { | |
311 return ResetConnection(); | |
312 } | |
313 response_.request_report_event = reinterpret_cast<uint32_t>(request_report_eve nt); | |
scottmg
2015/05/21 16:17:35
Add a short comment here about the fact that it's
scottmg
2015/05/21 16:17:35
80 col
| |
314 response_.report_complete_event = | |
315 reinterpret_cast<uint32_t>(report_complete_event); | |
316 return IssueWrite(); | |
317 } | |
318 | |
319 bool PipeState::ResetConnection() { | |
320 memset(&request_, 0, sizeof(request_)); | |
321 | |
322 if (!DisconnectNamedPipe(pipe_.get())) { | |
323 PLOG(ERROR) << "DisconnectNamedPipe"; | |
324 return false; | |
325 } else { | |
326 return IssueConnect(); | |
327 } | |
328 } | |
329 | |
330 } // namespace | |
331 | |
332 RegistrationServer::RegistrationServer() : stop_event_() { | |
333 stop_event_.reset(CreateEvent(nullptr, false, false, nullptr)); | |
334 DPCHECK(stop_event_.is_valid()); | |
335 } | |
336 | |
337 RegistrationServer::~RegistrationServer() { | |
338 } | |
339 | |
340 bool RegistrationServer::Run(const base::string16& pipe_name, | |
341 Delegate* delegate) { | |
342 if (!stop_event_.is_valid()) { | |
343 LOG(ERROR) << "Failed to create stop_event_."; | |
344 return false; | |
345 } | |
346 | |
347 PointerVector<PipeState> pipes; | |
348 std::vector<HANDLE> handles; | |
349 | |
350 const int kNumPipes = 3; | |
351 | |
352 // Create the named pipes. | |
353 for (int i = 0; i < kNumPipes; ++i) { | |
354 DWORD open_mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED; | |
355 if (pipes.size() == 0) | |
356 open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; | |
357 ScopedFileHANDLE pipe( | |
358 CreateNamedPipe(pipe_name.c_str(), | |
359 open_mode, | |
360 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, | |
361 kNumPipes, | |
362 512, // nOutBufferSize | |
363 512, // nInBufferSize | |
364 20, // nDefaultTimeOut | |
365 nullptr)); // lpSecurityAttributes | |
366 if (pipe.is_valid()) { | |
367 scoped_ptr<PipeState> pipe_state(new PipeState(pipe.Pass(), delegate)); | |
368 if (pipe_state->Initialize()) { | |
369 pipes.push_back(pipe_state.release()); | |
370 handles.push_back(pipes.back()->completion_event()); | |
371 } | |
372 } else { | |
373 PLOG(ERROR) << "CreateNamedPipe"; | |
374 } | |
375 } | |
376 | |
377 if (pipes.size() == 0) { | |
378 LOG(ERROR) << "Failed to initialize any pipes."; | |
379 return false; | |
380 } | |
381 | |
382 delegate->OnStarted(); | |
383 | |
384 // Add stop_event_ to the list of events we will observe. | |
385 handles.push_back(stop_event_.get()); | |
386 | |
387 bool stopped = false; | |
388 | |
389 // Run the main loop, dispatching completion event signals to the pipe | |
390 // instances. | |
391 while (true) { | |
392 DWORD wait_result = WaitForMultipleObjects( | |
393 static_cast<DWORD>(handles.size()), handles.data(), false, INFINITE); | |
394 if (wait_result >= WAIT_OBJECT_0 && | |
395 wait_result < WAIT_OBJECT_0 + pipes.size()) { | |
396 int index = wait_result - WAIT_OBJECT_0; | |
397 // Handle a completion event. | |
398 if (!pipes[index]->OnCompletion()) { | |
399 pipes.erase(pipes.begin() + index); | |
400 handles.erase(handles.begin() + index); | |
401 } | |
402 if (pipes.size()) | |
403 continue; | |
404 // Exit due to all pipes having failed. | |
405 } else if (wait_result == WAIT_OBJECT_0 + pipes.size()) { | |
406 // Exit due to stop_event_. | |
407 stopped = true; | |
408 } else if (wait_result == WAIT_FAILED) { | |
409 // Exit due to error. | |
410 PLOG(ERROR) << "WaitForMultipleObjects"; | |
411 } else { | |
412 // Exit due to unexpected return code. | |
413 NOTREACHED(); | |
414 } | |
415 break; | |
416 } | |
417 | |
418 // Remove |stop_event_| from the wait list. | |
419 handles.pop_back(); | |
420 | |
421 // Cancel any ongoing asynchronous operations. | |
422 for (auto& pipe : pipes) { | |
423 pipe->Stop(); | |
424 } | |
425 | |
426 // Wait until all of the pipe instances are ready to be destroyed. | |
427 DWORD wait_result = WaitForMultipleObjects( | |
428 static_cast<DWORD>(handles.size()), handles.data(), true, INFINITE); | |
429 PCHECK(wait_result != WAIT_FAILED); | |
430 DCHECK_GE(wait_result, WAIT_OBJECT_0); | |
431 DCHECK_LT(wait_result, WAIT_OBJECT_0 + handles.size()); | |
432 | |
433 return stopped; | |
434 } | |
435 | |
436 void RegistrationServer::Stop() { | |
437 if (!SetEvent(stop_event_.get())) | |
438 PLOG(FATAL) << "SetEvent"; | |
439 } | |
440 | |
441 } // namespace crashpad | |
OLD | NEW |