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 <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"); | |
| 38 if (kernel_dll) { | |
| 39 decltype(GetNamedPipeClientProcessId)* proc = | |
| 40 reinterpret_cast<decltype(GetNamedPipeClientProcessId)*>( | |
| 41 GetProcAddress(kernel_dll, "GetNamedPipeClientProcessId")); | |
| 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 | |
| 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 } | |
| 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 response_.request_report_event = reinterpret_cast<uint32_t>(request_report_eve nt); | |
|
scottmg
2015/05/21 16:17:35
Add a short comment here about the fact that it's
scottmg
2015/05/21 16:17:35
80 col
| |
| 314 response_.report_complete_event = | |
| 315 reinterpret_cast<uint32_t>(report_complete_event); | |
| 316 return IssueWrite(); | |
| 317 } | |
| 318 | |
| 319 bool PipeState::ResetConnection() { | |
| 320 memset(&request_, 0, sizeof(request_)); | |
| 321 | |
| 322 if (!DisconnectNamedPipe(pipe_.get())) { | |
| 323 PLOG(ERROR) << "DisconnectNamedPipe"; | |
| 324 return false; | |
| 325 } else { | |
| 326 return IssueConnect(); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 } // namespace | |
| 331 | |
| 332 RegistrationServer::RegistrationServer() : stop_event_() { | |
| 333 stop_event_.reset(CreateEvent(nullptr, false, false, nullptr)); | |
| 334 DPCHECK(stop_event_.is_valid()); | |
| 335 } | |
| 336 | |
| 337 RegistrationServer::~RegistrationServer() { | |
| 338 } | |
| 339 | |
| 340 bool RegistrationServer::Run(const base::string16& pipe_name, | |
| 341 Delegate* delegate) { | |
| 342 if (!stop_event_.is_valid()) { | |
| 343 LOG(ERROR) << "Failed to create stop_event_."; | |
| 344 return false; | |
| 345 } | |
| 346 | |
| 347 PointerVector<PipeState> pipes; | |
| 348 std::vector<HANDLE> handles; | |
| 349 | |
| 350 const int kNumPipes = 3; | |
| 351 | |
| 352 // Create the named pipes. | |
| 353 for (int i = 0; i < kNumPipes; ++i) { | |
| 354 DWORD open_mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED; | |
| 355 if (pipes.size() == 0) | |
| 356 open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; | |
| 357 ScopedFileHANDLE pipe( | |
| 358 CreateNamedPipe(pipe_name.c_str(), | |
| 359 open_mode, | |
| 360 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, | |
| 361 kNumPipes, | |
| 362 512, // nOutBufferSize | |
| 363 512, // nInBufferSize | |
| 364 20, // nDefaultTimeOut | |
| 365 nullptr)); // lpSecurityAttributes | |
| 366 if (pipe.is_valid()) { | |
| 367 scoped_ptr<PipeState> pipe_state(new PipeState(pipe.Pass(), delegate)); | |
| 368 if (pipe_state->Initialize()) { | |
| 369 pipes.push_back(pipe_state.release()); | |
| 370 handles.push_back(pipes.back()->completion_event()); | |
| 371 } | |
| 372 } else { | |
| 373 PLOG(ERROR) << "CreateNamedPipe"; | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 if (pipes.size() == 0) { | |
| 378 LOG(ERROR) << "Failed to initialize any pipes."; | |
| 379 return false; | |
| 380 } | |
| 381 | |
| 382 delegate->OnStarted(); | |
| 383 | |
| 384 // Add stop_event_ to the list of events we will observe. | |
| 385 handles.push_back(stop_event_.get()); | |
| 386 | |
| 387 bool stopped = false; | |
| 388 | |
| 389 // Run the main loop, dispatching completion event signals to the pipe | |
| 390 // instances. | |
| 391 while (true) { | |
| 392 DWORD wait_result = WaitForMultipleObjects( | |
| 393 static_cast<DWORD>(handles.size()), handles.data(), false, INFINITE); | |
| 394 if (wait_result >= WAIT_OBJECT_0 && | |
| 395 wait_result < WAIT_OBJECT_0 + pipes.size()) { | |
| 396 int index = wait_result - WAIT_OBJECT_0; | |
| 397 // Handle a completion event. | |
| 398 if (!pipes[index]->OnCompletion()) { | |
| 399 pipes.erase(pipes.begin() + index); | |
| 400 handles.erase(handles.begin() + index); | |
| 401 } | |
| 402 if (pipes.size()) | |
| 403 continue; | |
| 404 // Exit due to all pipes having failed. | |
| 405 } else if (wait_result == WAIT_OBJECT_0 + pipes.size()) { | |
| 406 // Exit due to stop_event_. | |
| 407 stopped = true; | |
| 408 } else if (wait_result == WAIT_FAILED) { | |
| 409 // Exit due to error. | |
| 410 PLOG(ERROR) << "WaitForMultipleObjects"; | |
| 411 } else { | |
| 412 // Exit due to unexpected return code. | |
| 413 NOTREACHED(); | |
| 414 } | |
| 415 break; | |
| 416 } | |
| 417 | |
| 418 // Remove |stop_event_| from the wait list. | |
| 419 handles.pop_back(); | |
| 420 | |
| 421 // Cancel any ongoing asynchronous operations. | |
| 422 for (auto& pipe : pipes) { | |
| 423 pipe->Stop(); | |
| 424 } | |
| 425 | |
| 426 // Wait until all of the pipe instances are ready to be destroyed. | |
| 427 DWORD wait_result = WaitForMultipleObjects( | |
| 428 static_cast<DWORD>(handles.size()), handles.data(), true, INFINITE); | |
| 429 PCHECK(wait_result != WAIT_FAILED); | |
| 430 DCHECK_GE(wait_result, WAIT_OBJECT_0); | |
| 431 DCHECK_LT(wait_result, WAIT_OBJECT_0 + handles.size()); | |
| 432 | |
| 433 return stopped; | |
| 434 } | |
| 435 | |
| 436 void RegistrationServer::Stop() { | |
| 437 if (!SetEvent(stop_event_.get())) | |
| 438 PLOG(FATAL) << "SetEvent"; | |
| 439 } | |
| 440 | |
| 441 } // namespace crashpad | |
| OLD | NEW |