Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: handler/win/registration_server.cc

Issue 1126783004: Introduce RegistrationServer. (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Review comments. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698