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