OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sandbox/src/broker_services.h" | 5 #include "sandbox/src/broker_services.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
9 #include "base/threading/platform_thread.h" | 9 #include "base/threading/platform_thread.h" |
10 #include "base/win/scoped_handle.h" | 10 #include "base/win/scoped_handle.h" |
(...skipping 23 matching lines...) Expand all Loading... |
34 target->Terminate(); | 34 target->Terminate(); |
35 delete target; | 35 delete target; |
36 ::SetLastError(error); | 36 ::SetLastError(error); |
37 return sandbox::SBOX_ERROR_GENERIC; | 37 return sandbox::SBOX_ERROR_GENERIC; |
38 } | 38 } |
39 | 39 |
40 // the different commands that you can send to the worker thread that | 40 // the different commands that you can send to the worker thread that |
41 // executes TargetEventsThread(). | 41 // executes TargetEventsThread(). |
42 enum { | 42 enum { |
43 THREAD_CTRL_NONE, | 43 THREAD_CTRL_NONE, |
| 44 THREAD_CTRL_REMOVE_PEER, |
44 THREAD_CTRL_QUIT, | 45 THREAD_CTRL_QUIT, |
45 THREAD_CTRL_LAST | 46 THREAD_CTRL_LAST, |
46 }; | 47 }; |
47 | 48 |
48 } | 49 // Helper structure that allows the Broker to associate a job notification |
| 50 // with a job object and with a policy. |
| 51 struct JobTracker { |
| 52 HANDLE job; |
| 53 sandbox::PolicyBase* policy; |
| 54 JobTracker(HANDLE cjob, sandbox::PolicyBase* cpolicy) |
| 55 : job(cjob), policy(cpolicy) { |
| 56 } |
| 57 }; |
| 58 |
| 59 // Helper structure that allows the broker to track peer processes |
| 60 struct PeerTracker { |
| 61 HANDLE wait_object; |
| 62 base::win::ScopedHandle process; |
| 63 DWORD id; |
| 64 HANDLE job_port; |
| 65 PeerTracker(DWORD process_id, HANDLE broker_job_port) |
| 66 : wait_object(NULL), id(process_id), job_port(broker_job_port) { |
| 67 } |
| 68 }; |
| 69 |
| 70 } // namespace |
49 | 71 |
50 namespace sandbox { | 72 namespace sandbox { |
51 | 73 |
52 BrokerServicesBase::BrokerServicesBase() | 74 BrokerServicesBase::BrokerServicesBase() |
53 : thread_pool_(NULL), job_port_(NULL), no_targets_(NULL), | 75 : thread_pool_(NULL), job_port_(NULL), no_targets_(NULL), |
54 job_thread_(NULL) { | 76 job_thread_(NULL) { |
55 } | 77 } |
56 | 78 |
57 // The broker uses a dedicated worker thread that services the job completion | 79 // The broker uses a dedicated worker thread that services the job completion |
58 // port to perform policy notifications and associated cleanup tasks. | 80 // port to perform policy notifications and associated cleanup tasks. |
(...skipping 19 matching lines...) Expand all Loading... |
78 | 100 |
79 // The destructor should only be called when the Broker process is terminating. | 101 // The destructor should only be called when the Broker process is terminating. |
80 // Since BrokerServicesBase is a singleton, this is called from the CRT | 102 // Since BrokerServicesBase is a singleton, this is called from the CRT |
81 // termination handlers, if this code lives on a DLL it is called during | 103 // termination handlers, if this code lives on a DLL it is called during |
82 // DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot | 104 // DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot |
83 // wait for threads here. | 105 // wait for threads here. |
84 BrokerServicesBase::~BrokerServicesBase() { | 106 BrokerServicesBase::~BrokerServicesBase() { |
85 // If there is no port Init() was never called successfully. | 107 // If there is no port Init() was never called successfully. |
86 if (!job_port_) | 108 if (!job_port_) |
87 return; | 109 return; |
| 110 |
88 // Closing the port causes, that no more Job notifications are delivered to | 111 // Closing the port causes, that no more Job notifications are delivered to |
89 // the worker thread and also causes the thread to exit. This is what we | 112 // the worker thread and also causes the thread to exit. This is what we |
90 // want to do since we are going to close all outstanding Jobs and notifying | 113 // want to do since we are going to close all outstanding Jobs and notifying |
91 // the policy objects ourselves. | 114 // the policy objects ourselves. |
92 ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE); | 115 ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE); |
93 ::CloseHandle(job_port_); | 116 ::CloseHandle(job_port_); |
94 | 117 |
95 if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) { | 118 if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) { |
96 // Cannot clean broker services. | 119 // Cannot clean broker services. |
97 NOTREACHED(); | 120 NOTREACHED(); |
98 return; | 121 return; |
99 } | 122 } |
100 | 123 |
101 JobTrackerList::iterator it; | 124 JobTrackerList::iterator it; |
102 for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) { | 125 for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) { |
103 JobTracker* tracker = (*it); | 126 JobTracker* tracker = (*it); |
104 FreeResources(tracker); | 127 FreeResources(tracker); |
105 delete tracker; | 128 delete tracker; |
106 } | 129 } |
107 ::CloseHandle(job_thread_); | 130 ::CloseHandle(job_thread_); |
108 delete thread_pool_; | 131 delete thread_pool_; |
109 ::CloseHandle(no_targets_); | 132 ::CloseHandle(no_targets_); |
| 133 |
| 134 // Cancel the wait events and delete remaining peer trackers. |
| 135 for (PeerTrackerMap::iterator it = peer_map_.begin(); |
| 136 it != peer_map_.end(); ++it) { |
| 137 // Deregistration shouldn't fail, but we leak rather than crash if it does. |
| 138 if (::UnregisterWaitEx(it->second->wait_object, INVALID_HANDLE_VALUE)) { |
| 139 delete it->second; |
| 140 } else { |
| 141 NOTREACHED(); |
| 142 } |
| 143 } |
| 144 |
110 // If job_port_ isn't NULL, assumes that the lock has been initialized. | 145 // If job_port_ isn't NULL, assumes that the lock has been initialized. |
111 if (job_port_) | 146 if (job_port_) |
112 ::DeleteCriticalSection(&lock_); | 147 ::DeleteCriticalSection(&lock_); |
113 } | 148 } |
114 | 149 |
115 TargetPolicy* BrokerServicesBase::CreatePolicy() { | 150 TargetPolicy* BrokerServicesBase::CreatePolicy() { |
116 // If you change the type of the object being created here you must also | 151 // If you change the type of the object being created here you must also |
117 // change the downcast to it in SpawnTarget(). | 152 // change the downcast to it in SpawnTarget(). |
118 return new PolicyBase; | 153 return new PolicyBase; |
119 } | 154 } |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: { | 237 case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: { |
203 break; | 238 break; |
204 } | 239 } |
205 | 240 |
206 default: { | 241 default: { |
207 NOTREACHED(); | 242 NOTREACHED(); |
208 break; | 243 break; |
209 } | 244 } |
210 } | 245 } |
211 | 246 |
| 247 } else if (THREAD_CTRL_REMOVE_PEER == key) { |
| 248 // Remove a process from our list of peers. |
| 249 AutoLock lock(&broker->lock_); |
| 250 PeerTrackerMap::iterator it = |
| 251 broker->peer_map_.find(reinterpret_cast<DWORD>(ovl)); |
| 252 // This shouldn't fail, but if it does leak the memory rather than crash. |
| 253 if (::UnregisterWaitEx(it->second->wait_object, INVALID_HANDLE_VALUE)) { |
| 254 delete it->second; |
| 255 broker->peer_map_.erase(it); |
| 256 } else { |
| 257 NOTREACHED(); |
| 258 } |
| 259 |
212 } else if (THREAD_CTRL_QUIT == key) { | 260 } else if (THREAD_CTRL_QUIT == key) { |
213 // The broker object is being destroyed so the thread needs to exit. | 261 // The broker object is being destroyed so the thread needs to exit. |
214 return 0; | 262 return 0; |
| 263 |
215 } else { | 264 } else { |
216 // We have not implemented more commands. | 265 // We have not implemented more commands. |
217 NOTREACHED(); | 266 NOTREACHED(); |
218 } | 267 } |
219 } | 268 } |
220 | 269 |
221 NOTREACHED(); | 270 NOTREACHED(); |
222 return 0; | 271 return 0; |
223 } | 272 } |
224 | 273 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 } | 354 } |
306 | 355 |
307 | 356 |
308 ResultCode BrokerServicesBase::WaitForAllTargets() { | 357 ResultCode BrokerServicesBase::WaitForAllTargets() { |
309 ::WaitForSingleObject(no_targets_, INFINITE); | 358 ::WaitForSingleObject(no_targets_, INFINITE); |
310 return SBOX_ALL_OK; | 359 return SBOX_ALL_OK; |
311 } | 360 } |
312 | 361 |
313 bool BrokerServicesBase::IsActiveTarget(DWORD process_id) { | 362 bool BrokerServicesBase::IsActiveTarget(DWORD process_id) { |
314 AutoLock lock(&lock_); | 363 AutoLock lock(&lock_); |
315 return child_process_ids_.find(process_id) != child_process_ids_.end(); | 364 return child_process_ids_.find(process_id) != child_process_ids_.end() || |
| 365 peer_map_.find(process_id) != peer_map_.end(); |
| 366 } |
| 367 |
| 368 VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN) { |
| 369 PeerTracker* peer = reinterpret_cast<PeerTracker*>(parameter); |
| 370 // Don't check the return code because we this may fail (safely) at shutdown. |
| 371 ::PostQueuedCompletionStatus(peer->job_port, 0, THREAD_CTRL_REMOVE_PEER, |
| 372 reinterpret_cast<LPOVERLAPPED>(peer->id)); |
| 373 } |
| 374 |
| 375 ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) { |
| 376 scoped_ptr<PeerTracker> peer(new PeerTracker(::GetProcessId(peer_process), |
| 377 job_port_)); |
| 378 if (!peer->id) |
| 379 return SBOX_ERROR_GENERIC; |
| 380 |
| 381 if (!::DuplicateHandle(::GetCurrentProcess(), peer_process, |
| 382 ::GetCurrentProcess(), peer->process.Receive(), |
| 383 SYNCHRONIZE, FALSE, 0)) { |
| 384 return SBOX_ERROR_GENERIC; |
| 385 } |
| 386 |
| 387 AutoLock lock(&lock_); |
| 388 if (!peer_map_.insert(std::make_pair(peer->id, peer.get())).second) |
| 389 return SBOX_ERROR_BAD_PARAMS; |
| 390 |
| 391 if (!::RegisterWaitForSingleObject(&peer->wait_object, |
| 392 peer->process, RemovePeer, |
| 393 peer.get(), INFINITE, WT_EXECUTEONLYONCE | |
| 394 WT_EXECUTEINWAITTHREAD)) { |
| 395 peer_map_.erase(peer->id); |
| 396 return SBOX_ERROR_GENERIC; |
| 397 } |
| 398 |
| 399 // Leak the pointer since it will be cleaned up by the callback. |
| 400 peer.release(); |
| 401 return SBOX_ALL_OK; |
316 } | 402 } |
317 | 403 |
318 } // namespace sandbox | 404 } // namespace sandbox |
OLD | NEW |