This source file includes following definitions.
- NonZeroSegmentBaseIsSlow
- SendIPCRequestAndReadReply
- fd_
- Init
- InitialUMA
- CanHelp
- AckChild
- GetTerminationStatus
#include "components/nacl/zygote/nacl_fork_delegate_linux.h"
#include <signal.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <set>
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/cpu.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/global_descriptors.h"
#include "base/posix/unix_domain_socket_linux.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "components/nacl/common/nacl_paths.h"
#include "components/nacl/common/nacl_switches.h"
#include "components/nacl/loader/nacl_helper_linux.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_switches.h"
namespace {
const char kNaClHelperReservedAtZero[] =
"--reserved_at_zero=0xXXXXXXXXXXXXXXXX";
const char kNaClHelperRDebug[] = "--r_debug=0xXXXXXXXXXXXXXXXX";
#if defined(ARCH_CPU_X86)
bool NonZeroSegmentBaseIsSlow() {
base::CPU cpuid;
if (cpuid.family() == 6) {
switch (cpuid.model()) {
case 0x1c:
case 0x26:
case 0x27:
case 0x35:
case 0x36:
return true;
}
}
return false;
}
#endif
bool SendIPCRequestAndReadReply(int ipc_channel,
const std::vector<int>& attached_fds,
const Pickle& request_pickle,
char* reply_data_buffer,
size_t reply_data_buffer_size,
ssize_t* reply_size) {
DCHECK_LE(static_cast<size_t>(kNaClMaxIPCMessageLength),
reply_data_buffer_size);
DCHECK(reply_size);
if (!UnixDomainSocket::SendMsg(ipc_channel, request_pickle.data(),
request_pickle.size(), attached_fds)) {
LOG(ERROR) << "SendIPCRequestAndReadReply: SendMsg failed";
return false;
}
std::vector<int> received_fds;
const ssize_t msg_len =
UnixDomainSocket::RecvMsg(ipc_channel, reply_data_buffer,
reply_data_buffer_size, &received_fds);
if (msg_len <= 0) {
LOG(ERROR) << "SendIPCRequestAndReadReply: RecvMsg failed";
return false;
}
*reply_size = msg_len;
return true;
}
}
NaClForkDelegate::NaClForkDelegate()
: status_(kNaClHelperUnused),
fd_(-1) {}
void NaClForkDelegate::Init(const int sandboxdesc) {
VLOG(1) << "NaClForkDelegate::Init()";
int fds[2];
int nacl_sandbox_descriptor =
base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel;
DCHECK_EQ(sandboxdesc, nacl_sandbox_descriptor);
CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
base::FileHandleMappingVector fds_to_map;
fds_to_map.push_back(std::make_pair(fds[1], kNaClZygoteDescriptor));
fds_to_map.push_back(std::make_pair(sandboxdesc, nacl_sandbox_descriptor));
#if defined(ARCH_CPU_X86_64)
bool kUseNaClBootstrap = false;
#elif defined(ARCH_CPU_X86)
bool kUseNaClBootstrap = NonZeroSegmentBaseIsSlow();
#else
bool kUseNaClBootstrap = true;
#endif
status_ = kNaClHelperUnused;
base::FilePath helper_exe;
base::FilePath helper_bootstrap_exe;
if (!PathService::Get(nacl::FILE_NACL_HELPER, &helper_exe)) {
status_ = kNaClHelperMissing;
} else if (kUseNaClBootstrap &&
!PathService::Get(nacl::FILE_NACL_HELPER_BOOTSTRAP,
&helper_bootstrap_exe)) {
status_ = kNaClHelperBootstrapMissing;
} else if (RunningOnValgrind()) {
status_ = kNaClHelperValgrind;
} else {
CommandLine::StringVector argv_to_launch;
{
CommandLine cmd_line(CommandLine::NO_PROGRAM);
if (kUseNaClBootstrap)
cmd_line.SetProgram(helper_bootstrap_exe);
else
cmd_line.SetProgram(helper_exe);
static const char* kForwardSwitches[] = {
switches::kDisableSeccompFilterSandbox,
switches::kNoSandbox,
};
const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
cmd_line.CopySwitchesFrom(current_cmd_line, kForwardSwitches,
arraysize(kForwardSwitches));
argv_to_launch = cmd_line.argv();
}
if (kUseNaClBootstrap) {
CommandLine::StringVector bootstrap_prepend;
bootstrap_prepend.push_back(helper_exe.value());
bootstrap_prepend.push_back(kNaClHelperReservedAtZero);
bootstrap_prepend.push_back(kNaClHelperRDebug);
argv_to_launch.insert(argv_to_launch.begin() + 1,
bootstrap_prepend.begin(),
bootstrap_prepend.end());
}
base::LaunchOptions options;
options.fds_to_remap = &fds_to_map;
options.clone_flags = CLONE_FS | SIGCHLD;
std::vector<int> max_these_limits;
max_these_limits.push_back(RLIMIT_AS);
options.maximize_rlimits = &max_these_limits;
if (!base::LaunchProcess(argv_to_launch, options, NULL))
status_ = kNaClHelperLaunchFailed;
}
if (IGNORE_EINTR(close(fds[1])) != 0)
LOG(ERROR) << "close(fds[1]) failed";
if (status_ == kNaClHelperUnused) {
const ssize_t kExpectedLength = strlen(kNaClHelperStartupAck);
char buf[kExpectedLength];
const ssize_t nread = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
if (nread == kExpectedLength &&
memcmp(buf, kNaClHelperStartupAck, nread) == 0) {
status_ = kNaClHelperSuccess;
fd_ = fds[0];
return;
}
status_ = kNaClHelperAckFailed;
LOG(ERROR) << "Bad NaCl helper startup ack (" << nread << " bytes)";
}
fd_ = -1;
if (IGNORE_EINTR(close(fds[0])) != 0)
LOG(ERROR) << "close(fds[0]) failed";
}
void NaClForkDelegate::InitialUMA(std::string* uma_name,
int* uma_sample,
int* uma_boundary_value) {
*uma_name = "NaCl.Client.Helper.InitState";
*uma_sample = status_;
*uma_boundary_value = kNaClHelperStatusBoundary;
}
NaClForkDelegate::~NaClForkDelegate() {
if (status_ == kNaClHelperSuccess) {
if (IGNORE_EINTR(close(fd_)) != 0)
LOG(ERROR) << "close(fd_) failed";
}
}
bool NaClForkDelegate::CanHelp(const std::string& process_type,
std::string* uma_name,
int* uma_sample,
int* uma_boundary_value) {
if (process_type != switches::kNaClLoaderProcess &&
process_type != switches::kNaClLoaderNonSfiProcess)
return false;
*uma_name = "NaCl.Client.Helper.StateOnFork";
*uma_sample = status_;
*uma_boundary_value = kNaClHelperStatusBoundary;
return true;
}
pid_t NaClForkDelegate::Fork(const std::string& process_type,
const std::vector<int>& fds) {
VLOG(1) << "NaClForkDelegate::Fork";
DCHECK(fds.size() == kNumPassedFDs);
if (status_ != kNaClHelperSuccess) {
LOG(ERROR) << "Cannot launch NaCl process: nacl_helper failed to start";
return -1;
}
Pickle write_pickle;
write_pickle.WriteInt(nacl::kNaClForkRequest);
const bool uses_nonsfi_mode =
process_type == switches::kNaClLoaderNonSfiProcess;
write_pickle.WriteBool(uses_nonsfi_mode);
char reply_buf[kNaClMaxIPCMessageLength];
ssize_t reply_size = 0;
bool got_reply =
SendIPCRequestAndReadReply(fd_, fds, write_pickle,
reply_buf, sizeof(reply_buf), &reply_size);
if (!got_reply) {
LOG(ERROR) << "Could not perform remote fork.";
return -1;
}
Pickle reply_pickle(reply_buf, reply_size);
PickleIterator iter(reply_pickle);
pid_t nacl_child;
if (!iter.ReadInt(&nacl_child)) {
LOG(ERROR) << "NaClForkDelegate::Fork: pickle failed";
return -1;
}
VLOG(1) << "nacl_child is " << nacl_child;
return nacl_child;
}
bool NaClForkDelegate::AckChild(const int fd,
const std::string& channel_switch) {
int nwritten = HANDLE_EINTR(write(fd, channel_switch.c_str(),
channel_switch.length()));
if (nwritten != static_cast<int>(channel_switch.length())) {
return false;
}
return true;
}
bool NaClForkDelegate::GetTerminationStatus(pid_t pid, bool known_dead,
base::TerminationStatus* status,
int* exit_code) {
VLOG(1) << "NaClForkDelegate::GetTerminationStatus";
DCHECK(status);
DCHECK(exit_code);
Pickle write_pickle;
write_pickle.WriteInt(nacl::kNaClGetTerminationStatusRequest);
write_pickle.WriteInt(pid);
write_pickle.WriteBool(known_dead);
const std::vector<int> empty_fds;
char reply_buf[kNaClMaxIPCMessageLength];
ssize_t reply_size = 0;
bool got_reply =
SendIPCRequestAndReadReply(fd_, empty_fds, write_pickle,
reply_buf, sizeof(reply_buf), &reply_size);
if (!got_reply) {
LOG(ERROR) << "Could not perform remote GetTerminationStatus.";
return false;
}
Pickle reply_pickle(reply_buf, reply_size);
PickleIterator iter(reply_pickle);
int termination_status;
if (!iter.ReadInt(&termination_status) ||
termination_status < 0 ||
termination_status >= base::TERMINATION_STATUS_MAX_ENUM) {
LOG(ERROR) << "GetTerminationStatus: pickle failed";
return false;
}
int remote_exit_code;
if (!iter.ReadInt(&remote_exit_code)) {
LOG(ERROR) << "GetTerminationStatus: pickle failed";
return false;
}
*status = static_cast<base::TerminationStatus>(termination_status);
*exit_code = remote_exit_code;
return true;
}