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 |