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

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: Fix .gyp for POSIX. 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
« no previous file with comments | « handler/win/registration_server.h ('k') | handler/win/registration_server_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/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");
cpu_(ooo_6.6-7.5) 2015/05/21 22:16:28 loadlibrary is far slower than GetModuleHandle, th
erikwright (departed) 2015/05/22 01:22:04 Done.
38 if (kernel_dll) {
39 decltype(GetNamedPipeClientProcessId)* proc =
40 reinterpret_cast<decltype(GetNamedPipeClientProcessId)*>(
41 GetProcAddress(kernel_dll, "GetNamedPipeClientProcessId"));
cpu_(ooo_6.6-7.5) 2015/05/21 22:16:28 the lines 39-41 are classy. I've been meaning to d
erikwright (departed) 2015/05/22 01:22:04 Credit goes to Scott, who proposed this approach.
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
cpu_(ooo_6.6-7.5) 2015/05/21 22:16:28 as a side note c+11 alias makes 86 look nicer, but
erikwright (departed) 2015/05/22 01:22:04 Done.
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 }
cpu_(ooo_6.6-7.5) 2015/05/21 22:16:28 this is tricky, ansync is how we did breakpad but
erikwright (departed) 2015/05/22 01:22:04 I agree that these comments seem to suggest that t
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
314 // A handle has at most 32 significant bits, though its type is void*. Thus we
315 // truncate it here. An interesting exception is INVALID_HANDLE_VALUE, which
316 // is '-1'. It is still safe to truncate it from 0xFFFFFFFFFFFFFFFF to
317 // 0xFFFFFFFF, but a 64-bit client receiving that value must correctly sign
318 // extend it.
319 response_.request_report_event =
320 reinterpret_cast<uint32_t>(request_report_event);
321 response_.report_complete_event =
322 reinterpret_cast<uint32_t>(report_complete_event);
323 return IssueWrite();
324 }
325
326 bool PipeState::ResetConnection() {
327 memset(&request_, 0, sizeof(request_));
328
329 if (!DisconnectNamedPipe(pipe_.get())) {
330 PLOG(ERROR) << "DisconnectNamedPipe";
331 return false;
332 } else {
333 return IssueConnect();
334 }
335 }
336
337 } // namespace
338
339 RegistrationServer::RegistrationServer() : stop_event_() {
340 stop_event_.reset(CreateEvent(nullptr, false, false, nullptr));
341 DPCHECK(stop_event_.is_valid());
342 }
343
344 RegistrationServer::~RegistrationServer() {
345 }
346
347 bool RegistrationServer::Run(const base::string16& pipe_name,
348 Delegate* delegate) {
349 if (!stop_event_.is_valid()) {
350 LOG(ERROR) << "Failed to create stop_event_.";
351 return false;
352 }
353
354 PointerVector<PipeState> pipes;
355 std::vector<HANDLE> handles;
356
357 const int kNumPipes = 3;
358
359 // Create the named pipes.
360 for (int i = 0; i < kNumPipes; ++i) {
361 DWORD open_mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
362 if (pipes.size() == 0)
363 open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
364 ScopedFileHANDLE pipe(
365 CreateNamedPipe(pipe_name.c_str(),
366 open_mode,
367 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
368 kNumPipes,
369 512, // nOutBufferSize
370 512, // nInBufferSize
371 20, // nDefaultTimeOut
372 nullptr)); // lpSecurityAttributes
373 if (pipe.is_valid()) {
374 scoped_ptr<PipeState> pipe_state(new PipeState(pipe.Pass(), delegate));
375 if (pipe_state->Initialize()) {
376 pipes.push_back(pipe_state.release());
377 handles.push_back(pipes.back()->completion_event());
378 }
379 } else {
380 PLOG(ERROR) << "CreateNamedPipe";
381 }
382 }
383
384 if (pipes.size() == 0) {
385 LOG(ERROR) << "Failed to initialize any pipes.";
386 return false;
387 }
388
389 delegate->OnStarted();
390
391 // Add stop_event_ to the list of events we will observe.
392 handles.push_back(stop_event_.get());
393
394 bool stopped = false;
395
396 // Run the main loop, dispatching completion event signals to the pipe
397 // instances.
398 while (true) {
cpu_(ooo_6.6-7.5) 2015/05/21 22:55:58 we also lost the ability to service two clients at
erikwright (departed) 2015/05/22 01:22:04 Single-threaded and async IO are not mutually excl
399 DWORD wait_result = WaitForMultipleObjects(
400 static_cast<DWORD>(handles.size()), handles.data(), false, INFINITE);
401 if (wait_result >= WAIT_OBJECT_0 &&
402 wait_result < WAIT_OBJECT_0 + pipes.size()) {
403 int index = wait_result - WAIT_OBJECT_0;
404 // Handle a completion event.
405 if (!pipes[index]->OnCompletion()) {
406 pipes.erase(pipes.begin() + index);
407 handles.erase(handles.begin() + index);
408 }
409 if (pipes.size())
410 continue;
411 // Exit due to all pipes having failed.
412 } else if (wait_result == WAIT_OBJECT_0 + pipes.size()) {
413 // Exit due to stop_event_.
414 stopped = true;
415 } else if (wait_result == WAIT_FAILED) {
416 // Exit due to error.
417 PLOG(ERROR) << "WaitForMultipleObjects";
418 } else {
419 // Exit due to unexpected return code.
420 NOTREACHED();
421 }
422 break;
423 }
424
425 // Remove |stop_event_| from the wait list.
426 handles.pop_back();
427
428 // Cancel any ongoing asynchronous operations.
429 for (auto& pipe : pipes) {
430 pipe->Stop();
431 }
432
cpu_(ooo_6.6-7.5) 2015/05/21 22:55:58 How does this work? I mean why do we believe we ge
erikwright (departed) 2015/05/22 01:22:04 In PipeState::Stop we call CancelIo for the pipe.
433 // Wait until all of the pipe instances are ready to be destroyed.
434 DWORD wait_result = WaitForMultipleObjects(
435 static_cast<DWORD>(handles.size()), handles.data(), true, INFINITE);
436 PCHECK(wait_result != WAIT_FAILED);
437 DCHECK_GE(wait_result, WAIT_OBJECT_0);
438 DCHECK_LT(wait_result, WAIT_OBJECT_0 + handles.size());
439
440 return stopped;
441 }
442
443 void RegistrationServer::Stop() {
444 if (!SetEvent(stop_event_.get()))
445 PLOG(FATAL) << "SetEvent";
446 }
447
448 } // namespace crashpad
OLDNEW
« no previous file with comments | « handler/win/registration_server.h ('k') | handler/win/registration_server_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698