Chromium Code Reviews| 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/win/src/broker_services.h" | 5 #include "sandbox/win/src/broker_services.h" |
| 6 | 6 |
| 7 #include <AclAPI.h> | 7 #include <AclAPI.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 | 9 |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 46 // executes TargetEventsThread(). | 46 // executes TargetEventsThread(). |
| 47 enum { | 47 enum { |
| 48 THREAD_CTRL_NONE, | 48 THREAD_CTRL_NONE, |
| 49 THREAD_CTRL_QUIT, | 49 THREAD_CTRL_QUIT, |
| 50 THREAD_CTRL_LAST, | 50 THREAD_CTRL_LAST, |
| 51 }; | 51 }; |
| 52 | 52 |
| 53 // Helper structure that allows the Broker to associate a job notification | 53 // Helper structure that allows the Broker to associate a job notification |
| 54 // with a job object and with a policy. | 54 // with a job object and with a policy. |
| 55 struct JobTracker { | 55 struct JobTracker { |
| 56 JobTracker(base::win::ScopedHandle job, sandbox::PolicyBase* policy) | 56 JobTracker(base::win::ScopedHandle job, |
| 57 scoped_refptr<sandbox::PolicyBase> policy) | |
| 57 : job(std::move(job)), policy(policy) {} | 58 : job(std::move(job)), policy(policy) {} |
| 58 ~JobTracker() { | 59 ~JobTracker() { |
| 59 FreeResources(); | 60 FreeResources(); |
| 60 } | 61 } |
| 61 | 62 |
| 62 // Releases the Job and notifies the associated Policy object to release its | 63 // Releases the Job and notifies the associated Policy object to release its |
| 63 // resources as well. | 64 // resources as well. |
| 64 void FreeResources(); | 65 void FreeResources(); |
| 65 | 66 |
| 66 base::win::ScopedHandle job; | 67 base::win::ScopedHandle job; |
| 67 sandbox::PolicyBase* policy; | 68 scoped_refptr<sandbox::PolicyBase> policy; |
| 68 }; | 69 }; |
| 69 | 70 |
| 70 void JobTracker::FreeResources() { | 71 void JobTracker::FreeResources() { |
| 71 if (policy) { | 72 if (policy) { |
| 72 BOOL res = ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK); | 73 BOOL res = ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK); |
| 73 DCHECK(res); | 74 DCHECK(res); |
| 74 // Closing the job causes the target process to be destroyed so this needs | 75 // Closing the job causes the target process to be destroyed so this needs |
| 75 // to happen before calling OnJobEmpty(). | 76 // to happen before calling OnJobEmpty(). |
| 76 HANDLE stale_job_handle = job.Get(); | 77 HANDLE stale_job_handle = job.Get(); |
| 77 job.Close(); | 78 job.Close(); |
| 78 | 79 |
| 79 // In OnJobEmpty() we don't actually use the job handle directly. | 80 // In OnJobEmpty() we don't actually use the job handle directly. |
| 80 policy->OnJobEmpty(stale_job_handle); | 81 policy->OnJobEmpty(stale_job_handle); |
| 81 policy->Release(); | 82 policy = nullptr; |
| 82 policy = NULL; | |
| 83 } | 83 } |
| 84 } | 84 } |
| 85 | 85 |
| 86 } // namespace | 86 } // namespace |
| 87 | 87 |
| 88 namespace sandbox { | 88 namespace sandbox { |
| 89 | 89 |
| 90 BrokerServicesBase::BrokerServicesBase() {} | 90 BrokerServicesBase::BrokerServicesBase() {} |
| 91 | 91 |
| 92 // The broker uses a dedicated worker thread that services the job completion | 92 // The broker uses a dedicated worker thread that services the job completion |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 133 NOTREACHED(); | 133 NOTREACHED(); |
| 134 return; | 134 return; |
| 135 } | 135 } |
| 136 | 136 |
| 137 tracker_list_.clear(); | 137 tracker_list_.clear(); |
| 138 thread_pool_.reset(); | 138 thread_pool_.reset(); |
| 139 | 139 |
| 140 ::DeleteCriticalSection(&lock_); | 140 ::DeleteCriticalSection(&lock_); |
| 141 } | 141 } |
| 142 | 142 |
| 143 TargetPolicy* BrokerServicesBase::CreatePolicy() { | 143 scoped_refptr<TargetPolicy> BrokerServicesBase::CreatePolicy() { |
| 144 // If you change the type of the object being created here you must also | 144 // If you change the type of the object being created here you must also |
| 145 // change the downcast to it in SpawnTarget(). | 145 // change the downcast to it in SpawnTarget(). |
| 146 return new PolicyBase; | 146 scoped_refptr<TargetPolicy> policy(new PolicyBase); |
| 147 // PolicyBase starts with refcount 1. | |
| 148 policy->Release(); | |
| 149 return policy; | |
| 147 } | 150 } |
| 148 | 151 |
| 149 // The worker thread stays in a loop waiting for asynchronous notifications | 152 // The worker thread stays in a loop waiting for asynchronous notifications |
| 150 // from the job objects. Right now we only care about knowing when the last | 153 // from the job objects. Right now we only care about knowing when the last |
| 151 // process on a job terminates, but in general this is the place to tell | 154 // process on a job terminates, but in general this is the place to tell |
| 152 // the policy about events. | 155 // the policy about events. |
| 153 DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { | 156 DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { |
| 154 if (NULL == param) | 157 if (NULL == param) |
| 155 return 1; | 158 return 1; |
| 156 | 159 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 259 } | 262 } |
| 260 | 263 |
| 261 NOTREACHED(); | 264 NOTREACHED(); |
| 262 return 0; | 265 return 0; |
| 263 } | 266 } |
| 264 | 267 |
| 265 // SpawnTarget does all the interesting sandbox setup and creates the target | 268 // SpawnTarget does all the interesting sandbox setup and creates the target |
| 266 // process inside the sandbox. | 269 // process inside the sandbox. |
| 267 ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, | 270 ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, |
| 268 const wchar_t* command_line, | 271 const wchar_t* command_line, |
| 269 TargetPolicy* policy, | 272 scoped_refptr<TargetPolicy> policy, |
| 270 ResultCode* last_warning, | 273 ResultCode* last_warning, |
| 271 DWORD* last_error, | 274 DWORD* last_error, |
| 272 PROCESS_INFORMATION* target_info) { | 275 PROCESS_INFORMATION* target_info) { |
| 273 if (!exe_path) | 276 if (!exe_path) |
| 274 return SBOX_ERROR_BAD_PARAMS; | 277 return SBOX_ERROR_BAD_PARAMS; |
| 275 | 278 |
| 276 if (!policy) | 279 if (!policy) |
| 277 return SBOX_ERROR_BAD_PARAMS; | 280 return SBOX_ERROR_BAD_PARAMS; |
| 278 | 281 |
| 279 // Even though the resources touched by SpawnTarget can be accessed in | 282 // Even though the resources touched by SpawnTarget can be accessed in |
| 280 // multiple threads, the method itself cannot be called from more than | 283 // multiple threads, the method itself cannot be called from more than |
| 281 // 1 thread. This is to protect the global variables used while setting up | 284 // 1 thread. This is to protect the global variables used while setting up |
| 282 // the child process. | 285 // the child process. |
| 283 static DWORD thread_id = ::GetCurrentThreadId(); | 286 static DWORD thread_id = ::GetCurrentThreadId(); |
| 284 DCHECK(thread_id == ::GetCurrentThreadId()); | 287 DCHECK(thread_id == ::GetCurrentThreadId()); |
| 285 *last_warning = SBOX_ALL_OK; | 288 *last_warning = SBOX_ALL_OK; |
| 286 | 289 |
| 287 AutoLock lock(&lock_); | 290 AutoLock lock(&lock_); |
| 288 | 291 |
| 289 // This downcast is safe as long as we control CreatePolicy() | 292 // This downcast is safe as long as we control CreatePolicy() |
| 290 PolicyBase* policy_base = static_cast<PolicyBase*>(policy); | 293 scoped_refptr<PolicyBase> policy_base(static_cast<PolicyBase*>(policy.get())); |
| 291 | 294 |
| 292 // Construct the tokens and the job object that we are going to associate | 295 // Construct the tokens and the job object that we are going to associate |
| 293 // with the soon to be created target process. | 296 // with the soon to be created target process. |
| 294 base::win::ScopedHandle initial_token; | 297 base::win::ScopedHandle initial_token; |
| 295 base::win::ScopedHandle lockdown_token; | 298 base::win::ScopedHandle lockdown_token; |
| 296 base::win::ScopedHandle lowbox_token; | 299 base::win::ScopedHandle lowbox_token; |
| 297 ResultCode result = SBOX_ALL_OK; | 300 ResultCode result = SBOX_ALL_OK; |
| 298 | 301 |
| 299 result = | 302 result = |
| 300 policy_base->MakeTokens(&initial_token, &lockdown_token, &lowbox_token); | 303 policy_base->MakeTokens(&initial_token, &lockdown_token, &lowbox_token); |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 432 result = policy_base->AddTarget(target); | 435 result = policy_base->AddTarget(target); |
| 433 | 436 |
| 434 if (result != SBOX_ALL_OK) { | 437 if (result != SBOX_ALL_OK) { |
| 435 *last_error = ::GetLastError(); | 438 *last_error = ::GetLastError(); |
| 436 SpawnCleanup(target); | 439 SpawnCleanup(target); |
| 437 return result; | 440 return result; |
| 438 } | 441 } |
| 439 | 442 |
| 440 // We are going to keep a pointer to the policy because we'll call it when | 443 // We are going to keep a pointer to the policy because we'll call it when |
| 441 // the job object generates notifications using the completion port. | 444 // the job object generates notifications using the completion port. |
| 442 policy_base->AddRef(); | |
| 443 if (job.IsValid()) { | 445 if (job.IsValid()) { |
| 444 std::unique_ptr<JobTracker> tracker = | 446 std::unique_ptr<JobTracker> tracker = |
| 445 base::MakeUnique<JobTracker>(std::move(job), policy_base); | 447 base::MakeUnique<JobTracker>(std::move(job), policy_base); |
| 446 | 448 |
| 447 // There is no obvious recovery after failure here. Previous version with | 449 // There is no obvious recovery after failure here. Previous version with |
| 448 // SpawnCleanup() caused deletion of TargetProcess twice. crbug.com/480639 | 450 // SpawnCleanup() caused deletion of TargetProcess twice. crbug.com/480639 |
| 449 CHECK(AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), | 451 CHECK(AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), |
| 450 tracker.get())); | 452 tracker.get())); |
| 451 | 453 |
| 452 // Save the tracker because in cleanup we might need to force closing | 454 // Save the tracker because in cleanup we might need to force closing |
| 453 // the Jobs. | 455 // the Jobs. |
| 454 tracker_list_.push_back(std::move(tracker)); | 456 tracker_list_.push_back(std::move(tracker)); |
| 455 child_process_ids_.insert(process_info.process_id()); | 457 child_process_ids_.insert(process_info.process_id()); |
| 456 } else { | 458 } else { |
| 459 // Leak policy_base. This needs to outlive the child process, but there's | |
| 460 // nothing that tracks that lifetime properly if there's no job object. | |
| 461 // TODO(wfh): Find a way to make this have the correct lifetime. | |
| 462 policy_base->AddRef(); | |
|
Will Harris
2017/01/24 19:47:32
note: new change
| |
| 463 | |
| 457 // We have to signal the event once here because the completion port will | 464 // We have to signal the event once here because the completion port will |
| 458 // never get a message that this target is being terminated thus we should | 465 // never get a message that this target is being terminated thus we should |
| 459 // not block WaitForAllTargets until we have at least one target with job. | 466 // not block WaitForAllTargets until we have at least one target with job. |
| 460 if (child_process_ids_.empty()) | 467 if (child_process_ids_.empty()) |
| 461 ::SetEvent(no_targets_.Get()); | 468 ::SetEvent(no_targets_.Get()); |
| 462 } | 469 } |
| 463 | 470 |
| 464 *target_info = process_info.Take(); | 471 *target_info = process_info.Take(); |
| 465 return result; | 472 return result; |
| 466 } | 473 } |
| 467 | 474 |
| 468 | 475 |
| 469 ResultCode BrokerServicesBase::WaitForAllTargets() { | 476 ResultCode BrokerServicesBase::WaitForAllTargets() { |
| 470 ::WaitForSingleObject(no_targets_.Get(), INFINITE); | 477 ::WaitForSingleObject(no_targets_.Get(), INFINITE); |
| 471 return SBOX_ALL_OK; | 478 return SBOX_ALL_OK; |
| 472 } | 479 } |
| 473 | 480 |
| 474 bool BrokerServicesBase::IsActiveTarget(DWORD process_id) { | 481 bool BrokerServicesBase::IsActiveTarget(DWORD process_id) { |
| 475 AutoLock lock(&lock_); | 482 AutoLock lock(&lock_); |
| 476 return child_process_ids_.find(process_id) != child_process_ids_.end(); | 483 return child_process_ids_.find(process_id) != child_process_ids_.end(); |
| 477 } | 484 } |
| 478 | 485 |
| 479 } // namespace sandbox | 486 } // namespace sandbox |
| OLD | NEW |