
This CL adds a service process host option to pin user32 prior to lockdown in utility main, and plumbs the option from the service process host. Echo service tests are added to exercise the function in user32 which motivated user32 pinning for services. As content_browsertests effectively pins user32 anyway this test always passes, but it will prevent regressions. Services will migrate to use this API in a following CL. Bug: 1435571,1408988 Change-Id: Ie36f16987f05cdcf42c70d28afbb04f6678c1e5e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4534183 Reviewed-by: Ken Rockot <rockot@google.com> Commit-Queue: Alex Gough <ajgo@chromium.org> Reviewed-by: Nasko Oskov <nasko@chromium.org> Reviewed-by: Will Harris <wfh@chromium.org> Cr-Commit-Position: refs/heads/main@{#1146962}
271 lines
9.4 KiB
C++
271 lines
9.4 KiB
C++
// Copyright 2019 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "base/functional/bind.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
#include "base/no_destructor.h"
|
|
#include "base/observer_list.h"
|
|
#include "base/process/process.h"
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "base/synchronization/lock.h"
|
|
#include "build/chromecast_buildflags.h"
|
|
#include "content/browser/utility_process_host.h"
|
|
#include "content/common/child_process.mojom.h"
|
|
#include "content/public/browser/browser_task_traits.h"
|
|
#include "content/public/browser/browser_thread.h"
|
|
#include "content/public/browser/content_browser_client.h"
|
|
#include "content/public/browser/service_process_host.h"
|
|
#include "content/public/browser/service_process_info.h"
|
|
#include "content/public/common/content_client.h"
|
|
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
|
|
#include "mojo/public/cpp/bindings/remote.h"
|
|
#include "sandbox/policy/mojom/sandbox.mojom.h"
|
|
|
|
#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
|
|
#include "base/process/process_handle.h"
|
|
#endif
|
|
|
|
namespace content {
|
|
|
|
namespace {
|
|
|
|
// Changes to this function should be reviewed by a security person.
|
|
bool ShouldEnableSandbox(sandbox::mojom::Sandbox sandbox) {
|
|
if (sandbox == sandbox::mojom::Sandbox::kAudio)
|
|
return GetContentClient()->browser()->ShouldSandboxAudioService();
|
|
if (sandbox == sandbox::mojom::Sandbox::kNetwork)
|
|
return GetContentClient()->browser()->ShouldSandboxNetworkService();
|
|
return true;
|
|
}
|
|
|
|
// Internal helper to track running service processes.
|
|
class ServiceProcessTracker {
|
|
public:
|
|
ServiceProcessTracker() = default;
|
|
|
|
ServiceProcessTracker(const ServiceProcessTracker&) = delete;
|
|
ServiceProcessTracker& operator=(const ServiceProcessTracker&) = delete;
|
|
|
|
~ServiceProcessTracker() = default;
|
|
|
|
ServiceProcessInfo AddProcess(base::Process process,
|
|
const absl::optional<GURL>& site,
|
|
const std::string& service_interface_name) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
auto id = GenerateNextId();
|
|
ServiceProcessInfo info(service_interface_name, site, id,
|
|
std::move(process));
|
|
auto info_dup = info.Duplicate();
|
|
processes_.insert({id, std::move(info)});
|
|
for (auto& observer : observers_)
|
|
observer.OnServiceProcessLaunched(info_dup);
|
|
return info_dup;
|
|
}
|
|
|
|
void NotifyTerminated(ServiceProcessId id) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
auto iter = processes_.find(id);
|
|
DCHECK(iter != processes_.end());
|
|
|
|
for (auto& observer : observers_)
|
|
observer.OnServiceProcessTerminatedNormally(iter->second.Duplicate());
|
|
processes_.erase(iter);
|
|
}
|
|
|
|
void NotifyCrashed(ServiceProcessId id) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
auto iter = processes_.find(id);
|
|
DCHECK(iter != processes_.end());
|
|
for (auto& observer : observers_)
|
|
observer.OnServiceProcessCrashed(iter->second.Duplicate());
|
|
processes_.erase(iter);
|
|
}
|
|
|
|
void AddObserver(ServiceProcessHost::Observer* observer) {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
observers_.AddObserver(observer);
|
|
}
|
|
|
|
void RemoveObserver(ServiceProcessHost::Observer* observer) {
|
|
// NOTE: Some tests may remove observers after BrowserThreads are shut down.
|
|
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
|
|
!BrowserThread::IsThreadInitialized(BrowserThread::UI));
|
|
observers_.RemoveObserver(observer);
|
|
}
|
|
|
|
std::vector<ServiceProcessInfo> GetProcesses() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
std::vector<ServiceProcessInfo> processes;
|
|
for (const auto& entry : processes_)
|
|
processes.push_back(entry.second.Duplicate());
|
|
return processes;
|
|
}
|
|
|
|
private:
|
|
ServiceProcessId GenerateNextId() {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
return service_process_id_generator_.GenerateNextId();
|
|
}
|
|
|
|
ServiceProcessId::Generator service_process_id_generator_;
|
|
|
|
std::map<ServiceProcessId, ServiceProcessInfo> processes_;
|
|
|
|
// Observers are owned and used exclusively on the UI thread.
|
|
base::ObserverList<ServiceProcessHost::Observer> observers_;
|
|
};
|
|
|
|
ServiceProcessTracker& GetServiceProcessTracker() {
|
|
static base::NoDestructor<ServiceProcessTracker> tracker;
|
|
return *tracker;
|
|
}
|
|
|
|
// Helper to bridge UtilityProcessHost IO thread events to the
|
|
// ServiceProcessTracker. Every UtilityProcessHost created for a service process
|
|
// has a unique instance of this class associated with it.
|
|
class UtilityProcessClient : public UtilityProcessHost::Client {
|
|
public:
|
|
UtilityProcessClient(
|
|
const std::string& service_interface_name,
|
|
const absl::optional<GURL>& site,
|
|
base::OnceCallback<void(const base::Process&)> process_callback)
|
|
: service_interface_name_(service_interface_name),
|
|
site_(std::move(site)),
|
|
process_callback_(std::move(process_callback)) {}
|
|
|
|
UtilityProcessClient(const UtilityProcessClient&) = delete;
|
|
UtilityProcessClient& operator=(const UtilityProcessClient&) = delete;
|
|
|
|
~UtilityProcessClient() override = default;
|
|
|
|
// UtilityProcessHost::Client:
|
|
void OnProcessLaunched(const base::Process& process) override {
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
process_info_.emplace(GetServiceProcessTracker().AddProcess(
|
|
process.Duplicate(), site_, service_interface_name_));
|
|
if (process_callback_) {
|
|
std::move(process_callback_).Run(process);
|
|
}
|
|
}
|
|
|
|
void OnProcessTerminatedNormally() override {
|
|
GetServiceProcessTracker().NotifyTerminated(
|
|
process_info_->service_process_id());
|
|
}
|
|
|
|
void OnProcessCrashed() override {
|
|
// TODO(https://crbug.com/1016027): It is unclear how we can observe
|
|
// |OnProcessCrashed()| without observing |OnProcessLaunched()| first, but
|
|
// it can happen on Android. Ignore the notification in this case.
|
|
if (!process_info_)
|
|
return;
|
|
|
|
GetServiceProcessTracker().NotifyCrashed(
|
|
process_info_->service_process_id());
|
|
}
|
|
|
|
private:
|
|
const std::string service_interface_name_;
|
|
|
|
// Optional site GURL for per-site utility processes.
|
|
const absl::optional<GURL> site_;
|
|
|
|
base::OnceCallback<void(const base::Process&)> process_callback_;
|
|
absl::optional<ServiceProcessInfo> process_info_;
|
|
};
|
|
|
|
// TODO(crbug.com/977637): Once UtilityProcessHost is used only by service
|
|
// processes, its logic can be inlined here.
|
|
void LaunchServiceProcess(mojo::GenericPendingReceiver receiver,
|
|
ServiceProcessHost::Options options,
|
|
sandbox::mojom::Sandbox sandbox) {
|
|
UtilityProcessHost* host =
|
|
new UtilityProcessHost(std::make_unique<UtilityProcessClient>(
|
|
*receiver.interface_name(), options.site,
|
|
std::move(options.process_callback)));
|
|
host->SetName(!options.display_name.empty()
|
|
? options.display_name
|
|
: base::UTF8ToUTF16(*receiver.interface_name()));
|
|
host->SetMetricsName(*receiver.interface_name());
|
|
if (!ShouldEnableSandbox(sandbox)) {
|
|
sandbox = sandbox::mojom::Sandbox::kNoSandbox;
|
|
}
|
|
host->SetSandboxType(sandbox);
|
|
host->SetExtraCommandLineSwitches(std::move(options.extra_switches));
|
|
if (options.child_flags) {
|
|
host->set_child_flags(*options.child_flags);
|
|
}
|
|
#if BUILDFLAG(IS_WIN)
|
|
if (!options.preload_libraries.empty()) {
|
|
host->SetPreloadLibraries(options.preload_libraries);
|
|
}
|
|
if (options.pin_user32) {
|
|
host->SetPinUser32();
|
|
}
|
|
#endif // BUILDFLAG(IS_WIN)
|
|
host->Start();
|
|
host->GetChildProcess()->BindServiceInterface(std::move(receiver));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
std::vector<ServiceProcessInfo> ServiceProcessHost::GetRunningProcessInfo() {
|
|
return GetServiceProcessTracker().GetProcesses();
|
|
}
|
|
|
|
// static
|
|
void ServiceProcessHost::AddObserver(Observer* observer) {
|
|
GetServiceProcessTracker().AddObserver(observer);
|
|
}
|
|
|
|
// static
|
|
void ServiceProcessHost::RemoveObserver(Observer* observer) {
|
|
GetServiceProcessTracker().RemoveObserver(observer);
|
|
}
|
|
|
|
// static
|
|
void ServiceProcessHost::Launch(mojo::GenericPendingReceiver receiver,
|
|
Options options,
|
|
sandbox::mojom::Sandbox sandbox) {
|
|
DCHECK(receiver.interface_name().has_value());
|
|
if (GetUIThreadTaskRunner({})->BelongsToCurrentThread()) {
|
|
LaunchServiceProcess(std::move(receiver), std::move(options), sandbox);
|
|
} else {
|
|
GetUIThreadTaskRunner({})->PostTask(
|
|
FROM_HERE, base::BindOnce(&LaunchServiceProcess, std::move(receiver),
|
|
std::move(options), sandbox));
|
|
}
|
|
}
|
|
|
|
// TODO(crbug.com/1328879): Remove this method when fixing the bug.
|
|
#if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
|
|
void LaunchUtilityProcessServiceDeprecated(
|
|
const std::string& service_name,
|
|
const std::u16string& display_name,
|
|
sandbox::mojom::Sandbox sandbox_type,
|
|
mojo::ScopedMessagePipeHandle service_pipe,
|
|
base::OnceCallback<void(base::ProcessId)> callback) {
|
|
UtilityProcessHost* host = new UtilityProcessHost();
|
|
host->SetName(display_name);
|
|
host->SetMetricsName(service_name);
|
|
host->SetSandboxType(sandbox_type);
|
|
host->Start();
|
|
host->RunServiceDeprecated(
|
|
service_name, std::move(service_pipe),
|
|
base::BindOnce(
|
|
[](base::OnceCallback<void(base::ProcessId)> callback,
|
|
const absl::optional<base::ProcessId> pid) {
|
|
std::move(callback).Run(pid.value_or(base::kNullProcessId));
|
|
},
|
|
std::move(callback)));
|
|
}
|
|
#endif
|
|
|
|
} // namespace content
|