blob: f01bcf11620bb0a9a83e778fbef159f41d9f0637 [file] [log] [blame]
[email protected]17773042010-07-28 17:35:071// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]cc8f1462009-06-12 17:36:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/zygote_host_linux.h"
6
[email protected]cc8f1462009-06-12 17:36:557#include <sys/socket.h>
[email protected]4378a822009-07-08 01:15:148#include <sys/stat.h>
[email protected]8ecd3aad2009-11-04 08:32:229#include <sys/types.h>
10#include <unistd.h>
[email protected]cc8f1462009-06-12 17:36:5511
12#include "base/command_line.h"
13#include "base/eintr_wrapper.h"
[email protected]76b90d312010-08-03 03:00:5014#include "base/environment.h"
[email protected]8ecd3aad2009-11-04 08:32:2215#include "base/linux_util.h"
[email protected]cc8f1462009-06-12 17:36:5516#include "base/logging.h"
17#include "base/path_service.h"
18#include "base/pickle.h"
19#include "base/process_util.h"
[email protected]528c56d2010-07-30 19:28:4420#include "base/string_number_conversions.h"
[email protected]4378a822009-07-08 01:15:1421#include "base/string_util.h"
[email protected]fc586c72010-07-31 16:55:4022#include "base/scoped_ptr.h"
[email protected]cc8f1462009-06-12 17:36:5523#include "base/unix_domain_socket_posix.h"
[email protected]be1ce6a72010-08-03 14:35:2224#include "base/utf_string_conversions.h"
[email protected]cc8f1462009-06-12 17:36:5525
[email protected]abe3ad92009-06-15 18:15:0826#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
[email protected]4378a822009-07-08 01:15:1427#include "chrome/common/chrome_constants.h"
[email protected]cc8f1462009-06-12 17:36:5528#include "chrome/common/chrome_switches.h"
[email protected]8ecd3aad2009-11-04 08:32:2229#include "chrome/common/process_watcher.h"
[email protected]cc8f1462009-06-12 17:36:5530
[email protected]1b5d28f2010-02-18 15:53:3631#include "sandbox/linux/suid/suid_unsafe_environment_variables.h"
32
33static void SaveSUIDUnsafeEnvironmentVariables() {
34 // The ELF loader will clear many environment variables so we save them to
35 // different names here so that the SUID sandbox can resolve them for the
36 // renderer.
37
38 for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
39 const char* const envvar = kSUIDUnsafeEnvironmentVariables[i];
40 char* const saved_envvar = SandboxSavedEnvironmentVariable(envvar);
41 if (!saved_envvar)
42 continue;
43
[email protected]76b90d312010-08-03 03:00:5044 scoped_ptr<base::Environment> env(base::Environment::Create());
[email protected]fc586c72010-07-31 16:55:4045 std::string value;
[email protected]3ba7e082010-08-07 02:57:5946 if (env->GetVar(envvar, &value))
[email protected]c87bcf002010-08-06 01:03:3747 env->SetVar(saved_envvar, value);
[email protected]1b5d28f2010-02-18 15:53:3648 else
[email protected]4fae3162010-08-04 02:13:3449 env->UnSetVar(saved_envvar);
[email protected]1b5d28f2010-02-18 15:53:3650
51 free(saved_envvar);
52 }
53}
54
[email protected]8ecd3aad2009-11-04 08:32:2255ZygoteHost::ZygoteHost()
56 : pid_(-1),
[email protected]e5856a7a2009-12-10 02:08:1057 init_(false),
[email protected]715b4f262010-07-13 14:17:2858 using_suid_sandbox_(false),
59 have_read_sandbox_status_word_(false) {
[email protected]8ecd3aad2009-11-04 08:32:2260}
61
62ZygoteHost::~ZygoteHost() {
63 if (init_)
64 close(control_fd_);
65}
66
67void ZygoteHost::Init(const std::string& sandbox_cmd) {
68 DCHECK(!init_);
69 init_ = true;
70
[email protected]ab926d552009-10-19 22:54:4171 FilePath chrome_path;
[email protected]cc8f1462009-06-12 17:36:5572 CHECK(PathService::Get(base::FILE_EXE, &chrome_path));
73 CommandLine cmd_line(chrome_path);
74
[email protected]05076ba22010-07-30 05:59:5775 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess);
[email protected]cc8f1462009-06-12 17:36:5576
77 int fds[2];
78 CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
79 base::file_handle_mapping_vector fds_to_map;
80 fds_to_map.push_back(std::make_pair(fds[1], 3));
81
82 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
83 if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) {
[email protected]13081fc2010-08-04 18:24:3884 cmd_line.PrependWrapper(
85 browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix));
[email protected]cc8f1462009-06-12 17:36:5586 }
[email protected]3bba9582009-08-05 01:29:1287 // Append any switches from the browser process that need to be forwarded on
88 // to the zygote/renderers.
[email protected]68621912009-09-09 19:51:5989 // Should this list be obtained from browser_render_process_host.cc?
[email protected]17773042010-07-28 17:35:0790 static const char* kForwardSwitches[] = {
91 switches::kAllowSandboxDebugging,
92 switches::kLoggingLevel,
93 switches::kEnableLogging, // Support, e.g., --enable-logging=stderr.
94 switches::kUserDataDir, // Make logs go to the right file.
95 // Load (in-process) Pepper plugins in-process in the zygote pre-sandbox.
96 switches::kRegisterPepperPlugins,
[email protected]39c4e1a82010-03-30 19:47:4197#if defined(USE_SECCOMP_SANDBOX)
[email protected]17773042010-07-28 17:35:0798 switches::kDisableSeccompSandbox,
[email protected]39c4e1a82010-03-30 19:47:4199#else
[email protected]17773042010-07-28 17:35:07100 switches::kEnableSeccompSandbox,
[email protected]39c4e1a82010-03-30 19:47:41101#endif
[email protected]17773042010-07-28 17:35:07102 };
[email protected]4f08c83f2010-07-29 23:02:34103 cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches,
104 arraysize(kForwardSwitches));
[email protected]cc8f1462009-06-12 17:36:55105
[email protected]e5856a7a2009-12-10 02:08:10106 sandbox_binary_ = sandbox_cmd.c_str();
[email protected]1b5d28f2010-02-18 15:53:36107 struct stat st;
108
109 if (!sandbox_cmd.empty() && stat(sandbox_binary_.c_str(), &st) == 0) {
110 if (access(sandbox_binary_.c_str(), X_OK) == 0 &&
111 (st.st_uid == 0) &&
112 (st.st_mode & S_ISUID) &&
113 (st.st_mode & S_IXOTH)) {
114 using_suid_sandbox_ = true;
[email protected]13081fc2010-08-04 18:24:38115 cmd_line.PrependWrapper(sandbox_binary_);
[email protected]1b5d28f2010-02-18 15:53:36116
117 SaveSUIDUnsafeEnvironmentVariables();
118 } else {
119 LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
120 "configured correctly. Rather than run without sandboxing "
121 "I'm aborting now. You need to make sure that "
122 << sandbox_binary_ << " is mode 4755 and owned by root.";
123 }
124 }
[email protected]4378a822009-07-08 01:15:14125
[email protected]abe3ad92009-06-15 18:15:08126 // Start up the sandbox host process and get the file descriptor for the
127 // renderers to talk to it.
128 const int sfd = Singleton<RenderSandboxHostLinux>()->GetRendererSocket();
[email protected]e00f9da2009-06-26 00:47:05129 fds_to_map.push_back(std::make_pair(sfd, 5));
[email protected]abe3ad92009-06-15 18:15:08130
[email protected]8ecd3aad2009-11-04 08:32:22131 int dummy_fd = -1;
[email protected]e5856a7a2009-12-10 02:08:10132 if (using_suid_sandbox_) {
[email protected]8ecd3aad2009-11-04 08:32:22133 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
134 CHECK(dummy_fd >= 0);
135 fds_to_map.push_back(std::make_pair(dummy_fd, 7));
136 }
137
[email protected]cc8f1462009-06-12 17:36:55138 base::ProcessHandle process;
139 base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
140 CHECK(process != -1) << "Failed to launch zygote process";
141
[email protected]e5856a7a2009-12-10 02:08:10142 if (using_suid_sandbox_) {
[email protected]8ecd3aad2009-11-04 08:32:22143 // In the SUID sandbox, the real zygote is forked from the sandbox.
144 // We need to look for it.
145 // But first, wait for the zygote to tell us it's running.
146 // The sending code is in chrome/browser/zygote_main_linux.cc.
147 std::vector<int> fds_vec;
148 const int kExpectedLength = sizeof(kZygoteMagic);
149 char buf[kExpectedLength];
150 const ssize_t len = base::RecvMsg(fds[0], buf, sizeof(buf), &fds_vec);
151 CHECK(len == kExpectedLength) << "Incorrect zygote magic length";
152 CHECK(0 == strcmp(buf, kZygoteMagic)) << "Incorrect zygote magic";
153
154 std::string inode_output;
155 ino_t inode = 0;
156 // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end,
157 // and find the zygote process holding |dummy_fd|.
158 if (base::FileDescriptorGetInode(&inode, dummy_fd)) {
159 close(dummy_fd);
160 std::vector<std::string> get_inode_cmdline;
[email protected]e5856a7a2009-12-10 02:08:10161 get_inode_cmdline.push_back(sandbox_binary_);
[email protected]8ecd3aad2009-11-04 08:32:22162 get_inode_cmdline.push_back(base::kFindInodeSwitch);
[email protected]528c56d2010-07-30 19:28:44163 get_inode_cmdline.push_back(base::Int64ToString(inode));
[email protected]8ecd3aad2009-11-04 08:32:22164 CommandLine get_inode_cmd(get_inode_cmdline);
165 if (base::GetAppOutput(get_inode_cmd, &inode_output)) {
[email protected]e83326f2010-07-31 17:29:25166 base::StringToInt(inode_output, &pid_);
[email protected]8ecd3aad2009-11-04 08:32:22167 }
168 }
[email protected]1c71576e2010-01-04 00:23:57169 CHECK(pid_ > 0) << "Did not find zygote process (using sandbox binary "
170 << sandbox_binary_ << ")";
[email protected]8ecd3aad2009-11-04 08:32:22171
172 if (process != pid_) {
173 // Reap the sandbox.
174 ProcessWatcher::EnsureProcessGetsReaped(process);
175 }
176 } else {
177 // Not using the SUID sandbox.
178 pid_ = process;
179 }
180
[email protected]cc8f1462009-06-12 17:36:55181 close(fds[1]);
182 control_fd_ = fds[0];
[email protected]715b4f262010-07-13 14:17:28183
184 Pickle pickle;
185 pickle.WriteInt(kCmdGetSandboxStatus);
186 std::vector<int> empty_fds;
187 if (!base::SendMsg(control_fd_, pickle.data(), pickle.size(), empty_fds))
188 LOG(FATAL) << "Cannot communicate with zygote";
189 // We don't wait for the reply. We'll read it in ReadReply.
190}
191
192ssize_t ZygoteHost::ReadReply(void* buf, size_t buf_len) {
193 // At startup we send a kCmdGetSandboxStatus request to the zygote, but don't
194 // wait for the reply. Thus, the first time that we read from the zygote, we
195 // get the reply to that request.
196 if (!have_read_sandbox_status_word_) {
197 if (HANDLE_EINTR(read(control_fd_, &sandbox_status_,
198 sizeof(sandbox_status_))) !=
199 sizeof(sandbox_status_)) {
200 return -1;
201 }
202 have_read_sandbox_status_word_ = true;
203 }
204
205 return HANDLE_EINTR(read(control_fd_, buf, buf_len));
[email protected]cc8f1462009-06-12 17:36:55206}
207
[email protected]cc8f1462009-06-12 17:36:55208pid_t ZygoteHost::ForkRenderer(
209 const std::vector<std::string>& argv,
210 const base::GlobalDescriptors::Mapping& mapping) {
[email protected]8ecd3aad2009-11-04 08:32:22211 DCHECK(init_);
[email protected]cc8f1462009-06-12 17:36:55212 Pickle pickle;
213
214 pickle.WriteInt(kCmdFork);
215 pickle.WriteInt(argv.size());
216 for (std::vector<std::string>::const_iterator
217 i = argv.begin(); i != argv.end(); ++i)
218 pickle.WriteString(*i);
219
220 pickle.WriteInt(mapping.size());
221
222 std::vector<int> fds;
223 for (base::GlobalDescriptors::Mapping::const_iterator
224 i = mapping.begin(); i != mapping.end(); ++i) {
225 pickle.WriteUInt32(i->first);
226 fds.push_back(i->second);
227 }
228
[email protected]cc8f1462009-06-12 17:36:55229 pid_t pid;
[email protected]b74538cf2010-05-07 17:22:40230 {
231 AutoLock lock(control_lock_);
232 if (!base::SendMsg(control_fd_, pickle.data(), pickle.size(), fds))
233 return base::kNullProcessHandle;
234
[email protected]715b4f262010-07-13 14:17:28235 if (ReadReply(&pid, sizeof(pid)) != sizeof(pid))
[email protected]b74538cf2010-05-07 17:22:40236 return base::kNullProcessHandle;
237 }
[email protected]cc8f1462009-06-12 17:36:55238
[email protected]c7571b52010-05-07 18:54:50239 // 1) You can't change the oom_adj of a non-dumpable process (EPERM) unless
240 // you're root. Because of this, we can't set the oom_adj from the browser
241 // process.
242 //
243 // 2) We can't set the oom_adj before entering the sandbox because the
244 // zygote is in the sandbox and the zygote is as critical as the browser
245 // process. Its oom_adj value shouldn't be changed.
246 //
247 // 3) A non-dumpable process can't even change its own oom_adj because it's
248 // root owned 0644. The sandboxed processes don't even have /proc, but one
249 // could imagine passing in a descriptor from outside.
250 //
251 // So, in the normal case, we use the SUID binary to change it for us.
252 // However, Fedora (and other SELinux systems) don't like us touching other
253 // process's oom_adj values
254 // (https://bugzilla.redhat.com/show_bug.cgi?id=581256).
255 //
256 // The offical way to get the SELinux mode is selinux_getenforcemode, but I
257 // don't want to add another library to the build as it's sure to cause
258 // problems with other, non-SELinux distros.
259 //
260 // So we just check for /selinux. This isn't foolproof, but it's not bad
261 // and it's easy.
262
263 static bool selinux;
264 static bool selinux_valid = false;
265
266 if (!selinux_valid) {
267 selinux = access("/selinux", X_OK) == 0;
268 selinux_valid = true;
269 }
270
[email protected]e5856a7a2009-12-10 02:08:10271 const int kRendererScore = 5;
[email protected]c7571b52010-05-07 18:54:50272 if (using_suid_sandbox_ && !selinux) {
[email protected]e5856a7a2009-12-10 02:08:10273 base::ProcessHandle sandbox_helper_process;
274 base::file_handle_mapping_vector dummy_map;
275 std::vector<std::string> adj_oom_score_cmdline;
276
277 adj_oom_score_cmdline.push_back(sandbox_binary_);
278 adj_oom_score_cmdline.push_back(base::kAdjustOOMScoreSwitch);
[email protected]528c56d2010-07-30 19:28:44279 adj_oom_score_cmdline.push_back(base::Int64ToString(pid));
280 adj_oom_score_cmdline.push_back(base::IntToString(kRendererScore));
[email protected]e5856a7a2009-12-10 02:08:10281 CommandLine adj_oom_score_cmd(adj_oom_score_cmdline);
282 if (base::LaunchApp(adj_oom_score_cmdline, dummy_map, false,
283 &sandbox_helper_process)) {
284 ProcessWatcher::EnsureProcessGetsReaped(sandbox_helper_process);
285 }
[email protected]c7571b52010-05-07 18:54:50286 } else if (!using_suid_sandbox_) {
287 if (!base::AdjustOOMScore(pid, kRendererScore))
288 LOG(ERROR) << "Failed to adjust OOM score of renderer";
[email protected]e5856a7a2009-12-10 02:08:10289 }
290
[email protected]cc8f1462009-06-12 17:36:55291 return pid;
292}
293
294void ZygoteHost::EnsureProcessTerminated(pid_t process) {
[email protected]8ecd3aad2009-11-04 08:32:22295 DCHECK(init_);
[email protected]cc8f1462009-06-12 17:36:55296 Pickle pickle;
297
298 pickle.WriteInt(kCmdReap);
299 pickle.WriteInt(process);
300
[email protected]19cb9292010-04-16 23:00:15301 if (HANDLE_EINTR(write(control_fd_, pickle.data(), pickle.size())) < 0)
302 PLOG(ERROR) << "write";
[email protected]cc8f1462009-06-12 17:36:55303}
[email protected]57113ea2009-06-18 02:23:16304
305bool ZygoteHost::DidProcessCrash(base::ProcessHandle handle,
306 bool* child_exited) {
[email protected]8ecd3aad2009-11-04 08:32:22307 DCHECK(init_);
[email protected]57113ea2009-06-18 02:23:16308 Pickle pickle;
309 pickle.WriteInt(kCmdDidProcessCrash);
310 pickle.WriteInt(handle);
311
[email protected]57113ea2009-06-18 02:23:16312 static const unsigned kMaxMessageLength = 128;
313 char buf[kMaxMessageLength];
[email protected]b74538cf2010-05-07 17:22:40314 ssize_t len;
315 {
316 AutoLock lock(control_lock_);
317 if (HANDLE_EINTR(write(control_fd_, pickle.data(), pickle.size())) < 0)
318 PLOG(ERROR) << "write";
319
[email protected]715b4f262010-07-13 14:17:28320 len = ReadReply(buf, sizeof(buf));
[email protected]b74538cf2010-05-07 17:22:40321 }
[email protected]57113ea2009-06-18 02:23:16322
323 if (len == -1) {
324 LOG(WARNING) << "Error reading message from zygote: " << errno;
325 return false;
326 } else if (len == 0) {
327 LOG(WARNING) << "Socket closed prematurely.";
328 return false;
329 }
330
331 Pickle read_pickle(buf, len);
332 bool did_crash, tmp_child_exited;
333 void* iter = NULL;
334 if (!read_pickle.ReadBool(&iter, &did_crash) ||
335 !read_pickle.ReadBool(&iter, &tmp_child_exited)) {
336 LOG(WARNING) << "Error parsing DidProcessCrash response from zygote.";
337 return false;
338 }
339
340 if (child_exited)
341 *child_exited = tmp_child_exited;
342
343 return did_crash;
344}