Chromium Code Reviews| 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 |