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

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: Implement pipe client PID detection. 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 <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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698