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/registrar.h" | |
| 16 | |
| 17 #include "base/logging.h" | |
| 18 | |
| 19 namespace crashpad { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 bool DuplicateHandleForProcess(HANDLE handle, | |
| 24 HANDLE process, | |
| 25 DWORD access, | |
| 26 HANDLE* duplicate) { | |
| 27 if (DuplicateHandle( | |
| 28 GetCurrentProcess(), handle, process, duplicate, access, FALSE, 0) != | |
| 29 TRUE) { | |
| 30 PLOG(ERROR) << "DuplicateHandle"; | |
| 31 return false; | |
| 32 } | |
| 33 return true; | |
| 34 } | |
| 35 | |
| 36 // Implements scoped acquisition and release of a mutex. | |
| 37 class AutoMutex { | |
| 38 public: | |
| 39 // Blocks indefinitely until |mutex| is acquired. |mutex| will be released in | |
| 40 // the destructor. | |
| 41 explicit AutoMutex(HANDLE mutex) : mutex_(mutex) { | |
| 42 DWORD result = WaitForSingleObject(mutex_, INFINITE); | |
| 43 if (result == WAIT_FAILED) | |
| 44 PLOG(FATAL) << "WaitForSingleObject"; | |
| 45 CHECK_EQ(WAIT_OBJECT_0, result) << "WaitForSingleObject"; | |
| 46 } | |
| 47 | |
| 48 ~AutoMutex() { PCHECK(ReleaseMutex(mutex_)) << "ReleaseMutex"; } | |
| 49 | |
| 50 private: | |
| 51 HANDLE mutex_; | |
| 52 | |
| 53 DISALLOW_COPY_AND_ASSIGN(AutoMutex); | |
| 54 }; | |
| 55 | |
| 56 // Helps clean up a `HANDLE` from another process, in case of failure. Use | |
| 57 // Receive() to store a `HANDLE`. If ownership is not reclaimed prior to | |
| 58 // destruction of this object it will close the `HANDLE`. | |
| 59 class ScopedOtherProcessHandle { | |
| 60 public: | |
| 61 explicit ScopedOtherProcessHandle(HANDLE other_process) | |
| 62 : other_process_(other_process), handle_(nullptr) {} | |
| 63 | |
| 64 ~ScopedOtherProcessHandle() { | |
| 65 if (!handle_) | |
| 66 return; | |
| 67 | |
| 68 if (!DuplicateHandle(other_process_, | |
| 69 handle_, | |
| 70 nullptr, | |
| 71 nullptr, | |
| 72 0, | |
| 73 false, | |
| 74 DUPLICATE_CLOSE_SOURCE)) | |
| 75 DPLOG(ERROR) << "DuplicateHandle"; | |
| 76 } | |
| 77 | |
| 78 // Returns a pointer to the internal `HANDLE` value. The caller may store a | |
| 79 // `HANDLE` at the returned address to pass ownership of the `HANDLE` to this | |
| 80 // instance. | |
| 81 HANDLE* Receive() { | |
| 82 CHECK(!handle_); | |
| 83 return &handle_; | |
| 84 } | |
| 85 | |
| 86 // Transfers ownership of the stored `HANDLE` value to the caller. | |
| 87 HANDLE Release() { | |
| 88 HANDLE temp = handle_; | |
| 89 handle_ = nullptr; | |
| 90 return temp; | |
| 91 } | |
| 92 | |
| 93 private: | |
| 94 HANDLE other_process_; | |
| 95 HANDLE handle_; | |
| 96 | |
| 97 DISALLOW_COPY_AND_ASSIGN(ScopedOtherProcessHandle); | |
| 98 }; | |
| 99 | |
| 100 // Helps clean up a registered wait handle in case of failure. Use Receive() to store a | |
|
scottmg
2015/06/29 20:29:05
80 col
| |
| 101 // registered wait `HANDLE`. If ownership is not reclaimed prior to destruction | |
| 102 // of this object it will perform a blocking UnregisterWaitEx operation to | |
| 103 // cancel the wait. | |
| 104 class BlockingUnregisterWait { | |
| 105 public: | |
| 106 BlockingUnregisterWait() : handle_(nullptr) {} | |
| 107 | |
| 108 ~BlockingUnregisterWait() { | |
| 109 if (!handle_) | |
| 110 return; | |
| 111 // UnregisterWaitEx with INVALID_HANDLE_VALUE blocks until any running | |
| 112 // callback has completed. | |
| 113 UnregisterWaitEx(handle_, INVALID_HANDLE_VALUE); | |
| 114 } | |
| 115 | |
| 116 // Returns a pointer to the internal `HANDLE` value. The caller may store a | |
| 117 // registered wait `HANDLE` at the returned address to pass ownership of the | |
| 118 // `HANDLE` to this instance. | |
| 119 HANDLE* Receive() { | |
| 120 CHECK(!handle_); | |
| 121 return &handle_; | |
| 122 } | |
| 123 | |
| 124 // Transfers ownership of the stored `HANDLE` value to the caller. | |
| 125 HANDLE Release() { | |
| 126 HANDLE temp = handle_; | |
| 127 handle_ = nullptr; | |
| 128 return temp; | |
| 129 } | |
| 130 | |
| 131 private: | |
| 132 HANDLE handle_; | |
| 133 | |
| 134 DISALLOW_COPY_AND_ASSIGN(BlockingUnregisterWait); | |
| 135 }; | |
| 136 | |
| 137 // Tests for SYNCHRONIZE access to a `HANDLE`. | |
| 138 bool HasSynchronizePrivilege(HANDLE handle) { | |
| 139 switch (WaitForSingleObject(handle, 0)) { | |
| 140 case WAIT_TIMEOUT: | |
| 141 case WAIT_OBJECT_0: | |
| 142 case WAIT_ABANDONED_0: | |
| 143 // We have the right privilege. | |
| 144 return true; | |
| 145 case WAIT_FAILED: | |
| 146 // We are probably missing privileges. | |
| 147 DCHECK(false) << "WaitForSingleObject"; | |
| 148 return false; | |
| 149 default: | |
| 150 NOTREACHED(); | |
| 151 return false; | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 // Creates an auto-reset, default non-signaled event. Returns true if | |
| 156 // successful. Logs an error and returns false otherwise. | |
| 157 bool LoggedCreateEvent(ScopedKernelHANDLE* destination) { | |
|
scottmg
2015/06/29 20:29:05
Logged -> Logging
| |
| 158 destination->reset(CreateEvent(nullptr, false, false, nullptr)); | |
| 159 if (destination->is_valid()) | |
| 160 return true; | |
| 161 PLOG(ERROR) << "CreateEvent"; | |
| 162 return false; | |
| 163 } | |
| 164 | |
| 165 // Creates a pair of events that may be used to communicate with a client | |
| 166 // process. Creates duplicates, valid in the client process, with suitable | |
| 167 // permissions. | |
| 168 bool CreateEventPair(HANDLE client_process, | |
| 169 ScopedKernelHANDLE* request_report_event, | |
| 170 ScopedKernelHANDLE* report_complete_event, | |
| 171 ScopedOtherProcessHandle* client_request_report_event, | |
| 172 ScopedOtherProcessHandle* client_report_complete_event) { | |
| 173 if (!LoggedCreateEvent(request_report_event)) | |
| 174 return false; | |
| 175 | |
| 176 if (!LoggedCreateEvent(report_complete_event)) | |
| 177 return false; | |
| 178 | |
| 179 if (!DuplicateHandleForProcess(request_report_event->get(), | |
| 180 client_process, | |
| 181 EVENT_MODIFY_STATE, | |
| 182 client_request_report_event->Receive())) { | |
| 183 return false; | |
| 184 } | |
| 185 | |
| 186 if (!DuplicateHandleForProcess(report_complete_event->get(), | |
| 187 client_process, | |
| 188 SYNCHRONIZE, | |
| 189 client_report_complete_event->Receive())) { | |
| 190 return false; | |
| 191 } | |
| 192 | |
| 193 return true; | |
| 194 } | |
| 195 | |
| 196 } // namespace | |
| 197 | |
| 198 struct Registrar::Entry { | |
| 199 ScopedKernelHANDLE process; | |
| 200 ScopedKernelHANDLE request_report_event; | |
| 201 ScopedKernelHANDLE report_complete_event; | |
| 202 HANDLE request_report_event_wait_handle; | |
| 203 HANDLE process_exit_wait_handle; | |
| 204 Registrar* registrar; | |
| 205 }; | |
| 206 | |
| 207 Registrar::Registrar(scoped_ptr<Delegate> delegate) | |
| 208 : delegate_(delegate.Pass()), mutex_() { | |
| 209 mutex_.reset(CreateMutex(nullptr, false, nullptr)); | |
| 210 PCHECK(mutex_.is_valid()) << "CreateMutex"; | |
| 211 } | |
| 212 | |
| 213 Registrar::~Registrar() { | |
| 214 // Unregister and destroy all of the client entries. | |
| 215 while (true) { | |
| 216 scoped_ptr<Entry> entry; | |
| 217 { | |
| 218 AutoMutex lock(mutex_.get()); | |
| 219 if (entries_.empty()) | |
| 220 break; | |
| 221 entry.reset(entries_.back()); | |
| 222 entries_.pop_back(); | |
| 223 } | |
| 224 | |
| 225 // These calls will block until running callbacks (if any) complete. | |
| 226 // ProcessExitCallback will acquire |mutex_| so it is essential that we do | |
| 227 // not hold it now. | |
| 228 | |
| 229 // Since we have already removed |entry| from |entries_| a concurrent | |
| 230 // OnProcessExit would return immediately. | |
| 231 UnregisterWaitEx(entry->process_exit_wait_handle, INVALID_HANDLE_VALUE); | |
| 232 | |
| 233 // The ReportRequestCallback, on the other hand, may still trigger a report now | |
|
scottmg
2015/06/29 20:29:05
80 col
| |
| 234 // using |delegate_|. | |
| 235 UnregisterWaitEx(entry->request_report_event_wait_handle, | |
| 236 INVALID_HANDLE_VALUE); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 bool Registrar::RegisterProcess(ScopedKernelHANDLE process, | |
| 241 HANDLE* request_report_event, | |
| 242 HANDLE* report_complete_event) { | |
| 243 // Verify that we have SYNCHRONIZE privilege. Otherwise | |
| 244 // RegisterWaitForSingleObject crashes! | |
| 245 DCHECK(HasSynchronizePrivilege(process.get())); | |
| 246 | |
| 247 scoped_ptr<Entry> entry(new Entry); | |
| 248 entry->registrar = this; | |
| 249 entry->process = process.Pass(); | |
| 250 | |
| 251 ScopedOtherProcessHandle client_request_report_event(entry->process.get()); | |
| 252 ScopedOtherProcessHandle client_report_complete_event(entry->process.get()); | |
| 253 if (!CreateEventPair(entry->process.get(), | |
| 254 &entry->request_report_event, | |
| 255 &entry->report_complete_event, | |
| 256 &client_request_report_event, | |
| 257 &client_report_complete_event)) { | |
| 258 return false; | |
| 259 } | |
| 260 | |
| 261 | |
| 262 BlockingUnregisterWait request_report_event_wait_handle; | |
| 263 BlockingUnregisterWait process_exit_wait_handle; | |
| 264 if (!RegisterWaits(entry.get(), | |
| 265 request_report_event_wait_handle.Receive(), | |
| 266 process_exit_wait_handle.Receive())) { | |
| 267 return false; | |
| 268 } | |
| 269 | |
| 270 // If the process has already exited the "process exit" callback may very well | |
|
scottmg
2015/06/29 20:29:05
The number of cases here worries me. Is it possibl
| |
| 271 // have already executed, but it wouldn't have been able to clean |entry| | |
| 272 // since we haven't inserted it yet. If that's the case we will abort. All of | |
| 273 // our resources will be automatically cleaned up. | |
| 274 | |
| 275 // This lock must have a shorter scope than |process_exit_wait_handle| to | |
| 276 // avoid a deadlock with OnProcessExit(). | |
| 277 AutoMutex lock(mutex_.get()); | |
| 278 | |
| 279 switch(WaitForSingleObject(entry->process.get(), 0)) { | |
| 280 case WAIT_FAILED: | |
| 281 PLOG(ERROR) << "WaitForSingleObject"; | |
| 282 return false; | |
| 283 case WAIT_OBJECT_0: | |
| 284 // The process already exited. | |
| 285 return false; | |
| 286 case WAIT_TIMEOUT: | |
| 287 // The client process has not exited yet, meaning the process exit | |
| 288 // callback can be relied upon to clean up |entry|. | |
| 289 break; | |
| 290 default: | |
| 291 NOTREACHED(); | |
| 292 return false; | |
| 293 } | |
| 294 | |
| 295 // Even if the exit happens between here and the insertion, we know the exit | |
| 296 // callback is not running because we are holding |mutex_|. | |
| 297 entry->process_exit_wait_handle = process_exit_wait_handle.Release(); | |
| 298 entry->request_report_event_wait_handle = | |
| 299 request_report_event_wait_handle.Release(); | |
| 300 entries_.push_back(entry.release()); | |
| 301 | |
| 302 // Return the client's copies of the event handles. | |
| 303 *request_report_event = client_request_report_event.Release(); | |
| 304 *report_complete_event = client_report_complete_event.Release(); | |
| 305 | |
| 306 return true; | |
| 307 } | |
| 308 | |
| 309 void Registrar::OnRequestReport(Entry* entry) { | |
| 310 // This method must not acquire |mutex_|. See OnProcessExit. | |
| 311 delegate_->GenerateReportForClient(entry->process.get()); | |
| 312 if (!SetEvent(entry->report_complete_event.get())) | |
| 313 PLOG(ERROR) << "SetEvent"; | |
| 314 } | |
| 315 | |
| 316 void Registrar::OnProcessExit(Entry* entry) { | |
| 317 AutoMutex lock(mutex_.get()); | |
| 318 | |
| 319 PointerVector<Entry>::iterator it = | |
| 320 std::find(entries_.begin(), entries_.end(), entry); | |
| 321 | |
| 322 // If ~Registrar is running concurrently with ProcessExitCallback it's | |
| 323 // possible this entry is already being cleaned up in the destructor. We | |
| 324 // return now to allow the destructor (which will block until our return using | |
| 325 // UnregisterWaitEx) to finish the job. | |
| 326 if (it == entries_.end()) | |
| 327 return; | |
| 328 | |
| 329 // Blocking call. We are holding |mutex_| as, until this call completes, it's | |
| 330 // possible to process a callback via ReportRequestCallback. Therefore, | |
| 331 // ReportRequestCallback must never acquire |mutex_|. | |
| 332 if (!UnregisterWaitEx((*it)->request_report_event_wait_handle, | |
| 333 INVALID_HANDLE_VALUE)) { | |
| 334 PLOG(FATAL) << "UnregisterWaitEx"; | |
| 335 } | |
| 336 | |
| 337 // Non-blocking call, since we are calling from within the callback. We expect | |
| 338 // ERROR_IO_PENDING (indicating that there is a running callback - us). Since | |
| 339 // the wait was registered with WT_EXECUTEONLYONCE, we know that there will | |
| 340 // not be another invocation. | |
| 341 if (!UnregisterWaitEx((*it)->process_exit_wait_handle, nullptr)) { | |
| 342 if (GetLastError() != ERROR_IO_PENDING) | |
| 343 PLOG(FATAL) << "UnregisterWaitEx"; | |
| 344 } | |
| 345 | |
| 346 delete *it; | |
| 347 entries_.erase(it); | |
| 348 } | |
| 349 | |
| 350 // static | |
| 351 bool Registrar::RegisterWaits(Entry* entry, | |
| 352 HANDLE* request_report_event_wait_handle, | |
| 353 HANDLE* process_exit_wait_handle) { | |
| 354 if (!RegisterWaitForSingleObject(request_report_event_wait_handle, | |
| 355 entry->request_report_event.get(), | |
| 356 &Registrar::ReportRequestCallback, | |
| 357 entry, | |
| 358 INFINITE, | |
| 359 WT_EXECUTELONGFUNCTION)) { | |
| 360 PLOG(ERROR) << "RegisterWaitForSingleObject"; | |
| 361 return false; | |
| 362 } | |
| 363 | |
| 364 // Because a process remains signaled, it is essential to pass | |
| 365 // WT_EXECUTEONLYONCE. Otherwise the callback could execute multiple times | |
| 366 // before we unregister the wait. | |
| 367 if (!RegisterWaitForSingleObject(process_exit_wait_handle, | |
| 368 entry->process.get(), | |
| 369 &Registrar::ProcessExitCallback, | |
| 370 entry, | |
| 371 INFINITE, | |
| 372 WT_EXECUTEONLYONCE)) { | |
| 373 PLOG(ERROR) << "RegisterWaitForSingleObject"; | |
| 374 return false; | |
| 375 } | |
| 376 | |
| 377 return true; | |
| 378 } | |
| 379 | |
| 380 // static | |
| 381 VOID CALLBACK Registrar::ReportRequestCallback(PVOID lpParameter, | |
| 382 BOOLEAN /* TimerOrWaitFired */) { | |
| 383 // This method must not acquire |mutex_|. See OnProcessExit. | |
| 384 | |
| 385 // |entry| is valid because, before deleting it (in OnProcessExit or | |
| 386 // ~Registrar), we do a blocking UnregisterWaitEx. | |
| 387 Entry* entry = reinterpret_cast<Entry*>(lpParameter); | |
| 388 entry->registrar->OnRequestReport(entry); | |
| 389 } | |
| 390 | |
| 391 // static | |
| 392 VOID CALLBACK Registrar::ProcessExitCallback(PVOID lpParameter, | |
| 393 BOOLEAN /* TimerOrWaitFired */) { | |
| 394 // |entry| is valid because, before deleting it (in ~Registrar), we do a | |
| 395 // blocking UnregisterWaitEx. In OnProcessExit we do a non-blocking | |
| 396 // UnregisterWaitEx, but since this callback can only be executed once | |
| 397 // (WT_EXECUTEONLYONCE) and we are executing it now, we know we're not racing | |
| 398 // against it. | |
| 399 Entry* entry = reinterpret_cast<Entry*>(lpParameter); | |
| 400 entry->registrar->OnProcessExit(entry); | |
| 401 } | |
| 402 | |
| 403 } // namespace crashpad | |
| OLD | NEW |