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 <cstring> | |
scottmg
2015/05/20 18:53:09
string.h
erikwright (departed)
2015/05/20 20:54:14
Done.
| |
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(TEXT("Kernel32.dll")); | |
scottmg
2015/05/20 18:53:09
We only build with _UNICODE, just L" instead, and
erikwright (departed)
2015/05/20 20:54:13
Done.
| |
37 if (kernel_dll) { | |
38 typedef BOOL(WINAPI * GetNamedPipeClientProcessIdProc)(HANDLE, PULONG); | |
scottmg
2015/05/20 18:53:09
We've been using decltype instead (since the proto
erikwright (departed)
2015/05/20 20:54:14
Done.
| |
39 GetNamedPipeClientProcessIdProc proc = | |
40 reinterpret_cast<GetNamedPipeClientProcessIdProc>( | |
41 GetProcAddress(kernel_dll, "GetNamedPipeClientProcessId")); | |
42 if (!proc) { | |
43 *process_id = 0; | |
44 result = true; | |
45 } else { | |
46 result = (proc)(pipe, process_id); | |
scottmg
2015/05/20 18:53:09
Remove extra () around `proc`.
erikwright (departed)
2015/05/20 20:54:13
Done.
| |
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|. Initiates an asynchronous ConnectNamedPipe before | |
58 // returning. | |
59 // The client must observe completion_event() and invoke OnCompletion() | |
60 // whenever it is signaled. | |
61 // Before destroying a PipeState instance you must invoke Stop() and then wait | |
62 // for completion_event() to be signaled one last time. | |
63 PipeState(ScopedFileHANDLE pipe, RegistrationServer::Delegate* delegate); | |
64 ~PipeState(); | |
65 | |
66 // Cancels any pending asynchronous operations. | |
67 void Stop(); | |
68 | |
69 // Returns an event handle that will be signaled whenever an asynchronous | |
70 // operation associated with this instance completes. | |
71 HANDLE completion_event() { return event_.get(); } | |
72 | |
73 // Must be called by the client whenever completion_event() is signaled. | |
74 void OnCompletion(); | |
75 | |
76 private: | |
77 typedef void (PipeState::*AsyncCompletionHandler)(DWORD bytes_transferred); | |
78 | |
79 // State transition handlers. | |
80 void OnConnectComplete(DWORD /* bytes_transferred */); | |
81 void OnReadComplete(DWORD bytes_transferred); | |
82 void OnWriteComplete(DWORD bytes_transferred); | |
83 | |
84 // Invokes ConnectNamedPipe. | |
85 void IssueConnect(); | |
86 // Invokes ReadFile. | |
scottmg
2015/05/20 18:53:09
These function comments don't seem very useful. (J
erikwright (departed)
2015/05/20 20:54:14
Done.
scottmg
2015/05/21 02:32:36
Sorry, I meant just IssueRead and IssueWrite. I th
erikwright (departed)
2015/05/21 15:12:37
Done.
| |
87 void IssueRead(); | |
88 // Invokes WriteFile. | |
89 void IssueWrite(); | |
90 | |
91 // Handles the request in request_ using delegate_; | |
92 void HandleRequest(); | |
93 | |
94 // Disconnects from the current client and invokes IssueConnect. | |
95 void ResetConnection(); | |
96 | |
97 RegistrationRequest request_; | |
98 RegistrationResponse response_; | |
99 // The state transition handler to be invoked when the active asynchronous | |
100 // operation completes. | |
101 AsyncCompletionHandler completion_handler_; | |
102 OVERLAPPED overlapped_; | |
103 ScopedKernelHANDLE event_; | |
104 ScopedFileHANDLE pipe_; | |
105 RegistrationServer::Delegate* delegate_; | |
106 | |
107 DISALLOW_COPY_AND_ASSIGN(PipeState); | |
108 }; | |
109 | |
110 PipeState::PipeState(ScopedFileHANDLE pipe, | |
111 RegistrationServer::Delegate* delegate) | |
112 : request_(), | |
113 response_(), | |
114 completion_handler_(nullptr), | |
115 overlapped_(), | |
116 event_(), | |
117 pipe_(pipe.Pass()), | |
118 delegate_(delegate) { | |
119 event_.reset(CreateEvent(nullptr, TRUE, FALSE, nullptr)); | |
scottmg
2015/05/20 18:53:09
true and false (even though they're BOOL, not bool
erikwright (departed)
2015/05/20 20:54:14
Done.
| |
120 if (!event_.is_valid()) { | |
121 PLOG(ERROR); | |
scottmg
2015/05/20 18:53:09
<< "failed to create event" or just << "CreateEven
erikwright (departed)
2015/05/20 20:54:14
I received some guidance in Chromium to not includ
scottmg
2015/05/21 02:32:36
Yeah, it's a divergence from Chromium. :(
Mark ha
erikwright (departed)
2015/05/21 15:12:37
Done.
| |
122 pipe_.reset(); | |
123 } else { | |
124 overlapped_.hEvent = event_.get(); | |
125 IssueConnect(); | |
126 } | |
127 | |
128 DCHECK(!pipe_.get() || completion_handler_); | |
129 } | |
130 | |
131 PipeState::~PipeState() { | |
132 } | |
133 | |
134 void PipeState::Stop() { | |
135 if (pipe_.is_valid()) { | |
136 if (!CancelIo(pipe_.get())) | |
137 PLOG(FATAL); | |
138 } else { | |
139 SetEvent(event_.get()); | |
scottmg
2015/05/20 18:53:09
I think this path would execute if the event faile
erikwright (departed)
2015/05/20 20:54:14
I fixed things so the client now knows when it ent
| |
140 } | |
141 } | |
142 | |
143 void PipeState::OnCompletion() { | |
144 AsyncCompletionHandler completion_handler = completion_handler_; | |
145 completion_handler_ = nullptr; | |
146 | |
147 DWORD bytes_transferred = 0; | |
148 BOOL success = GetOverlappedResult(pipe_.get(), | |
149 &overlapped_, | |
150 &bytes_transferred, | |
151 FALSE); // do not wait | |
152 ResetEvent(event_.get()); | |
153 | |
154 if (!completion_handler) { | |
155 NOTREACHED(); | |
156 ResetConnection(); | |
157 } else if (!success) { | |
158 PLOG(ERROR); | |
159 ResetConnection(); | |
160 } else { | |
161 (this->*completion_handler)(bytes_transferred); | |
162 } | |
163 | |
164 DCHECK(!pipe_.get() || completion_handler_); | |
165 } | |
166 | |
167 void PipeState::OnConnectComplete(DWORD /* bytes_transferred */) { | |
168 IssueRead(); | |
169 } | |
170 | |
171 void PipeState::OnReadComplete(DWORD bytes_transferred) { | |
172 if (bytes_transferred != sizeof(request_)) { | |
173 LOG(ERROR) << "Invalid message size: " << bytes_transferred; | |
174 ResetConnection(); | |
175 } else { | |
176 HandleRequest(); | |
177 } | |
178 } | |
179 | |
180 void PipeState::OnWriteComplete(DWORD bytes_transferred) { | |
181 if (bytes_transferred != sizeof(response_)) { | |
182 LOG(ERROR) << "Incomplete write operation. Bytes written: " | |
183 << bytes_transferred; | |
184 } | |
185 ResetConnection(); | |
186 } | |
187 | |
188 void PipeState::IssueConnect() { | |
189 if (ConnectNamedPipe(pipe_.get(), &overlapped_)) { | |
190 OnConnectComplete(0); // bytes_transferred (ignored) | |
191 } else { | |
192 DWORD result = GetLastError(); | |
193 if (result == ERROR_PIPE_CONNECTED) { | |
194 OnConnectComplete(0); // bytes_transferred (ignored) | |
195 } else if (result == ERROR_IO_PENDING){ | |
196 completion_handler_ = &PipeState::OnConnectComplete; | |
197 } else { | |
198 PLOG(ERROR); | |
199 pipe_.reset(); | |
200 completion_handler_ = nullptr; | |
201 } | |
202 } | |
203 } | |
204 | |
205 void PipeState::IssueRead() { | |
206 DWORD bytes_read = 0; | |
207 if (ReadFile(pipe_.get(), &request_, sizeof(request_), &bytes_read, &overlappe d_)) { | |
scottmg
2015/05/20 18:53:09
80 col
erikwright (departed)
2015/05/20 20:54:14
Done.
| |
208 OnReadComplete(bytes_read); | |
209 } else if (GetLastError() == ERROR_IO_PENDING) { | |
210 completion_handler_ = &PipeState::OnReadComplete; | |
211 } else { | |
212 PLOG(ERROR); | |
213 ResetConnection(); | |
214 } | |
215 } | |
216 | |
217 void PipeState::IssueWrite() { | |
218 DWORD bytes_written = 0; | |
219 if (WriteFile(pipe_.get(), &response_, sizeof(response_), &bytes_written, &ove rlapped_)) { | |
scottmg
2015/05/20 18:53:09
80 col
erikwright (departed)
2015/05/20 20:54:14
Done.
| |
220 OnWriteComplete(bytes_written); | |
221 } else if (GetLastError() == ERROR_IO_PENDING) { | |
222 completion_handler_ = &PipeState::OnWriteComplete; | |
223 } else { | |
224 PLOG(ERROR); | |
225 ResetConnection(); | |
226 } | |
227 } | |
228 | |
229 void PipeState::HandleRequest() { | |
230 DWORD real_client_process_id = 0; | |
231 if (TryGetNamedPipeClientProcessId(pipe_.get(), &real_client_process_id)) { | |
scottmg
2015/05/20 18:53:09
What's the upside of doing this when it doesn't wo
erikwright (departed)
2015/05/20 20:54:14
Added a comment. Carlos implied there was a minor
| |
232 if (real_client_process_id != 0 && | |
233 real_client_process_id != request_.client_process_id) { | |
234 LOG(ERROR) << "Client process ID from request (" | |
235 << request_.client_process_id | |
236 << ") does not match pipe client process ID (" | |
237 << real_client_process_id << ")."; | |
238 ResetConnection(); | |
239 return; | |
240 } | |
241 } | |
242 | |
243 ScopedKernelHANDLE client_process( | |
244 OpenProcess(PROCESS_ALL_ACCESS, FALSE, request_.client_process_id)); | |
245 if (!client_process.is_valid()) { | |
246 if (ImpersonateNamedPipeClient(pipe_.get())) { | |
247 client_process.reset( | |
248 OpenProcess(PROCESS_ALL_ACCESS, FALSE, request_.client_process_id)); | |
249 RevertToSelf(); | |
250 } | |
251 } | |
252 | |
253 if (!client_process.is_valid()) { | |
254 LOG(ERROR) << "Failed to open client process."; | |
255 ResetConnection(); | |
256 return; | |
257 } | |
258 | |
259 memset(&response_, 0, sizeof(response_)); | |
260 | |
261 if (!delegate_->RegisterClient(client_process.Pass(), | |
262 request_.crashpad_info_address, | |
263 &response_.request_report_event, | |
264 &response_.report_complete_event)) { | |
265 ResetConnection(); | |
scottmg
2015/05/20 18:53:09
LOG(ERROR) here
erikwright (departed)
2015/05/20 20:54:14
When calling into other crashpad code, I assume th
| |
266 return; | |
267 } | |
268 IssueWrite(); | |
269 } | |
270 | |
271 void PipeState::ResetConnection() { | |
272 memset(&request_, 0, sizeof(request_)); | |
273 | |
274 if (!DisconnectNamedPipe(pipe_.get())) { | |
275 PLOG(ERROR); | |
276 pipe_.reset(); | |
277 } else { | |
278 IssueConnect(); | |
279 } | |
280 } | |
281 | |
282 } // namespace | |
283 | |
284 RegistrationServer::RegistrationServer() : stop_event_()/*, stopped_event_()*/ { | |
scottmg
2015/05/20 18:53:09
Delete old (?) comment.
erikwright (departed)
2015/05/20 20:54:14
Done.
| |
285 stop_event_.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr)); | |
286 DPCHECK(stop_event_.is_valid()); | |
287 } | |
288 | |
289 RegistrationServer::~RegistrationServer() { | |
290 } | |
291 | |
292 void RegistrationServer::Run(const base::string16& pipe_name, | |
293 Delegate* delegate) { | |
294 PointerVector<PipeState> pipes; | |
295 std::vector<HANDLE> handles; | |
296 | |
297 const int kNumPipes = 3; | |
298 | |
299 // Create the named pipes. | |
300 for (int i = 0; i < kNumPipes; ++i) { | |
301 DWORD open_mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED; | |
302 if (i == 0) | |
303 open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; | |
304 ScopedFileHANDLE pipe( | |
305 CreateNamedPipe(pipe_name.c_str(), | |
306 open_mode, | |
307 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, | |
308 kNumPipes, | |
309 512, // nOutBufferSize | |
310 512, // nInBufferSize | |
311 20, // nDefaultTimeOut | |
312 nullptr)); // lpSecurityAttributes | |
313 if (pipe.is_valid()) { | |
314 // If this is the first successfully created pipe, notify the delegate. | |
315 if (pipes.size() == 0) | |
316 delegate->OnStarted(); | |
scottmg
2015/05/20 18:53:09
Moving this down below the loop seems tidier than
erikwright (departed)
2015/05/20 20:54:14
Done.
| |
317 pipes.push_back(new PipeState(pipe.Pass(), delegate)); | |
318 handles.push_back(pipes.back()->completion_event()); | |
319 } else { | |
320 PLOG(ERROR); | |
scottmg
2015/05/20 18:53:09
If we get to this case, then the next time around
erikwright (departed)
2015/05/20 20:54:13
Done.
| |
321 } | |
322 } | |
323 | |
324 // Add stop_event_ to the list of events we will observe. | |
325 handles.push_back(stop_event_.get()); | |
326 | |
327 // Run the main loop, dispatching completion event signals to the pipe | |
328 // instances. | |
329 while (true) { | |
330 DWORD wait_result = WaitForMultipleObjects( | |
331 static_cast<DWORD>(handles.size()), handles.data(), FALSE, INFINITE); | |
332 if (wait_result >= WAIT_OBJECT_0 && | |
333 wait_result < WAIT_OBJECT_0 + pipes.size()) { | |
334 // Handle a completion event. | |
335 pipes[wait_result - WAIT_OBJECT_0]->OnCompletion(); | |
336 continue; | |
337 } else if (wait_result == WAIT_OBJECT_0 + pipes.size()) { | |
338 // Exit due to stop_event_. | |
339 } else if (wait_result == WAIT_FAILED) { | |
340 // Exit due to error. | |
341 PLOG(ERROR); | |
342 } else { | |
343 // Exit due to unexpected return code. | |
344 NOTREACHED(); | |
345 } | |
346 break; | |
347 } | |
348 | |
349 // Remove |stop_event_| from the wait list. | |
350 handles.pop_back(); | |
351 | |
352 // Cancel any ongoing asynchronous operations. | |
353 for (auto& pipe : pipes) { | |
354 pipe->Stop(); | |
355 } | |
356 | |
357 // Wait until all of the pipe instances are ready to be destroyed. | |
358 DWORD wait_result = WaitForMultipleObjects( | |
359 static_cast<DWORD>(handles.size()), handles.data(), TRUE, INFINITE); | |
scottmg
2015/05/20 18:53:09
If we exited the loop above with WAIT_FAILED, is t
erikwright (departed)
2015/05/20 20:54:13
It's hard to rationalize about what would happen i
| |
360 PCHECK(wait_result != WAIT_FAILED); | |
361 DCHECK_GE(wait_result, WAIT_OBJECT_0); | |
362 DCHECK_LT(wait_result, WAIT_OBJECT_0 + handles.size()); | |
363 } | |
364 | |
365 void RegistrationServer::Stop() { | |
366 if (!SetEvent(stop_event_.get())) | |
367 PLOG(FATAL); | |
368 } | |
369 | |
370 } // namespace crashpad | |
OLD | NEW |