| Index: sandbox/win/src/broker_services.cc
|
| diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc
|
| index 68d68d829325d4b95ce6406d6638e3198eb96761..4baa20bf08b63a33d4f3d1a34de6e2caa8f21409 100644
|
| --- a/sandbox/win/src/broker_services.cc
|
| +++ b/sandbox/win/src/broker_services.cc
|
| @@ -8,6 +8,7 @@
|
|
|
| #include "base/logging.h"
|
| #include "base/memory/scoped_ptr.h"
|
| +#include "base/stl_util.h"
|
| #include "base/threading/platform_thread.h"
|
| #include "base/win/scoped_handle.h"
|
| #include "base/win/scoped_process_information.h"
|
| @@ -55,22 +56,47 @@ enum {
|
| // Helper structure that allows the Broker to associate a job notification
|
| // with a job object and with a policy.
|
| struct JobTracker {
|
| - HANDLE job;
|
| - sandbox::PolicyBase* policy;
|
| - JobTracker(HANDLE cjob, sandbox::PolicyBase* cpolicy)
|
| - : job(cjob), policy(cpolicy) {
|
| + JobTracker(base::win::ScopedHandle job, sandbox::PolicyBase* policy)
|
| + : job(job.Pass()), policy(policy) {
|
| + }
|
| + ~JobTracker() {
|
| + FreeResources();
|
| }
|
| +
|
| + // Releases the Job and notifies the associated Policy object to release its
|
| + // resources as well.
|
| + void FreeResources();
|
| +
|
| + base::win::ScopedHandle job;
|
| + sandbox::PolicyBase* policy;
|
| };
|
|
|
| +void JobTracker::FreeResources() {
|
| + if (policy) {
|
| + BOOL res = ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK);
|
| + DCHECK(res);
|
| + // Closing the job causes the target process to be destroyed so this needs
|
| + // to happen before calling OnJobEmpty().
|
| + HANDLE stale_job_handle = job.Get();
|
| + job.Close();
|
| +
|
| + // In OnJobEmpty() we don't actually use the job handle directly.
|
| + policy->OnJobEmpty(stale_job_handle);
|
| + policy->Release();
|
| + policy = NULL;
|
| + }
|
| +}
|
| +
|
| // Helper structure that allows the broker to track peer processes
|
| struct PeerTracker {
|
| + PeerTracker(DWORD process_id, HANDLE broker_job_port)
|
| + : wait_object(NULL), id(process_id), job_port(broker_job_port) {
|
| + }
|
| +
|
| HANDLE wait_object;
|
| base::win::ScopedHandle process;
|
| DWORD id;
|
| HANDLE job_port;
|
| - PeerTracker(DWORD process_id, HANDLE broker_job_port)
|
| - : wait_object(NULL), id(process_id), job_port(broker_job_port) {
|
| - }
|
| };
|
|
|
| void DeregisterPeerTracker(PeerTracker* peer) {
|
| @@ -124,30 +150,38 @@ uint32_t GenerateTokenCacheKey(const sandbox::PolicyBase* policy) {
|
|
|
| namespace sandbox {
|
|
|
| -BrokerServicesBase::BrokerServicesBase()
|
| - : job_port_(NULL),
|
| - no_targets_(NULL),
|
| - job_thread_(NULL),
|
| - thread_pool_(NULL) {
|
| +// TODO(rvargas): Replace this structure with a std::pair of ScopedHandles.
|
| +struct BrokerServicesBase::TokenPair {
|
| + TokenPair(base::win::ScopedHandle initial_token,
|
| + base::win::ScopedHandle lockdown_token)
|
| + : initial(initial_token.Pass()),
|
| + lockdown(lockdown_token.Pass()) {
|
| + }
|
| +
|
| + base::win::ScopedHandle initial;
|
| + base::win::ScopedHandle lockdown;
|
| +};
|
| +
|
| +BrokerServicesBase::BrokerServicesBase() : thread_pool_(NULL) {
|
| }
|
|
|
| // The broker uses a dedicated worker thread that services the job completion
|
| // port to perform policy notifications and associated cleanup tasks.
|
| ResultCode BrokerServicesBase::Init() {
|
| - if ((NULL != job_port_) || (NULL != thread_pool_))
|
| + if (job_port_.IsValid() || (NULL != thread_pool_))
|
| return SBOX_ERROR_UNEXPECTED_CALL;
|
|
|
| ::InitializeCriticalSection(&lock_);
|
|
|
| - job_port_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
| - if (NULL == job_port_)
|
| + job_port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0));
|
| + if (!job_port_.IsValid())
|
| return SBOX_ERROR_GENERIC;
|
|
|
| - no_targets_ = ::CreateEventW(NULL, TRUE, FALSE, NULL);
|
| + no_targets_.Set(::CreateEventW(NULL, TRUE, FALSE, NULL));
|
|
|
| - job_thread_ = ::CreateThread(NULL, 0, // Default security and stack.
|
| - TargetEventsThread, this, NULL, NULL);
|
| - if (NULL == job_thread_)
|
| + job_thread_.Set(::CreateThread(NULL, 0, // Default security and stack.
|
| + TargetEventsThread, this, NULL, NULL));
|
| + if (!job_thread_.IsValid())
|
| return SBOX_ERROR_GENERIC;
|
|
|
| return SBOX_ALL_OK;
|
| @@ -160,31 +194,24 @@ ResultCode BrokerServicesBase::Init() {
|
| // wait for threads here.
|
| BrokerServicesBase::~BrokerServicesBase() {
|
| // If there is no port Init() was never called successfully.
|
| - if (!job_port_)
|
| + if (!job_port_.IsValid())
|
| return;
|
|
|
| // Closing the port causes, that no more Job notifications are delivered to
|
| // the worker thread and also causes the thread to exit. This is what we
|
| // want to do since we are going to close all outstanding Jobs and notifying
|
| // the policy objects ourselves.
|
| - ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE);
|
| - ::CloseHandle(job_port_);
|
| + ::PostQueuedCompletionStatus(job_port_.Get(), 0, THREAD_CTRL_QUIT, FALSE);
|
|
|
| - if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) {
|
| + if (job_thread_.IsValid() &&
|
| + WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_.Get(), 1000)) {
|
| // Cannot clean broker services.
|
| NOTREACHED();
|
| return;
|
| }
|
|
|
| - JobTrackerList::iterator it;
|
| - for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) {
|
| - JobTracker* tracker = (*it);
|
| - FreeResources(tracker);
|
| - delete tracker;
|
| - }
|
| - ::CloseHandle(job_thread_);
|
| + STLDeleteElements(&tracker_list_);
|
| delete thread_pool_;
|
| - ::CloseHandle(no_targets_);
|
|
|
| // Cancel the wait events and delete remaining peer trackers.
|
| for (PeerTrackerMap::iterator it = peer_map_.begin();
|
| @@ -192,16 +219,10 @@ BrokerServicesBase::~BrokerServicesBase() {
|
| DeregisterPeerTracker(it->second);
|
| }
|
|
|
| - // If job_port_ isn't NULL, assumes that the lock has been initialized.
|
| - if (job_port_)
|
| - ::DeleteCriticalSection(&lock_);
|
| + ::DeleteCriticalSection(&lock_);
|
|
|
| // Close any token in the cache.
|
| - for (TokenCacheMap::iterator it = token_cache_.begin();
|
| - it != token_cache_.end(); ++it) {
|
| - ::CloseHandle(it->second.first);
|
| - ::CloseHandle(it->second.second);
|
| - }
|
| + STLDeleteValues(&token_cache_);
|
| }
|
|
|
| TargetPolicy* BrokerServicesBase::CreatePolicy() {
|
| @@ -210,21 +231,6 @@ TargetPolicy* BrokerServicesBase::CreatePolicy() {
|
| return new PolicyBase;
|
| }
|
|
|
| -void BrokerServicesBase::FreeResources(JobTracker* tracker) {
|
| - if (NULL != tracker->policy) {
|
| - BOOL res = ::TerminateJobObject(tracker->job, SBOX_ALL_OK);
|
| - DCHECK(res);
|
| - // Closing the job causes the target process to be destroyed so this
|
| - // needs to happen before calling OnJobEmpty().
|
| - res = ::CloseHandle(tracker->job);
|
| - DCHECK(res);
|
| - // In OnJobEmpty() we don't actually use the job handle directly.
|
| - tracker->policy->OnJobEmpty(tracker->job);
|
| - tracker->policy->Release();
|
| - tracker->policy = NULL;
|
| - }
|
| -}
|
| -
|
| // The worker thread stays in a loop waiting for asynchronous notifications
|
| // from the job objects. Right now we only care about knowing when the last
|
| // process on a job terminates, but in general this is the place to tell
|
| @@ -236,8 +242,8 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
|
| base::PlatformThread::SetName("BrokerEvent");
|
|
|
| BrokerServicesBase* broker = reinterpret_cast<BrokerServicesBase*>(param);
|
| - HANDLE port = broker->job_port_;
|
| - HANDLE no_targets = broker->no_targets_;
|
| + HANDLE port = broker->job_port_.Get();
|
| + HANDLE no_targets = broker->no_targets_.Get();
|
|
|
| int target_counter = 0;
|
| ::ResetEvent(no_targets);
|
| @@ -247,11 +253,12 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
|
| ULONG_PTR key = 0;
|
| LPOVERLAPPED ovl = NULL;
|
|
|
| - if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE))
|
| + if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE)) {
|
| // this call fails if the port has been closed before we have a
|
| // chance to service the last packet which is 'exit' anyway so
|
| // this is not an error.
|
| return 1;
|
| + }
|
|
|
| if (key > THREAD_CTRL_LAST) {
|
| // The notification comes from a job object. There are nine notifications
|
| @@ -265,7 +272,7 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
|
| // to appear out of thin air in this job, it safe to assume that
|
| // we can tell the policy to destroy the target object, and for
|
| // us to release our reference to the policy object.
|
| - FreeResources(tracker);
|
| + tracker->FreeResources();
|
| break;
|
| }
|
|
|
| @@ -297,7 +304,7 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
|
| }
|
|
|
| case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: {
|
| - BOOL res = ::TerminateJobObject(tracker->job,
|
| + BOOL res = ::TerminateJobObject(tracker->job.Get(),
|
| SBOX_FATAL_MEMORY_EXCEEDED);
|
| DCHECK(res);
|
| break;
|
| @@ -367,33 +374,31 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
|
| // process launch.
|
| uint32_t token_key = GenerateTokenCacheKey(policy_base);
|
| TokenCacheMap::iterator it = token_cache_.find(token_key);
|
| - HANDLE initial_token_temp;
|
| - HANDLE lockdown_token_temp;
|
| + TokenPair* tokens;
|
| if (it != token_cache_.end()) {
|
| - initial_token_temp = it->second.first;
|
| - lockdown_token_temp = it->second.second;
|
| + tokens = it->second;
|
| } else {
|
| result = policy_base->MakeTokens(&initial_token, &lockdown_token);
|
| if (SBOX_ALL_OK != result)
|
| return result;
|
| - token_cache_[token_key] =
|
| - std::make_pair(initial_token.Get(), lockdown_token.Get());
|
| - initial_token_temp = initial_token.Take();
|
| - lockdown_token_temp = lockdown_token.Take();
|
| +
|
| + tokens = new TokenPair(initial_token.Pass(), lockdown_token.Pass());
|
| + token_cache_[token_key] = tokens;
|
| }
|
|
|
| - if (!::DuplicateToken(initial_token_temp, SecurityImpersonation,
|
| - &initial_token_temp)) {
|
| + HANDLE temp_token;
|
| + if (!::DuplicateToken(tokens->initial.Get(), SecurityImpersonation,
|
| + &temp_token)) {
|
| return SBOX_ERROR_GENERIC;
|
| }
|
| - initial_token.Set(initial_token_temp);
|
| + initial_token.Set(temp_token);
|
|
|
| - if (!::DuplicateTokenEx(lockdown_token_temp, TOKEN_ALL_ACCESS, 0,
|
| + if (!::DuplicateTokenEx(tokens->lockdown.Get(), TOKEN_ALL_ACCESS, 0,
|
| SecurityIdentification, TokenPrimary,
|
| - &lockdown_token_temp)) {
|
| + &temp_token)) {
|
| return SBOX_ERROR_GENERIC;
|
| }
|
| - lockdown_token.Set(lockdown_token_temp);
|
| + lockdown_token.Set(temp_token);
|
| } else {
|
| result = policy_base->MakeTokens(&initial_token, &lockdown_token);
|
| if (SBOX_ALL_OK != result)
|
| @@ -523,11 +528,12 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
|
| // the job object generates notifications using the completion port.
|
| policy_base->AddRef();
|
| if (job.IsValid()) {
|
| - scoped_ptr<JobTracker> tracker(new JobTracker(job.Take(), policy_base));
|
| + scoped_ptr<JobTracker> tracker(new JobTracker(job.Pass(), policy_base));
|
|
|
| // There is no obvious recovery after failure here. Previous version with
|
| // SpawnCleanup() caused deletion of TargetProcess twice. crbug.com/480639
|
| - CHECK(AssociateCompletionPort(tracker->job, job_port_, tracker.get()));
|
| + CHECK(AssociateCompletionPort(tracker->job.Get(), job_port_.Get(),
|
| + tracker.get()));
|
|
|
| // Save the tracker because in cleanup we might need to force closing
|
| // the Jobs.
|
| @@ -538,7 +544,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
|
| // never get a message that this target is being terminated thus we should
|
| // not block WaitForAllTargets until we have at least one target with job.
|
| if (child_process_ids_.empty())
|
| - ::SetEvent(no_targets_);
|
| + ::SetEvent(no_targets_.Get());
|
| // We can not track the life time of such processes and it is responsibility
|
| // of the host application to make sure that spawned targets without jobs
|
| // are terminated when the main application don't need them anymore.
|
| @@ -553,7 +559,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
|
|
|
|
|
| ResultCode BrokerServicesBase::WaitForAllTargets() {
|
| - ::WaitForSingleObject(no_targets_, INFINITE);
|
| + ::WaitForSingleObject(no_targets_.Get(), INFINITE);
|
| return SBOX_ALL_OK;
|
| }
|
|
|
| @@ -573,7 +579,7 @@ VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN timeout) {
|
|
|
| ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
|
| scoped_ptr<PeerTracker> peer(new PeerTracker(::GetProcessId(peer_process),
|
| - job_port_));
|
| + job_port_.Get()));
|
| if (!peer->id)
|
| return SBOX_ERROR_GENERIC;
|
|
|
|
|