Revert "Parallel process launching"
This reverts commit 18905f7828
.
Reason for revert: suspected cause of https://issues.chromium.org/362910001
Original change's description:
> Parallel process launching
>
> This change adds support for parallel process launching on Windows.
>
> Background: Processes are launched on a launcher thread, but this thread
> can become bottlenecked if launching multiple processes simultaneously.
> Most of the time spent in the launcher thread is observed to be in the
> CreateProcess call.
>
> This change makes process launching async by moving the target creation
> work to the thread pool. This frees up the launcher thread so it can
> accept tasks more quickly, and multiple target creations can now run in
> parallel.
>
> --enable-features=WinSboxParallelProcessLaunch
>
> Bug: 1499551
> Tests: sbox_integration_tests ParallelLaunchTest.*
> Change-Id: I7add6a6a68d31868a41fb433b223695ff36d8769
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5015584
> Reviewed-by: Will Harris <wfh@chromium.org>
> Commit-Queue: Ben Bamesberger <benb@microsoft.com>
> Reviewed-by: Alex Gough <ajgo@chromium.org>
> Reviewed-by: Caitlin Fischer <caitlinfischer@google.com>
> Reviewed-by: Bo Liu <boliu@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1348194}
Bug: 1499551, 362910001
Change-Id: I0f1a61945b48d4877d044a988f35b58a05076ac5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5836201
Reviewed-by: Bo Liu <boliu@chromium.org>
Commit-Queue: Will Harris <wfh@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Caitlin Fischer <caitlinfischer@google.com>
Cr-Commit-Position: refs/heads/main@{#1350864}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
82bf1f5eff
commit
cb8c3da2d9
base/win
content
browser
child_process_launcher.ccchild_process_launcher_helper.ccchild_process_launcher_helper.hchild_process_launcher_helper_win.cc
common
public
common
sandbox
policy
win
tools/metrics/histograms/metadata/others
@ -40,12 +40,6 @@ ScopedProcessInformation::ScopedProcessInformation(
|
||||
Set(process_info);
|
||||
}
|
||||
|
||||
ScopedProcessInformation::ScopedProcessInformation(ScopedProcessInformation&&) =
|
||||
default;
|
||||
|
||||
ScopedProcessInformation& ScopedProcessInformation::operator=(
|
||||
ScopedProcessInformation&&) = default;
|
||||
|
||||
ScopedProcessInformation::~ScopedProcessInformation() {
|
||||
Close();
|
||||
}
|
||||
|
@ -20,9 +20,6 @@ class BASE_EXPORT ScopedProcessInformation {
|
||||
ScopedProcessInformation();
|
||||
explicit ScopedProcessInformation(const PROCESS_INFORMATION& process_info);
|
||||
|
||||
ScopedProcessInformation(ScopedProcessInformation&&);
|
||||
ScopedProcessInformation& operator=(ScopedProcessInformation&&);
|
||||
|
||||
ScopedProcessInformation(const ScopedProcessInformation&) = delete;
|
||||
ScopedProcessInformation& operator=(const ScopedProcessInformation&) = delete;
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "base/memory/unsafe_shared_memory_region.h"
|
||||
#include "base/process/launch.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "base/tracing/protos/chrome_track_event.pbzero.h"
|
||||
#include "base/types/expected.h"
|
||||
#include "base/types/optional_util.h"
|
||||
@ -112,7 +111,6 @@ ChildProcessLauncher::ChildProcessLauncher(
|
||||
#endif
|
||||
{
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("startup", "ChildProcessLauncher", this);
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
should_launch_elevated_ = delegate->ShouldLaunchElevated();
|
||||
@ -169,8 +167,6 @@ void ChildProcessLauncher::Notify(ChildProcessLauncherHelper::Process process,
|
||||
#endif
|
||||
int error_code) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
TRACE_EVENT_NESTABLE_ASYNC_END0("startup", "ChildProcessLauncher", this);
|
||||
|
||||
starting_ = false;
|
||||
process_ = std::move(process);
|
||||
|
||||
|
@ -339,23 +339,17 @@ void ChildProcessLauncherHelper::LaunchOnLauncherThread() {
|
||||
}
|
||||
|
||||
if (is_synchronous_launch) {
|
||||
// The LastError is set on the launcher thread, but needs to be transferred
|
||||
// to the Client thread.
|
||||
PostLaunchOnLauncherThread(std::move(process),
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
::GetLastError(),
|
||||
#endif
|
||||
launch_result);
|
||||
PostLaunchOnLauncherThread(std::move(process), launch_result);
|
||||
}
|
||||
}
|
||||
|
||||
void ChildProcessLauncherHelper::PostLaunchOnLauncherThread(
|
||||
ChildProcessLauncherHelper::Process process,
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
DWORD last_error,
|
||||
#endif
|
||||
int launch_result) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// The LastError is set on the launcher thread, but needs to be transferred to
|
||||
// the Client thread.
|
||||
DWORD last_error = ::GetLastError();
|
||||
const bool launch_elevated = delegate_->ShouldLaunchElevated();
|
||||
#else
|
||||
const bool launch_elevated = false;
|
||||
|
@ -186,14 +186,6 @@ class ChildProcessLauncherHelper
|
||||
bool* is_synchronous_launch,
|
||||
int* launch_result);
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// This is the callback target that handles the result from
|
||||
// StartSandboxedProcess().
|
||||
void FinishStartSandboxedProcessOnLauncherThread(base::Process process,
|
||||
DWORD last_error,
|
||||
int launch_result);
|
||||
#endif
|
||||
|
||||
// Called right after the process has been launched, whether it was created
|
||||
// successfully or not. If the process launch is asynchronous, the process may
|
||||
// not yet be created. Platform specific.
|
||||
@ -203,9 +195,6 @@ class ChildProcessLauncherHelper
|
||||
|
||||
// Called once the process has been created, successfully or not.
|
||||
void PostLaunchOnLauncherThread(ChildProcessLauncherHelper::Process process,
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
DWORD last_error,
|
||||
#endif
|
||||
int launch_result);
|
||||
|
||||
// Posted by PostLaunchOnLauncherThread onto the client thread.
|
||||
|
@ -156,24 +156,11 @@ ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
|
||||
: LAUNCH_RESULT_FAILURE;
|
||||
return process;
|
||||
}
|
||||
*is_synchronous_launch = false;
|
||||
*launch_result = StartSandboxedProcess(
|
||||
delegate_.get(), *command_line(), options->handles_to_inherit,
|
||||
base::BindOnce(&ChildProcessLauncherHelper::
|
||||
FinishStartSandboxedProcessOnLauncherThread,
|
||||
this));
|
||||
return ChildProcessLauncherHelper::Process();
|
||||
}
|
||||
|
||||
void ChildProcessLauncherHelper::FinishStartSandboxedProcessOnLauncherThread(
|
||||
base::Process process,
|
||||
DWORD last_error,
|
||||
int launch_result) {
|
||||
DCHECK(CurrentlyOnProcessLauncherTaskRunner());
|
||||
ChildProcessLauncherHelper::Process process_wrapper;
|
||||
process_wrapper.process = std::move(process);
|
||||
PostLaunchOnLauncherThread(std::move(process_wrapper), last_error,
|
||||
launch_result);
|
||||
ChildProcessLauncherHelper::Process process;
|
||||
*launch_result =
|
||||
StartSandboxedProcess(delegate_.get(), *command_line(),
|
||||
options->handles_to_inherit, &process.process);
|
||||
return process;
|
||||
}
|
||||
|
||||
void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
|
||||
|
@ -24,7 +24,7 @@ sandbox::ResultCode StartSandboxedProcess(
|
||||
SandboxedProcessLauncherDelegate* delegate,
|
||||
const base::CommandLine& target_command_line,
|
||||
const base::HandlesToInheritVector& handles_to_inherit,
|
||||
sandbox::StartSandboxedProcessCallback result_callback) {
|
||||
base::Process* process) {
|
||||
std::string type_str =
|
||||
target_command_line.GetSwitchValueASCII(switches::kProcessType);
|
||||
TRACE_EVENT1("startup", "StartProcessWithAccess", "type", type_str);
|
||||
@ -45,8 +45,7 @@ sandbox::ResultCode StartSandboxedProcess(
|
||||
}
|
||||
|
||||
return sandbox::policy::SandboxWin::StartSandboxedProcess(
|
||||
full_command_line, handles_to_inherit, delegate,
|
||||
std::move(result_callback));
|
||||
full_command_line, handles_to_inherit, delegate, process);
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -6,12 +6,12 @@
|
||||
#define CONTENT_PUBLIC_COMMON_SANDBOX_INIT_WIN_H_
|
||||
|
||||
#include "base/process/launch.h"
|
||||
#include "base/process/process.h"
|
||||
#include "content/common/content_export.h"
|
||||
#include "sandbox/win/src/sandbox_types.h"
|
||||
|
||||
namespace base {
|
||||
class CommandLine;
|
||||
class Process;
|
||||
} // namespace base
|
||||
|
||||
namespace content {
|
||||
@ -30,7 +30,7 @@ CONTENT_EXPORT sandbox::ResultCode StartSandboxedProcess(
|
||||
SandboxedProcessLauncherDelegate* delegate,
|
||||
const base::CommandLine& target_command_line,
|
||||
const base::HandlesToInheritVector& handles_to_inherit,
|
||||
sandbox::StartSandboxedProcessCallback result_callback);
|
||||
base::Process* process);
|
||||
|
||||
} // namespace content
|
||||
|
||||
|
@ -124,11 +124,6 @@ BASE_FEATURE(kWinSboxRestrictCoreSharingOnRenderer,
|
||||
"WinSboxRestrictCoreSharingOnRenderer",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// Enables parallel process launching using the thread pool.
|
||||
BASE_FEATURE(kWinSboxParallelProcessLaunch,
|
||||
"WinSboxParallelProcessLaunch",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// Creates an AppContainer policy without registering with the Windows firewall
|
||||
// service. See crbug.com/352720904 for details.
|
||||
BASE_FEATURE(kWinSboxACProfileWithoutFirewall,
|
||||
@ -237,9 +232,4 @@ bool IsNetworkSandboxEnabled() {
|
||||
#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_FUCHSIA)
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool IsParallelLaunchEnabled() {
|
||||
return base::FeatureList::IsEnabled(kWinSboxParallelProcessLaunch);
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
} // namespace sandbox::policy::features
|
||||
|
@ -39,7 +39,6 @@ SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kNetworkServiceCodeIntegrity);
|
||||
SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxNoFakeGdiInit);
|
||||
SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(
|
||||
kWinSboxRestrictCoreSharingOnRenderer);
|
||||
SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxParallelProcessLaunch);
|
||||
SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxACProfileWithoutFirewall);
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
@ -76,10 +75,6 @@ SANDBOX_POLICY_EXPORT bool IsNetworkSandboxSupported();
|
||||
// calling ContentBrowserClient::ShouldSandboxNetworkService().
|
||||
SANDBOX_POLICY_EXPORT bool IsNetworkSandboxEnabled();
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Returns whether parallel launching is enabled.
|
||||
SANDBOX_POLICY_EXPORT bool IsParallelLaunchEnabled();
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
} // namespace sandbox::policy::features
|
||||
|
||||
#endif // SANDBOX_POLICY_FEATURES_H_
|
||||
|
@ -41,8 +41,8 @@
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/system/sys_info.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/elapsed_timer.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "base/win/iat_patch_function.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
@ -100,6 +100,59 @@ BASE_FEATURE(kEnableCsrssLockdownFeature,
|
||||
"EnableCsrssLockdown",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// Helper to recording timing information during process creation.
|
||||
class SandboxLaunchTimer {
|
||||
public:
|
||||
SandboxLaunchTimer() = default;
|
||||
SandboxLaunchTimer(const SandboxLaunchTimer&) = delete;
|
||||
SandboxLaunchTimer& operator=(const SandboxLaunchTimer&) = delete;
|
||||
|
||||
// Call after the policy base object is created.
|
||||
void OnPolicyCreated() { policy_created_ = timer_.Elapsed(); }
|
||||
|
||||
// Call after the delegate has generated policy settings.
|
||||
void OnPolicyGenerated() { policy_generated_ = timer_.Elapsed(); }
|
||||
|
||||
// Call after CreateProcess() has returned a suspended process.
|
||||
void OnProcessSpawned() { process_spawned_ = timer_.Elapsed(); }
|
||||
|
||||
// Call after unsuspending the process.
|
||||
void OnProcessResumed() { process_resumed_ = timer_.Elapsed(); }
|
||||
|
||||
// Call once to record histograms for a successful process launch.
|
||||
void RecordHistograms() {
|
||||
// If these parameters change the histograms should be renamed.
|
||||
// We're interested in the happy fast case so have a low maximum.
|
||||
const auto kLowBound = base::Microseconds(5);
|
||||
const auto kHighBound = base::Microseconds(100000);
|
||||
const int kBuckets = 50;
|
||||
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.CreatePolicyDuration",
|
||||
policy_created_, kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.GeneratePolicyDuration",
|
||||
policy_generated_ - policy_created_, kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.SpawnTargetDuration",
|
||||
process_spawned_ - policy_generated_, kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.PostSpawnTargetDuration",
|
||||
process_resumed_ - process_spawned_, kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.TotalDuration", process_resumed_,
|
||||
kLowBound, kHighBound, kBuckets);
|
||||
}
|
||||
|
||||
private:
|
||||
// `timer_` starts when this object is created.
|
||||
const base::ElapsedTimer timer_;
|
||||
base::TimeDelta policy_created_;
|
||||
base::TimeDelta policy_generated_;
|
||||
base::TimeDelta process_spawned_;
|
||||
base::TimeDelta process_resumed_;
|
||||
};
|
||||
|
||||
// Adds the policy rules to allow read-only access to the windows system fonts
|
||||
// directory, and any subdirectories. Used by PDF renderers.
|
||||
bool AddWindowsFontsDir(TargetConfig* config) {
|
||||
@ -696,30 +749,6 @@ bool IsUnsandboxedProcess(
|
||||
|
||||
} // namespace
|
||||
|
||||
void SandboxLaunchTimer::RecordHistograms() {
|
||||
// If these parameters change the histograms should be renamed.
|
||||
// We're interested in the happy fast case so have a low maximum.
|
||||
const auto kLowBound = base::Microseconds(5);
|
||||
const auto kHighBound = base::Microseconds(100000);
|
||||
const int kBuckets = 50;
|
||||
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.CreatePolicyDuration", policy_created_,
|
||||
kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.GeneratePolicyDuration",
|
||||
policy_generated_ - policy_created_, kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.SpawnTargetDuration",
|
||||
process_spawned_ - policy_generated_, kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.PostSpawnTargetDuration",
|
||||
process_resumed_ - process_spawned_, kLowBound, kHighBound, kBuckets);
|
||||
base::UmaHistogramCustomMicrosecondsTimes(
|
||||
"Process.Sandbox.StartSandboxedWin.TotalDuration", process_resumed_,
|
||||
kLowBound, kHighBound, kBuckets);
|
||||
}
|
||||
|
||||
// static
|
||||
ResultCode SandboxWin::SetJobLevel(Sandbox sandbox_type,
|
||||
JobLevel job_level,
|
||||
@ -862,56 +891,13 @@ bool SandboxWin::IsAppContainerEnabledForSandbox(
|
||||
return false;
|
||||
}
|
||||
|
||||
class BrokerServicesDelegateImpl : public BrokerServicesDelegate {
|
||||
public:
|
||||
bool ParallelLaunchEnabled() override {
|
||||
return features::IsParallelLaunchEnabled();
|
||||
}
|
||||
|
||||
void ParallelLaunchPostTaskAndReplyWithResult(
|
||||
const base::Location& from_here,
|
||||
base::OnceCallback<CreateTargetResult()> task,
|
||||
base::OnceCallback<void(CreateTargetResult)> reply) override {
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
from_here,
|
||||
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
|
||||
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
|
||||
std::move(task), std::move(reply));
|
||||
}
|
||||
|
||||
void BeforeTargetProcessCreateOnCreationThread(
|
||||
const void* trace_id) override {
|
||||
int active_threads = ++creation_threads_in_use_;
|
||||
base::UmaHistogramCounts100("MPArch.ChildProcessLaunchActivelyInParallel",
|
||||
active_threads);
|
||||
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("startup", "TargetProcess::Create",
|
||||
trace_id);
|
||||
}
|
||||
|
||||
void AfterTargetProcessCreateOnCreationThread(const void* trace_id,
|
||||
DWORD process_id) override {
|
||||
creation_threads_in_use_--;
|
||||
TRACE_EVENT_NESTABLE_ASYNC_END1("startup", "TargetProcess::Create",
|
||||
trace_id, "pid", process_id);
|
||||
}
|
||||
|
||||
private:
|
||||
// When parallel launching is enabled, target creation will happen on the
|
||||
// thread pool. This is atomic to keep track of the number of threads that are
|
||||
// currently creating processes.
|
||||
std::atomic<int> creation_threads_in_use_ = 0;
|
||||
};
|
||||
|
||||
// static
|
||||
bool SandboxWin::InitBrokerServices(BrokerServices* broker_services) {
|
||||
// TODO(abarth): DCHECK(CalledOnValidThread());
|
||||
// See <http://b/1287166>.
|
||||
DCHECK(broker_services);
|
||||
DCHECK(!g_broker_services);
|
||||
|
||||
ResultCode init_result =
|
||||
broker_services->Init(std::make_unique<BrokerServicesDelegateImpl>());
|
||||
ResultCode init_result = broker_services->Init();
|
||||
g_broker_services = broker_services;
|
||||
|
||||
// In non-official builds warn about dangerous uses of DuplicateHandle. This
|
||||
@ -994,18 +980,14 @@ ResultCode SandboxWin::StartSandboxedProcess(
|
||||
const base::CommandLine& cmd_line,
|
||||
const base::HandlesToInheritVector& handles_to_inherit,
|
||||
SandboxDelegate* delegate,
|
||||
StartSandboxedProcessCallback result_callback) {
|
||||
base::Process* process) {
|
||||
SandboxLaunchTimer timer;
|
||||
|
||||
// Avoid making a policy if we won't use it.
|
||||
if (IsUnsandboxedProcess(delegate->GetSandboxType(), cmd_line,
|
||||
*base::CommandLine::ForCurrentProcess())) {
|
||||
base::Process process;
|
||||
ResultCode result =
|
||||
LaunchWithoutSandbox(cmd_line, handles_to_inherit, delegate, &process);
|
||||
DWORD last_error = GetLastError();
|
||||
std::move(result_callback).Run(std::move(process), last_error, result);
|
||||
return SBOX_ALL_OK;
|
||||
return LaunchWithoutSandbox(cmd_line, handles_to_inherit, delegate,
|
||||
process);
|
||||
}
|
||||
|
||||
auto policy = g_broker_services->CreatePolicy(delegate->GetSandboxTag());
|
||||
@ -1017,51 +999,27 @@ ResultCode SandboxWin::StartSandboxedProcess(
|
||||
return result;
|
||||
timer.OnPolicyGenerated();
|
||||
|
||||
int64_t trace_event_id = timer.GetStartTimeInMicroseconds();
|
||||
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
|
||||
"startup", "StartProcessWithAccess::LAUNCHPROCESS", trace_event_id);
|
||||
TRACE_EVENT_BEGIN0("startup", "StartProcessWithAccess::LAUNCHPROCESS");
|
||||
|
||||
result = g_broker_services->SpawnTargetAsync(
|
||||
PROCESS_INFORMATION temp_process_info = {};
|
||||
DWORD last_error = ERROR_SUCCESS;
|
||||
result = g_broker_services->SpawnTarget(
|
||||
cmd_line.GetProgram().value().c_str(),
|
||||
cmd_line.GetCommandLineString().c_str(), std::move(policy),
|
||||
base::BindOnce(&SandboxWin::FinishStartSandboxedProcess, delegate,
|
||||
std::move(timer), std::move(result_callback)));
|
||||
|
||||
if (result != SBOX_ALL_OK) {
|
||||
TRACE_EVENT_NESTABLE_ASYNC_END0(
|
||||
"startup", "StartProcessWithAccess::LAUNCHPROCESS", trace_event_id);
|
||||
|
||||
if (result == SBOX_ERROR_GENERIC) {
|
||||
DPLOG(ERROR) << "Failed to launch process";
|
||||
} else {
|
||||
DLOG(ERROR) << "Failed to launch process. Error: " << result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
void SandboxWin::FinishStartSandboxedProcess(
|
||||
SandboxDelegate* delegate,
|
||||
SandboxLaunchTimer timer,
|
||||
StartSandboxedProcessCallback result_callback,
|
||||
base::win::ScopedProcessInformation target,
|
||||
DWORD last_error,
|
||||
ResultCode result) {
|
||||
cmd_line.GetCommandLineString().c_str(), std::move(policy), &last_error,
|
||||
&temp_process_info);
|
||||
timer.OnProcessSpawned();
|
||||
|
||||
int64_t trace_event_id = timer.GetStartTimeInMicroseconds();
|
||||
TRACE_EVENT_NESTABLE_ASYNC_END0(
|
||||
"startup", "StartProcessWithAccess::LAUNCHPROCESS", trace_event_id);
|
||||
base::win::ScopedProcessInformation target(temp_process_info);
|
||||
|
||||
TRACE_EVENT_END0("startup", "StartProcessWithAccess::LAUNCHPROCESS");
|
||||
|
||||
if (SBOX_ALL_OK != result) {
|
||||
base::UmaHistogramSparse("Process.Sandbox.Launch.Error", last_error);
|
||||
if (result == SBOX_ERROR_GENERIC) {
|
||||
if (result == SBOX_ERROR_GENERIC)
|
||||
DPLOG(ERROR) << "Failed to launch process";
|
||||
} else {
|
||||
else
|
||||
DLOG(ERROR) << "Failed to launch process. Error: " << result;
|
||||
}
|
||||
std::move(result_callback).Run(base::Process(), last_error, result);
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
delegate->PostSpawnTarget(target.process_handle());
|
||||
@ -1073,8 +1031,8 @@ void SandboxWin::FinishStartSandboxedProcess(
|
||||
timer.RecordHistograms();
|
||||
}
|
||||
|
||||
base::Process process(target.TakeProcessHandle());
|
||||
std::move(result_callback).Run(std::move(process), last_error, result);
|
||||
*process = base::Process(target.TakeProcessHandle());
|
||||
return SBOX_ALL_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -7,16 +7,14 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <optional>
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "base/process/launch.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/timer/elapsed_timer.h"
|
||||
#include "base/win/scoped_process_information.h"
|
||||
#include "build/build_config.h"
|
||||
#include "sandbox/policy/export.h"
|
||||
#include "sandbox/policy/sandbox_delegate.h"
|
||||
@ -43,43 +41,6 @@ enum class Sandbox;
|
||||
namespace sandbox {
|
||||
namespace policy {
|
||||
|
||||
// Helper to recording timing information during process creation.
|
||||
class SANDBOX_POLICY_EXPORT SandboxLaunchTimer final {
|
||||
public:
|
||||
SandboxLaunchTimer() = default;
|
||||
SandboxLaunchTimer(const SandboxLaunchTimer&) = delete;
|
||||
SandboxLaunchTimer(SandboxLaunchTimer&& other) = default;
|
||||
SandboxLaunchTimer& operator=(const SandboxLaunchTimer&) = delete;
|
||||
|
||||
// Call after the policy base object is created.
|
||||
void OnPolicyCreated() { policy_created_ = timer_.Elapsed(); }
|
||||
|
||||
// Call after the delegate has generated policy settings.
|
||||
void OnPolicyGenerated() { policy_generated_ = timer_.Elapsed(); }
|
||||
|
||||
// Call after CreateProcess() has returned a suspended process.
|
||||
void OnProcessSpawned() { process_spawned_ = timer_.Elapsed(); }
|
||||
|
||||
// Call after unsuspending the process.
|
||||
void OnProcessResumed() { process_resumed_ = timer_.Elapsed(); }
|
||||
|
||||
// Returns when this timer was created.
|
||||
int64_t GetStartTimeInMicroseconds() const {
|
||||
return timer_.start_time().since_origin().InMicroseconds();
|
||||
}
|
||||
|
||||
// Call once to record histograms for a successful process launch.
|
||||
void RecordHistograms();
|
||||
|
||||
private:
|
||||
// `timer_` starts when this object is created.
|
||||
base::ElapsedTimer timer_;
|
||||
base::TimeDelta policy_created_;
|
||||
base::TimeDelta policy_generated_;
|
||||
base::TimeDelta process_spawned_;
|
||||
base::TimeDelta process_resumed_;
|
||||
};
|
||||
|
||||
class SANDBOX_POLICY_EXPORT SandboxWin {
|
||||
public:
|
||||
// Create a sandboxed process `process` with the specified `cmd_line`.
|
||||
@ -87,14 +48,13 @@ class SANDBOX_POLICY_EXPORT SandboxWin {
|
||||
// `delegate` specifies the sandbox delegate to use when resolving specific
|
||||
// sandbox policy.
|
||||
//
|
||||
// If SBOX_ALL_OK is returned, then `result_callback` will be called with the
|
||||
// process creation result. Otherwise, returns one of sandbox::ResultCode for
|
||||
// any other error.
|
||||
// Returns SBOX_ALL_OK if the process was successfully created.
|
||||
// Otherwise, returns one of sandbox::ResultCode for any other error.
|
||||
static ResultCode StartSandboxedProcess(
|
||||
const base::CommandLine& cmd_line,
|
||||
const base::HandlesToInheritVector& handles_to_inherit,
|
||||
SandboxDelegate* delegate,
|
||||
StartSandboxedProcessCallback result_callback);
|
||||
base::Process* process);
|
||||
|
||||
// Generates a sandbox policy into `policy` to match the one that would be
|
||||
// applied during `StartSandboxedProcess` for the identical set of arguments.
|
||||
@ -164,14 +124,6 @@ class SANDBOX_POLICY_EXPORT SandboxWin {
|
||||
private:
|
||||
FRIEND_TEST_ALL_PREFIXES(SandboxWinTest, GetJobMemoryLimit);
|
||||
|
||||
static void FinishStartSandboxedProcess(
|
||||
SandboxDelegate* delegate,
|
||||
SandboxLaunchTimer timer,
|
||||
StartSandboxedProcessCallback result_callback,
|
||||
base::win::ScopedProcessInformation target,
|
||||
DWORD last_error,
|
||||
ResultCode result);
|
||||
|
||||
static std::optional<size_t> GetJobMemoryLimit(
|
||||
sandbox::mojom::Sandbox sandbox_type);
|
||||
};
|
||||
|
@ -192,7 +192,6 @@ test("sbox_integration_tests") {
|
||||
"src/ipc_leak_test.cc",
|
||||
"src/ipc_ping_test.cc",
|
||||
"src/lpc_policy_test.cc",
|
||||
"src/parallel_launch_test.cc",
|
||||
"src/policy_target_test.cc",
|
||||
"src/process_delegate_data_test.cc",
|
||||
"src/process_mitigations_deathtest.cc",
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "base/win/access_token.h"
|
||||
#include "base/win/current_module.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/scoped_process_information.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "build/build_config.h"
|
||||
#include "sandbox/win/src/app_container.h"
|
||||
@ -254,11 +255,8 @@ BrokerServicesBase::BrokerServicesBase() {}
|
||||
|
||||
// The broker uses a dedicated worker thread that services the job completion
|
||||
// port to perform policy notifications and associated cleanup tasks.
|
||||
ResultCode BrokerServicesBase::InitInternal(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate,
|
||||
ResultCode BrokerServicesBase::Init(
|
||||
std::unique_ptr<BrokerServicesTargetTracker> target_tracker) {
|
||||
broker_services_delegate_ = std::move(delegate);
|
||||
|
||||
if (job_port_.is_valid() || thread_pool_) {
|
||||
return SBOX_ERROR_UNEXPECTED_CALL;
|
||||
}
|
||||
@ -299,17 +297,14 @@ ResultCode BrokerServicesBase::InitInternal(
|
||||
return SBOX_ALL_OK;
|
||||
}
|
||||
|
||||
ResultCode BrokerServicesBase::Init(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate) {
|
||||
return BrokerServicesBase::InitInternal(std::move(delegate), nullptr);
|
||||
ResultCode BrokerServicesBase::Init() {
|
||||
return BrokerServicesBase::Init(nullptr);
|
||||
}
|
||||
|
||||
// Only called in test code.
|
||||
ResultCode BrokerServicesBase::InitForTesting(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate,
|
||||
std::unique_ptr<BrokerServicesTargetTracker> target_tracker) {
|
||||
return BrokerServicesBase::InitInternal(std::move(delegate),
|
||||
std::move(target_tracker));
|
||||
return BrokerServicesBase::Init(std::move(target_tracker));
|
||||
}
|
||||
|
||||
// The destructor should only be called when the Broker process is terminating.
|
||||
@ -364,55 +359,13 @@ std::unique_ptr<TargetPolicy> BrokerServicesBase::CreatePolicy(
|
||||
return policy;
|
||||
}
|
||||
|
||||
// SpawnTarget does all the interesting sandbox setup and creates the target
|
||||
// process inside the sandbox.
|
||||
ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
|
||||
const wchar_t* command_line,
|
||||
std::unique_ptr<TargetPolicy> policy,
|
||||
DWORD* last_error,
|
||||
PROCESS_INFORMATION* target_info) {
|
||||
*last_error = 0;
|
||||
*target_info = {};
|
||||
// With parallel launching disabled, it is safe to capture local references
|
||||
// because SpawnTargetAsyncImpl is guaranteed to run the callback before
|
||||
// returning.
|
||||
ResultCode launch_result = SBOX_ERROR_GENERIC;
|
||||
ResultCode result = SpawnTargetAsyncImpl(
|
||||
exe_path, command_line, std::move(policy),
|
||||
base::BindOnce(
|
||||
[](DWORD* last_error, PROCESS_INFORMATION* target_info,
|
||||
ResultCode* launch_result,
|
||||
base::win::ScopedProcessInformation result_target_info,
|
||||
DWORD result_last_error, ResultCode result_code) -> void {
|
||||
*target_info = result_target_info.Take();
|
||||
*last_error = result_last_error;
|
||||
*launch_result = result_code;
|
||||
},
|
||||
last_error, target_info, &launch_result),
|
||||
/*allow_parallel_launch=*/false);
|
||||
|
||||
if (result == SBOX_ALL_OK) {
|
||||
result = launch_result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultCode BrokerServicesBase::SpawnTargetAsync(
|
||||
const wchar_t* exe_path,
|
||||
const wchar_t* command_line,
|
||||
std::unique_ptr<TargetPolicy> policy,
|
||||
SpawnTargetCallback result_callback) {
|
||||
return SpawnTargetAsyncImpl(exe_path, command_line, std::move(policy),
|
||||
std::move(result_callback),
|
||||
/*allow_parallel_launch=*/true);
|
||||
}
|
||||
|
||||
// SpawnTarget does all the interesting sandbox setup and creates the target
|
||||
// process inside the sandbox.
|
||||
ResultCode BrokerServicesBase::SpawnTargetAsyncImpl(
|
||||
const wchar_t* exe_path,
|
||||
const wchar_t* command_line,
|
||||
std::unique_ptr<TargetPolicy> policy,
|
||||
SpawnTargetCallback result_callback,
|
||||
bool allow_parallel_launch) {
|
||||
if (!exe_path)
|
||||
return SBOX_ERROR_BAD_PARAMS;
|
||||
|
||||
@ -511,88 +464,23 @@ ResultCode BrokerServicesBase::SpawnTargetAsyncImpl(
|
||||
|
||||
// Create the TargetProcess object and spawn the target suspended. Note that
|
||||
// Brokerservices does not own the target object. It is owned by the Policy.
|
||||
base::win::ScopedProcessInformation process_info;
|
||||
std::unique_ptr<TargetProcess> target = std::make_unique<TargetProcess>(
|
||||
std::move(*initial_token), std::move(*lockdown_token), thread_pool_);
|
||||
|
||||
if (allow_parallel_launch &&
|
||||
broker_services_delegate_->ParallelLaunchEnabled()) {
|
||||
TargetProcess* target_ptr = target.get();
|
||||
broker_services_delegate_->ParallelLaunchPostTaskAndReplyWithResult(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&BrokerServicesBase::CreateTarget,
|
||||
base::Unretained(this), target_ptr,
|
||||
std::wstring(exe_path), std::wstring(command_line),
|
||||
std::move(startup_info)),
|
||||
base::BindOnce(&BrokerServicesBase::FinishSpawnTarget,
|
||||
base::Unretained(this), std::move(policy_base),
|
||||
std::move(target), std::move(result_callback)));
|
||||
return SBOX_ALL_OK;
|
||||
}
|
||||
result = target->Create(exe_path, command_line, std::move(startup_info),
|
||||
&process_info, last_error);
|
||||
|
||||
CreateTargetResult target_result = CreateTarget(
|
||||
target.get(), exe_path, command_line, std::move(startup_info));
|
||||
|
||||
FinishSpawnTarget(std::move(policy_base), std::move(target),
|
||||
std::move(result_callback), std::move(target_result));
|
||||
return SBOX_ALL_OK;
|
||||
}
|
||||
|
||||
CreateTargetResult BrokerServicesBase::CreateTarget(
|
||||
TargetProcess* target,
|
||||
const std::wstring& exe_path,
|
||||
const std::wstring& command_line,
|
||||
std::unique_ptr<StartupInformationHelper> startup_info) {
|
||||
// A trace ID for the current scope is generated from the address of a local
|
||||
// variable to ensure uniqueness across threads.
|
||||
const void* trace_id = &startup_info;
|
||||
broker_services_delegate_->BeforeTargetProcessCreateOnCreationThread(
|
||||
trace_id);
|
||||
|
||||
CreateTargetResult result;
|
||||
result.result_code = target->Create(exe_path.c_str(), command_line.c_str(),
|
||||
std::move(startup_info),
|
||||
&result.process_info, &result.last_error);
|
||||
|
||||
broker_services_delegate_->AfterTargetProcessCreateOnCreationThread(
|
||||
trace_id, result.process_info.process_id());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void BrokerServicesBase::FinishSpawnTarget(
|
||||
std::unique_ptr<PolicyBase> policy_base,
|
||||
std::unique_ptr<TargetProcess> target,
|
||||
SpawnTargetCallback result_callback,
|
||||
CreateTargetResult target_result) {
|
||||
ResultCode result = FinishSpawnTargetImpl(
|
||||
target_result.result_code, std::move(policy_base), std::move(target),
|
||||
&target_result.process_info, &target_result.last_error);
|
||||
if (result != SBOX_ALL_OK) {
|
||||
target_result.process_info.Close();
|
||||
}
|
||||
std::move(result_callback)
|
||||
.Run(std::move(target_result.process_info), target_result.last_error,
|
||||
result);
|
||||
}
|
||||
|
||||
ResultCode BrokerServicesBase::FinishSpawnTargetImpl(
|
||||
ResultCode initial_result,
|
||||
std::unique_ptr<PolicyBase> policy_base,
|
||||
std::unique_ptr<TargetProcess> target,
|
||||
base::win::ScopedProcessInformation* process_info,
|
||||
DWORD* last_error) {
|
||||
if (initial_result != SBOX_ALL_OK) {
|
||||
target->Terminate();
|
||||
return initial_result;
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigBase* config_base = static_cast<ConfigBase*>(policy_base->GetConfig());
|
||||
|
||||
if (config_base->GetJobLevel() <= JobLevel::kLimitedUser) {
|
||||
// Restrict the job from containing any processes. Job restrictions
|
||||
// are only applied at process creation, so the target process is
|
||||
// unaffected.
|
||||
ResultCode result = policy_base->DropActiveProcessLimit();
|
||||
result = policy_base->DropActiveProcessLimit();
|
||||
if (result != SBOX_ALL_OK) {
|
||||
target->Terminate();
|
||||
return result;
|
||||
@ -601,7 +489,7 @@ ResultCode BrokerServicesBase::FinishSpawnTargetImpl(
|
||||
|
||||
// Now the policy is the owner of the target. TargetProcess will terminate
|
||||
// the process if it has not completed when it is destroyed.
|
||||
ResultCode result = policy_base->ApplyToTarget(std::move(target));
|
||||
result = policy_base->ApplyToTarget(std::move(target));
|
||||
|
||||
if (result != SBOX_ALL_OK) {
|
||||
*last_error = ::GetLastError();
|
||||
@ -610,7 +498,7 @@ ResultCode BrokerServicesBase::FinishSpawnTargetImpl(
|
||||
|
||||
HANDLE job_handle = policy_base->GetJobHandle();
|
||||
JobTracker* tracker =
|
||||
new JobTracker(std::move(policy_base), process_info->process_id());
|
||||
new JobTracker(std::move(policy_base), process_info.process_id());
|
||||
|
||||
// Post the tracker to the tracking thread, then associate the job with
|
||||
// the tracker. The worker thread takes ownership of these objects.
|
||||
@ -620,6 +508,7 @@ ResultCode BrokerServicesBase::FinishSpawnTargetImpl(
|
||||
// There is no obvious cleanup here.
|
||||
CHECK(AssociateCompletionPort(job_handle, job_port_.get(), tracker));
|
||||
|
||||
*target_info = process_info.Take();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -713,11 +602,6 @@ void BrokerServicesBase::DestroyDesktops() {
|
||||
alt_desktop_.reset();
|
||||
}
|
||||
|
||||
void BrokerServicesBase::SetBrokerServicesDelegateForTesting(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate) {
|
||||
broker_services_delegate_ = std::move(delegate);
|
||||
}
|
||||
|
||||
// static
|
||||
void BrokerServicesBase::FreezeTargetConfigForTesting(TargetConfig* config) {
|
||||
CHECK(!config->IsConfigured());
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/scoped_process_information.h"
|
||||
#include "sandbox/win/src/alternate_desktop.h"
|
||||
#include "sandbox/win/src/crosscall_server.h"
|
||||
#include "sandbox/win/src/sandbox.h"
|
||||
@ -46,9 +45,8 @@ class BrokerServicesBase final : public BrokerServices,
|
||||
~BrokerServicesBase();
|
||||
|
||||
// BrokerServices interface.
|
||||
ResultCode Init(std::unique_ptr<BrokerServicesDelegate> delegate) override;
|
||||
ResultCode Init() override;
|
||||
ResultCode InitForTesting(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate,
|
||||
std::unique_ptr<BrokerServicesTargetTracker> target_tracker) override;
|
||||
ResultCode CreateAlternateDesktop(Desktop desktop) override;
|
||||
void DestroyDesktops() override;
|
||||
@ -60,10 +58,6 @@ class BrokerServicesBase final : public BrokerServices,
|
||||
std::unique_ptr<TargetPolicy> policy,
|
||||
DWORD* last_error,
|
||||
PROCESS_INFORMATION* target) override;
|
||||
ResultCode SpawnTargetAsync(const wchar_t* exe_path,
|
||||
const wchar_t* command_line,
|
||||
std::unique_ptr<TargetPolicy> policy,
|
||||
SpawnTargetCallback result_callback) override;
|
||||
ResultCode GetPolicyDiagnostics(
|
||||
std::unique_ptr<PolicyDiagnosticsReceiver> receiver) override;
|
||||
void SetStartingMitigations(MitigationFlags starting_mitigations) override;
|
||||
@ -71,56 +65,15 @@ class BrokerServicesBase final : public BrokerServices,
|
||||
MitigationFlags additional_flags) override;
|
||||
std::wstring GetDesktopName(Desktop desktop) override;
|
||||
|
||||
void SetBrokerServicesDelegateForTesting(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate);
|
||||
|
||||
static void FreezeTargetConfigForTesting(TargetConfig* config);
|
||||
|
||||
private:
|
||||
// Implements Init and InitForTesting.
|
||||
ResultCode InitInternal(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate,
|
||||
std::unique_ptr<BrokerServicesTargetTracker> target_tracker);
|
||||
ResultCode Init(std::unique_ptr<BrokerServicesTargetTracker> target_tracker);
|
||||
|
||||
// Ensures the desktop integrity suits any process we are launching.
|
||||
ResultCode UpdateDesktopIntegrity(Desktop desktop, IntegrityLevel integrity);
|
||||
|
||||
// Creates the target process and returns the new process handle in the
|
||||
// result. In parallel launch mode, this function runs on the thread pool.
|
||||
CreateTargetResult CreateTarget(
|
||||
TargetProcess* target,
|
||||
const std::wstring& exe_path,
|
||||
const std::wstring& command_line,
|
||||
std::unique_ptr<StartupInformationHelper> startup_info);
|
||||
|
||||
// Implementation for SpawnTarget and SpawnTargetAsync.
|
||||
// Parallel launching will be used if `allow_parallel_launch` is true and
|
||||
// BrokerServicesDelegate::EnableParallelLaunch() returns true.
|
||||
// When this function returns SBOX_ALL_OK, `result_callback` will be called
|
||||
// with the target creation result. Otherwise, the returned error code is
|
||||
// final and `result_callback` will not be called.
|
||||
ResultCode SpawnTargetAsyncImpl(const wchar_t* exe_path,
|
||||
const wchar_t* command_line,
|
||||
std::unique_ptr<TargetPolicy> policy,
|
||||
SpawnTargetCallback result_callback,
|
||||
bool allow_parallel_launch);
|
||||
|
||||
// This function is a wrapper for FinishSpawnTargetImpl and gets called after
|
||||
// the target process is created. This function is responsible for running
|
||||
// `result_callback` to return the information about the new process.
|
||||
void FinishSpawnTarget(std::unique_ptr<PolicyBase> policy_base,
|
||||
std::unique_ptr<TargetProcess> target,
|
||||
SpawnTargetCallback result_callback,
|
||||
CreateTargetResult target_result);
|
||||
|
||||
// Finishes setup after the target process is created.
|
||||
ResultCode FinishSpawnTargetImpl(
|
||||
ResultCode initial_result,
|
||||
std::unique_ptr<PolicyBase> policy_base,
|
||||
std::unique_ptr<TargetProcess> target,
|
||||
base::win::ScopedProcessInformation* process_info,
|
||||
DWORD* last_error);
|
||||
|
||||
// The completion port used by the job objects to communicate events to
|
||||
// the worker thread.
|
||||
base::win::ScopedHandle job_port_;
|
||||
@ -140,9 +93,6 @@ class BrokerServicesBase final : public BrokerServices,
|
||||
// Cache of configs backing policies. Entries are retained until shutdown and
|
||||
// used to prime policies created by CreatePolicy() with the same `tag`.
|
||||
base::flat_map<std::string, std::unique_ptr<TargetConfig>> config_cache_;
|
||||
|
||||
// Provides configuration for using parallel or synchronous process launching.
|
||||
std::unique_ptr<BrokerServicesDelegate> broker_services_delegate_;
|
||||
};
|
||||
|
||||
} // namespace sandbox
|
||||
|
@ -1,251 +0,0 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/process/process_info.h"
|
||||
#include "base/task/lazy_thread_pool_task_runner.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "base/task/single_thread_task_runner_thread_mode.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "base/win/scoped_process_information.h"
|
||||
#include "sandbox/win/src/broker_services.h"
|
||||
#include "sandbox/win/tests/common/controller.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace sandbox {
|
||||
|
||||
// BrokerServicesDelegate common implementation.
|
||||
class TestBrokerServicesDelegateBase : public BrokerServicesDelegate {
|
||||
public:
|
||||
bool ParallelLaunchEnabled() override { return true; }
|
||||
|
||||
void ParallelLaunchPostTaskAndReplyWithResult(
|
||||
const base::Location& from_here,
|
||||
base::OnceCallback<CreateTargetResult()> task,
|
||||
base::OnceCallback<void(CreateTargetResult)> reply) override {
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
from_here,
|
||||
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
|
||||
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
|
||||
std::move(task), std::move(reply));
|
||||
}
|
||||
|
||||
void AfterTargetProcessCreateOnCreationThread(const void* trace_id,
|
||||
DWORD process_id) override {}
|
||||
};
|
||||
|
||||
class ParallelLaunchTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
task_environment_ = std::make_unique<base::test::TaskEnvironment>();
|
||||
}
|
||||
|
||||
static void FinishSpawnTargetAsync(CreateTargetResult* spawn_result,
|
||||
base::RunLoop* run_loop,
|
||||
int* launches_remaining_count,
|
||||
base::win::ScopedProcessInformation target,
|
||||
DWORD last_error,
|
||||
ResultCode result) {
|
||||
spawn_result->process_info = std::move(target);
|
||||
spawn_result->last_error = last_error;
|
||||
spawn_result->result_code = result;
|
||||
|
||||
if (--*launches_remaining_count == 0) {
|
||||
run_loop->Quit();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<base::test::TaskEnvironment> task_environment_;
|
||||
};
|
||||
|
||||
class SingleLaunch_TestBrokerServicesDelegate
|
||||
: public TestBrokerServicesDelegateBase {
|
||||
public:
|
||||
void BeforeTargetProcessCreateOnCreationThread(
|
||||
const void* trace_id) override {
|
||||
creation_thread_id_ = ::GetCurrentThreadId();
|
||||
}
|
||||
|
||||
DWORD creation_thread_id_ = 0;
|
||||
};
|
||||
|
||||
// Launches a single child with parallel launching enabled. The child process
|
||||
// will be created on the thread pool.
|
||||
TEST_F(ParallelLaunchTest, SingleLaunch) {
|
||||
BrokerServices* broker = GetBroker();
|
||||
ASSERT_TRUE(broker);
|
||||
|
||||
auto* delegate = new SingleLaunch_TestBrokerServicesDelegate();
|
||||
static_cast<BrokerServicesBase*>(broker)->SetBrokerServicesDelegateForTesting(
|
||||
std::unique_ptr<BrokerServicesDelegate>(delegate));
|
||||
|
||||
// Get the path to the sandboxed app.
|
||||
wchar_t prog_name[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
|
||||
|
||||
std::wstring arguments(L"\"");
|
||||
arguments += prog_name;
|
||||
arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
|
||||
|
||||
auto policy = broker->CreatePolicy();
|
||||
EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE,
|
||||
USER_LOCKDOWN));
|
||||
|
||||
CreateTargetResult spawn_result;
|
||||
spawn_result.result_code = SBOX_ERROR_GENERIC;
|
||||
|
||||
base::RunLoop run_loop;
|
||||
int launches_remaining_count = 1;
|
||||
ResultCode result_code = broker->SpawnTargetAsync(
|
||||
prog_name, arguments.c_str(), std::move(policy),
|
||||
base::BindOnce(&FinishSpawnTargetAsync, base::Unretained(&spawn_result),
|
||||
base::Unretained(&run_loop),
|
||||
base::Unretained(&launches_remaining_count)));
|
||||
EXPECT_EQ(result_code, SBOX_ALL_OK);
|
||||
|
||||
run_loop.Run();
|
||||
|
||||
// Target creation should happen on a different thread.
|
||||
EXPECT_NE(delegate->creation_thread_id_, 0u);
|
||||
EXPECT_NE(delegate->creation_thread_id_, GetCurrentThreadId());
|
||||
|
||||
EXPECT_EQ(SBOX_ALL_OK, spawn_result.result_code);
|
||||
|
||||
EXPECT_EQ(1u, ::ResumeThread(spawn_result.process_info.thread_handle()));
|
||||
|
||||
EXPECT_EQ(
|
||||
static_cast<DWORD>(WAIT_TIMEOUT),
|
||||
::WaitForSingleObject(spawn_result.process_info.process_handle(), 2000));
|
||||
|
||||
EXPECT_TRUE(
|
||||
::TerminateProcess(spawn_result.process_info.process_handle(), 0));
|
||||
|
||||
::WaitForSingleObject(spawn_result.process_info.process_handle(), INFINITE);
|
||||
}
|
||||
|
||||
class ParallelLaunch_TestBrokerServicesDelegate
|
||||
: public TestBrokerServicesDelegateBase {
|
||||
public:
|
||||
void BeforeTargetProcessCreateOnCreationThread(
|
||||
const void* trace_id) override {
|
||||
if (first_launch_) {
|
||||
first_creation_thread_id_ = ::GetCurrentThreadId();
|
||||
first_trace_id_ = reinterpret_cast<uintptr_t>(trace_id);
|
||||
first_launch_ = false;
|
||||
EXPECT_TRUE(::SetEvent(reached_first_creation_event_));
|
||||
::WaitForSingleObject(first_block_event_, INFINITE);
|
||||
} else {
|
||||
second_creation_thread_id_ = ::GetCurrentThreadId();
|
||||
second_trace_id_ = reinterpret_cast<uintptr_t>(trace_id);
|
||||
EXPECT_TRUE(::SetEvent(first_block_event_));
|
||||
}
|
||||
}
|
||||
|
||||
bool first_launch_ = true;
|
||||
HANDLE reached_first_creation_event_;
|
||||
HANDLE first_block_event_;
|
||||
DWORD first_creation_thread_id_;
|
||||
DWORD second_creation_thread_id_;
|
||||
uintptr_t first_trace_id_;
|
||||
uintptr_t second_trace_id_;
|
||||
};
|
||||
|
||||
// This test launches two processes and synchronizes the target creation threads
|
||||
// to run at the same time.
|
||||
TEST_F(ParallelLaunchTest, ParallelLaunch) {
|
||||
BrokerServices* broker = GetBroker();
|
||||
ASSERT_TRUE(broker);
|
||||
|
||||
auto* delegate = new ParallelLaunch_TestBrokerServicesDelegate();
|
||||
static_cast<BrokerServicesBase*>(broker)->SetBrokerServicesDelegateForTesting(
|
||||
std::unique_ptr<BrokerServicesDelegate>(delegate));
|
||||
|
||||
// Get the path to the sandboxed app.
|
||||
wchar_t prog_name[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
|
||||
|
||||
std::wstring arguments(L"\"");
|
||||
arguments += prog_name;
|
||||
arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
|
||||
|
||||
base::RunLoop run_loop;
|
||||
int launches_remaining_count = 2;
|
||||
|
||||
// Launch the first process. This will block on the creation thread and wait
|
||||
// for the second process launch to unblock it.
|
||||
CreateTargetResult first_spawn_result;
|
||||
first_spawn_result.result_code = SBOX_ERROR_GENERIC;
|
||||
delegate->reached_first_creation_event_ =
|
||||
::CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
delegate->first_block_event_ = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
{
|
||||
auto policy = broker->CreatePolicy();
|
||||
EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE,
|
||||
USER_LOCKDOWN));
|
||||
|
||||
ResultCode result_code = broker->SpawnTargetAsync(
|
||||
prog_name, arguments.c_str(), std::move(policy),
|
||||
base::BindOnce(&FinishSpawnTargetAsync,
|
||||
base::Unretained(&first_spawn_result),
|
||||
base::Unretained(&run_loop),
|
||||
base::Unretained(&launches_remaining_count)));
|
||||
EXPECT_EQ(result_code, SBOX_ALL_OK);
|
||||
}
|
||||
|
||||
::WaitForSingleObject(delegate->reached_first_creation_event_, INFINITE);
|
||||
|
||||
// Launch the second process.
|
||||
CreateTargetResult second_spawn_result;
|
||||
second_spawn_result.result_code = SBOX_ERROR_GENERIC;
|
||||
{
|
||||
auto policy = broker->CreatePolicy();
|
||||
EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE,
|
||||
USER_LOCKDOWN));
|
||||
|
||||
ResultCode result_code = broker->SpawnTargetAsync(
|
||||
prog_name, arguments.c_str(), std::move(policy),
|
||||
base::BindOnce(&FinishSpawnTargetAsync,
|
||||
base::Unretained(&second_spawn_result),
|
||||
base::Unretained(&run_loop),
|
||||
base::Unretained(&launches_remaining_count)));
|
||||
EXPECT_EQ(result_code, SBOX_ALL_OK);
|
||||
}
|
||||
|
||||
run_loop.Run();
|
||||
|
||||
// Targets should be created on different threads.
|
||||
EXPECT_NE(delegate->first_creation_thread_id_,
|
||||
delegate->second_creation_thread_id_);
|
||||
|
||||
EXPECT_NE(delegate->first_trace_id_, delegate->second_trace_id_);
|
||||
|
||||
EXPECT_EQ(SBOX_ALL_OK, first_spawn_result.result_code);
|
||||
EXPECT_EQ(SBOX_ALL_OK, second_spawn_result.result_code);
|
||||
|
||||
EXPECT_EQ(1u,
|
||||
::ResumeThread(first_spawn_result.process_info.thread_handle()));
|
||||
EXPECT_EQ(1u,
|
||||
::ResumeThread(second_spawn_result.process_info.thread_handle()));
|
||||
|
||||
HANDLE handles[2] = {first_spawn_result.process_info.process_handle(),
|
||||
second_spawn_result.process_info.process_handle()};
|
||||
EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
|
||||
::WaitForMultipleObjects(2, handles, /*bWaitAll=*/TRUE, 2000));
|
||||
|
||||
EXPECT_TRUE(
|
||||
::TerminateProcess(first_spawn_result.process_info.process_handle(), 0));
|
||||
EXPECT_TRUE(
|
||||
::TerminateProcess(second_spawn_result.process_info.process_handle(), 0));
|
||||
|
||||
::WaitForMultipleObjects(2, handles, /*bWaitAll=*/TRUE, INFINITE);
|
||||
}
|
||||
|
||||
} // namespace sandbox
|
@ -20,14 +20,13 @@
|
||||
#define SANDBOX_WIN_SRC_SANDBOX_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "base/win/scoped_process_information.h"
|
||||
#include "base/win/windows_types.h"
|
||||
#include "sandbox/win/src/sandbox_policy.h"
|
||||
#include "sandbox/win/src/sandbox_types.h"
|
||||
@ -35,7 +34,6 @@
|
||||
// sandbox: Google User-Land Application Sandbox
|
||||
namespace sandbox {
|
||||
|
||||
class BrokerServicesDelegate;
|
||||
class BrokerServicesTargetTracker;
|
||||
class PolicyDiagnosticsReceiver;
|
||||
class ProcessState;
|
||||
@ -61,22 +59,15 @@ enum class Desktop;
|
||||
// compatible definitions of the class even when LTO is enabled.
|
||||
class [[clang::lto_visibility_public]] BrokerServices {
|
||||
public:
|
||||
// The callback used for receiving the SpawnTarget() process launch result.
|
||||
// The parameters include the new process and thread handle, the Win32 last
|
||||
// error code, and the sandbox ResultCode.
|
||||
using SpawnTargetCallback = base::OnceCallback<
|
||||
void(base::win::ScopedProcessInformation, DWORD, ResultCode)>;
|
||||
|
||||
// Initializes the broker. Must be called before any other on this class.
|
||||
// returns ALL_OK if successful. All other return values imply failure.
|
||||
// If the return is ERROR_GENERIC, you can call ::GetLastError() to get
|
||||
// more information.
|
||||
virtual ResultCode Init(std::unique_ptr<BrokerServicesDelegate> delegate) = 0;
|
||||
virtual ResultCode Init() = 0;
|
||||
|
||||
// May be called in place of Init in test code to add a tracker that validates
|
||||
// job notifications and signals an event when all tracked processes are done.
|
||||
virtual ResultCode InitForTesting(
|
||||
std::unique_ptr<BrokerServicesDelegate> delegate,
|
||||
std::unique_ptr<BrokerServicesTargetTracker> target_tracker) = 0;
|
||||
|
||||
// Pre-creates an alternate desktop. Must be called before a non-default
|
||||
@ -138,18 +129,6 @@ class [[clang::lto_visibility_public]] BrokerServices {
|
||||
DWORD* last_error,
|
||||
PROCESS_INFORMATION* target) = 0;
|
||||
|
||||
// Async version of SpawnTarget that supports parallel process launching.
|
||||
// Target creation happens on the thread pool when parallel launching is
|
||||
// enabled (controlled by BrokerServicesDelegate). This function is the same
|
||||
// as SpawnTarget, except the out parameters `last_error`, `target` and final
|
||||
// ResultCode are passed to `result_callback`.
|
||||
// Returns ALL_OK if `result_callback` will run with the result. Otherwise,
|
||||
// the return error is final and `result_callback` will not be run.
|
||||
virtual ResultCode SpawnTargetAsync(const wchar_t* exe_path,
|
||||
const wchar_t* command_line,
|
||||
std::unique_ptr<TargetPolicy> policy,
|
||||
SpawnTargetCallback result_callback) = 0;
|
||||
|
||||
// This call creates a snapshot of policies managed by the sandbox and
|
||||
// returns them via a helper class.
|
||||
// Parameters:
|
||||
@ -274,38 +253,6 @@ class [[clang::lto_visibility_public]] BrokerServicesTargetTracker {
|
||||
virtual ~BrokerServicesTargetTracker() {}
|
||||
};
|
||||
|
||||
// Used internally by SpawnTarget() to return process launch info from a task.
|
||||
struct [[clang::lto_visibility_public]] CreateTargetResult {
|
||||
base::win::ScopedProcessInformation process_info;
|
||||
DWORD last_error;
|
||||
ResultCode result_code;
|
||||
};
|
||||
|
||||
// This class configures BrokerServices to use parallel or synchronous process
|
||||
// launching, and provides callbacks for implementing tracing.
|
||||
class [[clang::lto_visibility_public]] BrokerServicesDelegate {
|
||||
public:
|
||||
// Returns true if parallel launching is enabled, otherwise synchronous
|
||||
// launching is used.
|
||||
virtual bool ParallelLaunchEnabled() = 0;
|
||||
// This method runs `task` on the thread pool, then runs `reply` on the
|
||||
// calling sequence with the returned CreateTargetResult. This method must be
|
||||
// implemented if ParallelLaunchEnabled() can return true.
|
||||
virtual void ParallelLaunchPostTaskAndReplyWithResult(
|
||||
const base::Location& from_here,
|
||||
base::OnceCallback<CreateTargetResult()> task,
|
||||
base::OnceCallback<void(CreateTargetResult)> reply) = 0;
|
||||
// Called before a target process is created. If parallel launching is
|
||||
// enabled, this will be called on the thread pool.
|
||||
virtual void BeforeTargetProcessCreateOnCreationThread(
|
||||
const void* trace_id) = 0;
|
||||
// Called after a target process is created. If parallel launching is enabled,
|
||||
// this will be called on the thread pool.
|
||||
virtual void AfterTargetProcessCreateOnCreationThread(const void* trace_id,
|
||||
DWORD process_id) = 0;
|
||||
virtual ~BrokerServicesDelegate() {}
|
||||
};
|
||||
|
||||
} // namespace sandbox
|
||||
|
||||
#endif // SANDBOX_WIN_SRC_SANDBOX_H_
|
||||
|
@ -5,7 +5,6 @@
|
||||
#ifndef SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
|
||||
#define SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
|
||||
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/process/kill.h"
|
||||
#include "base/process/launch.h"
|
||||
@ -213,13 +212,6 @@ enum InterceptionType {
|
||||
INTERCEPTION_LAST // Placeholder for last item in the enumeration
|
||||
};
|
||||
|
||||
// This callback is used for returning a process launch result from
|
||||
// StartSandboxedProcess() to the child process launcher helper. The parameters
|
||||
// include the new process handle, the Win32 last error code, and the sandbox
|
||||
// ResultCode.
|
||||
using StartSandboxedProcessCallback =
|
||||
base::OnceCallback<void(base::Process, DWORD, int)>;
|
||||
|
||||
} // namespace sandbox
|
||||
|
||||
#endif // SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/dcheck_is_on.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/platform_shared_memory_region.h"
|
||||
#include "base/memory/read_only_shared_memory_region.h"
|
||||
#include "base/process/process.h"
|
||||
@ -134,28 +133,6 @@ std::wstring MakePathToSys(const wchar_t* name, bool is_obj_man_path) {
|
||||
: MakePathToSys32(name, is_obj_man_path);
|
||||
}
|
||||
|
||||
// This delegate is required for initializing BrokerServices and configures it
|
||||
// to use synchronous launching.
|
||||
class TestBrokerServicesDelegateImpl : public BrokerServicesDelegate {
|
||||
public:
|
||||
bool ParallelLaunchEnabled() override { return false; }
|
||||
|
||||
void ParallelLaunchPostTaskAndReplyWithResult(
|
||||
const base::Location& from_here,
|
||||
base::OnceCallback<CreateTargetResult()> task,
|
||||
base::OnceCallback<void(CreateTargetResult)> reply) override {
|
||||
// This function is only used for parallel launching and should not get
|
||||
// called.
|
||||
CHECK(false);
|
||||
}
|
||||
|
||||
void BeforeTargetProcessCreateOnCreationThread(
|
||||
const void* trace_id) override {}
|
||||
|
||||
void AfterTargetProcessCreateOnCreationThread(const void* trace_id,
|
||||
DWORD process_id) override {}
|
||||
};
|
||||
|
||||
BrokerServices* GetBroker() {
|
||||
static BrokerServices* broker = SandboxFactory::GetBrokerServices();
|
||||
static bool is_initialized = false;
|
||||
@ -171,9 +148,7 @@ BrokerServices* GetBroker() {
|
||||
}
|
||||
|
||||
auto tracker = std::make_unique<TargetTracker>(g_no_targets_event);
|
||||
if (SBOX_ALL_OK != broker->InitForTesting( // IN-TEST
|
||||
std::make_unique<TestBrokerServicesDelegateImpl>(),
|
||||
std::move(tracker))) {
|
||||
if (SBOX_ALL_OK != broker->InitForTesting(std::move(tracker))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -6166,17 +6166,6 @@ chromium-metrics-reviews@google.com.
|
||||
</summary>
|
||||
</histogram>
|
||||
|
||||
<histogram name="MPArch.ChildProcessLaunchActivelyInParallel" units="threads"
|
||||
expires_after="2025-09-01">
|
||||
<owner>ajgo@chromium.org</owner>
|
||||
<owner>src/sandbox/policy/win/OWNERS</owner>
|
||||
<summary>
|
||||
The number of process creation threads that are actively spawning sub
|
||||
processes. Recorded at the start of each process launch. Only recorded on
|
||||
Windows and when parallel process launching is enabled.
|
||||
</summary>
|
||||
</histogram>
|
||||
|
||||
<histogram name="MPArch.ChildProcessLauncher.{Event}" units="ms"
|
||||
expires_after="2025-01-05">
|
||||
<owner>ajgo@chromium.org</owner>
|
||||
|
Reference in New Issue
Block a user