0

Support MojoIpcz in NaCl processes

NaCl processes must use the same Mojo implementation as other Chrome
processes. The Mojo implementation is selected by a base::FeatureList
entry, and base::FeatureList is often not properly initialized in NaCl
processes.

This ensures that NaCl helper processes (on zygote-based POSIX systems
and on Windows) forward necessary information to NaCl loader processes
so that FieldTrialList and FeatureList can be appropriately
initialized. It also ensures that Mojo honors relevant features when
initialized in these processes, such that NaCl now works with the
MojoIpcz feature enabled.

LOW_COVERAGE_REASON=The lines identified as not having test coverage
most certainly do have test coverage via several browser tests.

Bug: 1299283
Change-Id: I31e381722494f9b38242e0edb406bc8d8f7a513b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3901076
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Derek Schuff <dschuff@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1051436}
This commit is contained in:
Ken Rockot
2022-09-26 22:14:29 +00:00
committed by Chromium LUCI CQ
parent 530f6816b6
commit f986fdc047
9 changed files with 87 additions and 11 deletions

@ -23,12 +23,14 @@
#include <vector>
#include "base/at_exit.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/scoped_file.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/field_trial.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/global_descriptors.h"
#include "base/posix/unix_domain_socket.h"
@ -122,17 +124,24 @@ void AddVerboseLoggingInNaclSwitch(base::CommandLine* command_line) {
// if (!child) {
void BecomeNaClLoader(base::ScopedFD browser_fd,
const NaClLoaderSystemInfo& system_info,
nacl::NaClSandbox* nacl_sandbox) {
nacl::NaClSandbox* nacl_sandbox,
const std::vector<std::string>& args) {
DCHECK(nacl_sandbox);
VLOG(1) << "NaCl loader: setting up IPC descriptor";
// Close or shutdown IPC channels that we don't need anymore.
PCHECK(0 == IGNORE_EINTR(close(kNaClZygoteDescriptor)));
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kVerboseLoggingInNacl)) {
// Append any passed switches to the forked loader's command line. This is
// necessary to get e.g. any field trial handle and feature overrides from
// whomever initiated the this fork request.
base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
base::CommandLine passed_command_line(base::CommandLine::NO_PROGRAM);
passed_command_line.InitFromArgv(args);
command_line.AppendArguments(passed_command_line, /*include_program=*/false);
if (command_line.HasSwitch(switches::kVerboseLoggingInNacl)) {
base::Environment::Create()->SetVar(
"NACLVERBOSITY",
command_line->GetSwitchValueASCII(switches::kVerboseLoggingInNacl));
command_line.GetSwitchValueASCII(switches::kVerboseLoggingInNacl));
}
// Always ignore SIGPIPE, for consistency with other Chrome processes and
@ -140,6 +149,14 @@ void BecomeNaClLoader(base::ScopedFD browser_fd,
// We do this before seccomp-bpf is initialized.
PCHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
base::FieldTrialList field_trial_list(nullptr);
base::FieldTrialList::CreateTrialsFromCommandLine(command_line,
kFieldTrialDescriptor);
auto feature_list = std::make_unique<base::FeatureList>();
base::FieldTrialList::CreateFeaturesFromCommandLine(command_line,
feature_list.get());
base::FeatureList::SetInstance(std::move(feature_list));
// Finish layer-1 sandbox initialization and initialize the layer-2 sandbox.
CHECK(!nacl_sandbox->HasOpenDirectory());
nacl_sandbox->InitializeLayerTwoSandbox();
@ -150,6 +167,7 @@ void BecomeNaClLoader(base::ScopedFD browser_fd,
browser_fd.release());
// The Mojo EDK must be initialized before using IPC.
mojo::core::InitFeatures();
mojo::core::Init();
base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
@ -165,7 +183,8 @@ void BecomeNaClLoader(base::ScopedFD browser_fd,
void ChildNaClLoaderInit(std::vector<base::ScopedFD> child_fds,
const NaClLoaderSystemInfo& system_info,
nacl::NaClSandbox* nacl_sandbox,
const std::string& channel_id) {
const std::string& channel_id,
const std::vector<std::string>& args) {
DCHECK(child_fds.size() >
std::max(content::ZygoteForkDelegate::kPIDOracleFDIndex,
content::ZygoteForkDelegate::kBrowserFDIndex));
@ -174,12 +193,18 @@ void ChildNaClLoaderInit(std::vector<base::ScopedFD> child_fds,
CHECK(content::SendZygoteChildPing(
child_fds[content::ZygoteForkDelegate::kPIDOracleFDIndex].get()));
// Stash the field trial descriptor in GlobalDescriptors so FieldTrialList
// can be initialized later. See BecomeNaClLoader().
base::GlobalDescriptors::GetInstance()->Set(
kFieldTrialDescriptor,
child_fds[content::ZygoteForkDelegate::kFieldTrialFDIndex].release());
// Save the browser socket and close the rest.
base::ScopedFD browser_fd(
std::move(child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]));
child_fds.clear();
BecomeNaClLoader(std::move(browser_fd), system_info, nacl_sandbox);
BecomeNaClLoader(std::move(browser_fd), system_info, nacl_sandbox, args);
_exit(1);
}
@ -197,6 +222,21 @@ bool HandleForkRequest(std::vector<base::ScopedFD> child_fds,
return false;
}
// Read the args passed by the launcher and prepare to forward them to our own
// forked child.
int argc;
if (!input_iter->ReadInt(&argc) || argc < 0) {
LOG(ERROR) << "nacl_helper: Invalid argument list";
return false;
}
std::vector<std::string> args(static_cast<size_t>(argc));
for (std::string& arg : args) {
if (!input_iter->ReadString(&arg)) {
LOG(ERROR) << "nacl_helper: Invalid argument list";
return false;
}
}
if (content::ZygoteForkDelegate::kNumPassedFDs != child_fds.size()) {
LOG(ERROR) << "nacl_helper: unexpected number of fds, got "
<< child_fds.size();
@ -218,7 +258,7 @@ bool HandleForkRequest(std::vector<base::ScopedFD> child_fds,
if (child_pid == 0) {
ChildNaClLoaderInit(std::move(child_fds), system_info, nacl_sandbox,
channel_id);
channel_id, args);
NOTREACHED();
}

@ -7,9 +7,12 @@
#include <string>
#include <utility>
#include "base/base_switches.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/field_trial.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_device_source.h"
#include "base/process/launch.h"
@ -39,6 +42,7 @@ int NaClBrokerMain(content::MainFunctionParams parameters) {
base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
base::PlatformThread::SetName("CrNaClBrokerMain");
mojo::core::InitFeatures();
mojo::core::Init();
base::PowerMonitor::Initialize(
@ -64,6 +68,15 @@ int NaClWin64Main() {
std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
base::FieldTrialList field_trial_list(nullptr);
base::FieldTrialList::CreateTrialsFromCommandLine(*command_line,
/*unused_fd_key=*/0);
auto feature_list = std::make_unique<base::FeatureList>();
base::FieldTrialList::CreateFeaturesFromCommandLine(*command_line,
feature_list.get());
base::FeatureList::SetInstance(std::move(feature_list));
// Copy what ContentMain() does.
base::EnableTerminationOnHeapCorruption();
base::EnableTerminationOnOutOfMemory();

@ -5,6 +5,7 @@
#include <utility>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/message_loop/message_pump_type.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_device_source.h"
@ -26,6 +27,7 @@ int NaClMain(content::MainFunctionParams parameters) {
const base::CommandLine& parsed_command_line = *parameters.command_line;
// The Mojo EDK must be initialized before using IPC.
mojo::core::InitFeatures();
mojo::core::Init();
// The main thread of the plugin services IO.

@ -1,5 +1,6 @@
include_rules = [
"+crypto",
"+mojo/core",
"+sandbox/linux/services",
"+sandbox/linux/suid",
"+sandbox/policy",

@ -18,6 +18,7 @@
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/path_service.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
@ -32,6 +33,7 @@
#include "components/nacl/loader/nacl_helper_linux.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_switches.h"
#include "mojo/core/embedder/embedder.h"
#include "sandbox/linux/services/namespace_sandbox.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
#include "sandbox/linux/suid/client/setuid_sandbox_host.h"
@ -346,6 +348,7 @@ bool NaClForkDelegate::CanHelp(const std::string& process_type,
}
pid_t NaClForkDelegate::Fork(const std::string& process_type,
const std::vector<std::string>& args,
const std::vector<int>& fds,
const std::string& channel_id) {
VLOG(1) << "NaClForkDelegate::Fork";
@ -361,6 +364,10 @@ pid_t NaClForkDelegate::Fork(const std::string& process_type,
base::Pickle write_pickle;
write_pickle.WriteInt(nacl::kNaClForkRequest);
write_pickle.WriteString(channel_id);
write_pickle.WriteInt(base::checked_cast<int>(args.size()));
for (const std::string& arg : args) {
write_pickle.WriteString(arg);
}
char reply_buf[kNaClMaxIPCMessageLength];
ssize_t reply_size = 0;

@ -44,6 +44,7 @@ class NaClForkDelegate : public content::ZygoteForkDelegate {
int* uma_sample,
int* uma_boundary_value) override;
pid_t Fork(const std::string& process_type,
const std::vector<std::string>& args,
const std::vector<int>& fds,
const std::string& channel_id) override;
bool GetTerminationStatus(pid_t pid,

@ -59,6 +59,9 @@ class ZygoteForkDelegate {
// The child process is required to write to the socket after
// successfully forking.
kPIDOracleFDIndex,
// A descriptor for a read-only shared memory region that can be mapped and
// used to initialize a base::FieldTrialList.
kFieldTrialFDIndex,
kNumPassedFDs // Number of FDs in the vector passed to Fork().
};
@ -69,6 +72,7 @@ class ZygoteForkDelegate {
// Delegate is responsible for communicating the channel ID to the
// newly created child process.
virtual pid_t Fork(const std::string& process_type,
const std::vector<std::string>& args,
const std::vector<int>& fds,
const std::string& channel_id) = 0;

@ -395,6 +395,7 @@ void Zygote::HandleGetTerminationStatus(int fd, base::PickleIterator iter) {
}
int Zygote::ForkWithRealPid(const std::string& process_type,
const std::vector<std::string>& args,
const base::GlobalDescriptors::Mapping& fd_mapping,
base::ScopedFD pid_oracle,
std::string* uma_name,
@ -416,10 +417,12 @@ int Zygote::ForkWithRealPid(const std::string& process_type,
DLOG(ERROR) << "Failed to find kMojoIPCChannel in FD mapping";
return -1;
}
int field_trial_fd = LookUpFd(fd_mapping, kFieldTrialDescriptor);
std::vector<int> fds;
fds.push_back(mojo_channel_fd); // kBrowserFDIndex
fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex
pid = helper->Fork(process_type, fds, /*channel_id=*/std::string());
fds.push_back(field_trial_fd);
pid = helper->Fork(process_type, args, fds, /*channel_id=*/std::string());
// Helpers should never return in the child process.
CHECK_NE(pid, 0);
@ -587,10 +590,14 @@ base::ProcessId Zygote::ReadArgsAndFork(base::PickleIterator iter,
mapping.push_back(ipc_backchannel_);
// Returns twice, once per process.
// Returns at most twice: once with a valid PID (in the parent process,
// returning the PID of the new child); and optionally once with a zero PID
// in the forked child process. Note that a delegate may spawn the child
// process without actually forking the calling process directly, so the
// second return path is not guanteed.
base::ProcessId child_pid =
ForkWithRealPid(process_type, mapping, std::move(pid_oracle), uma_name,
uma_sample, uma_boundary_value);
ForkWithRealPid(process_type, args, mapping, std::move(pid_oracle),
uma_name, uma_sample, uma_boundary_value);
if (!child_pid) {
// This is the child process.

@ -94,6 +94,7 @@ class Zygote {
// and |uma_boundary_value| may be set if the helper wants to make a UMA
// report via UMA_HISTOGRAM_ENUMERATION.
int ForkWithRealPid(const std::string& process_type,
const std::vector<std::string>& args,
const base::GlobalDescriptors::Mapping& fd_mapping,
base::ScopedFD pid_oracle,
std::string* uma_name,