blob: f61d81382f1e354942d9b9e03a033c1aa59a1d0c [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/zygote_host/zygote_host_impl_linux.h"
#include "base/allocator/allocator_extension.h"
#include "base/files/file_enumerator.h"
#include "base/process/kill.h"
#include "base/process/memory.h"
#include "base/strings/string_number_conversions.h"
#include "content/public/browser/content_browser_client.h"
#include "sandbox/linux/suid/common/sandbox.h"
namespace content {
// static
ZygoteHost* ZygoteHost::GetInstance() {
return ZygoteHostImpl::GetInstance();
}
ZygoteHostImpl::ZygoteHostImpl()
: use_suid_sandbox_for_adj_oom_score_(false),
sandbox_binary_(),
zygote_pids_lock_(),
zygote_pids_() {}
ZygoteHostImpl::~ZygoteHostImpl() {}
// static
ZygoteHostImpl* ZygoteHostImpl::GetInstance() {
return base::Singleton<ZygoteHostImpl>::get();
}
void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
sandbox_binary_ = sandbox_cmd;
}
void ZygoteHostImpl::AddZygotePid(pid_t pid) {
base::AutoLock lock(zygote_pids_lock_);
zygote_pids_.insert(pid);
}
bool ZygoteHostImpl::IsZygotePid(pid_t pid) {
base::AutoLock lock(zygote_pids_lock_);
return zygote_pids_.find(pid) != zygote_pids_.end();
}
const std::string& ZygoteHostImpl::SandboxCommand() const {
return sandbox_binary_;
}
void ZygoteHostImpl::SetRendererSandboxStatus(int status) {
renderer_sandbox_status_ = status;
}
int ZygoteHostImpl::GetRendererSandboxStatus() const {
return renderer_sandbox_status_;
}
#if !defined(OS_OPENBSD)
void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid,
int score) {
// 1) You can't change the oom_score_adj of a non-dumpable process
// (EPERM) unless you're root. Because of this, we can't set the
// oom_adj from the browser process.
//
// 2) We can't set the oom_score_adj before entering the sandbox
// because the zygote is in the sandbox and the zygote is as
// critical as the browser process. Its oom_adj value shouldn't
// be changed.
//
// 3) A non-dumpable process can't even change its own oom_score_adj
// because it's root owned 0644. The sandboxed processes don't
// even have /proc, but one could imagine passing in a descriptor
// from outside.
//
// So, in the normal case, we use the SUID binary to change it for us.
// However, Fedora (and other SELinux systems) don't like us touching other
// process's oom_score_adj (or oom_adj) values
// (https://bugzilla.redhat.com/show_bug.cgi?id=581256).
//
// The offical way to get the SELinux mode is selinux_getenforcemode, but I
// don't want to add another library to the build as it's sure to cause
// problems with other, non-SELinux distros.
//
// So we just check for files in /selinux. This isn't foolproof, but it's not
// bad and it's easy.
static bool selinux;
static bool selinux_valid = false;
if (!selinux_valid) {
const base::FilePath kSelinuxPath("/selinux");
base::FileEnumerator en(kSelinuxPath, false, base::FileEnumerator::FILES);
bool has_selinux_files = !en.Next().empty();
selinux = access(kSelinuxPath.value().c_str(), X_OK) == 0 &&
has_selinux_files;
selinux_valid = true;
}
if (use_suid_sandbox_for_adj_oom_score_ && !selinux) {
// If heap profiling is running, these processes are not exiting, at least
// on ChromeOS. The easiest thing to do is not launch them when profiling.
// TODO(stevenjb): Investigate further and fix.
if (base::allocator::IsHeapProfilerRunning())
return;
std::vector<std::string> adj_oom_score_cmdline;
adj_oom_score_cmdline.push_back(sandbox_binary_);
adj_oom_score_cmdline.push_back(sandbox::kAdjustOOMScoreSwitch);
adj_oom_score_cmdline.push_back(base::Int64ToString(pid));
adj_oom_score_cmdline.push_back(base::IntToString(score));
base::Process sandbox_helper_process;
base::LaunchOptions options;
// sandbox_helper_process is a setuid binary.
options.allow_new_privs = true;
sandbox_helper_process =
base::LaunchProcess(adj_oom_score_cmdline, options);
if (sandbox_helper_process.IsValid())
base::EnsureProcessGetsReaped(sandbox_helper_process.Pid());
} else if (!use_suid_sandbox_for_adj_oom_score_) {
if (!base::AdjustOOMScore(pid, score))
PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid;
}
}
#endif
} // namespace content