1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/zygote_host/zygote_host_impl_linux.h" 6 7 #include <sys/socket.h> 8 #include <sys/stat.h> 9 #include <sys/types.h> 10 #include <unistd.h> 11 12 #include "base/base_switches.h" 13 #include "base/command_line.h" 14 #include "base/environment.h" 15 #include "base/file_util.h" 16 #include "base/files/file_enumerator.h" 17 #include "base/linux_util.h" 18 #include "base/logging.h" 19 #include "base/memory/linked_ptr.h" 20 #include "base/memory/scoped_ptr.h" 21 #include "base/metrics/histogram.h" 22 #include "base/path_service.h" 23 #include "base/posix/eintr_wrapper.h" 24 #include "base/posix/unix_domain_socket_linux.h" 25 #include "base/process/launch.h" 26 #include "base/process/memory.h" 27 #include "base/strings/string_number_conversions.h" 28 #include "base/strings/string_util.h" 29 #include "base/strings/utf_string_conversions.h" 30 #include "base/time/time.h" 31 #include "content/browser/renderer_host/render_sandbox_host_linux.h" 32 #include "content/common/child_process_sandbox_support_impl_linux.h" 33 #include "content/common/zygote_commands_linux.h" 34 #include "content/public/browser/content_browser_client.h" 35 #include "content/public/common/content_switches.h" 36 #include "content/public/common/result_codes.h" 37 #include "sandbox/linux/suid/client/setuid_sandbox_client.h" 38 #include "sandbox/linux/suid/common/sandbox.h" 39 #include "ui/base/ui_base_switches.h" 40 #include "ui/gfx/switches.h" 41 42 #if defined(USE_TCMALLOC) 43 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" 44 #endif 45 46 namespace content { 47 48 // static 49 ZygoteHost* ZygoteHost::GetInstance() { 50 return ZygoteHostImpl::GetInstance(); 51 } 52 53 ZygoteHostImpl::ZygoteHostImpl() 54 : control_fd_(-1), 55 control_lock_(), 56 pid_(-1), 57 init_(false), 58 using_suid_sandbox_(false), 59 sandbox_binary_(), 60 have_read_sandbox_status_word_(false), 61 sandbox_status_(0), 62 child_tracking_lock_(), 63 list_of_running_zygote_children_(), 64 should_teardown_after_last_child_exits_(false) {} 65 66 ZygoteHostImpl::~ZygoteHostImpl() { TearDown(); } 67 68 // static 69 ZygoteHostImpl* ZygoteHostImpl::GetInstance() { 70 return Singleton<ZygoteHostImpl>::get(); 71 } 72 73 void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { 74 DCHECK(!init_); 75 init_ = true; 76 77 base::FilePath chrome_path; 78 CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); 79 CommandLine cmd_line(chrome_path); 80 81 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); 82 83 int fds[2]; 84 #if defined(OS_FREEBSD) || defined(OS_OPENBSD) 85 // The BSDs often don't support SOCK_SEQPACKET yet, so fall back to 86 // SOCK_DGRAM if necessary. 87 if (socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) != 0) 88 CHECK(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) == 0); 89 #else 90 CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); 91 #endif 92 base::FileHandleMappingVector fds_to_map; 93 fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd)); 94 95 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 96 if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { 97 cmd_line.PrependWrapper( 98 browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); 99 } 100 // Append any switches from the browser process that need to be forwarded on 101 // to the zygote/renderers. 102 // Should this list be obtained from browser_render_process_host.cc? 103 static const char* kForwardSwitches[] = { 104 switches::kAllowSandboxDebugging, 105 switches::kLoggingLevel, 106 switches::kEnableLogging, // Support, e.g., --enable-logging=stderr. 107 switches::kV, 108 switches::kVModule, 109 switches::kRegisterPepperPlugins, 110 switches::kDisableSeccompFilterSandbox, 111 112 // Zygote process needs to know what resources to have loaded when it 113 // becomes a renderer process. 114 switches::kForceDeviceScaleFactor, 115 switches::kTouchOptimizedUI, 116 117 switches::kNoSandbox, 118 }; 119 cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, 120 arraysize(kForwardSwitches)); 121 122 GetContentClient()->browser()->AppendExtraCommandLineSwitches(&cmd_line, -1); 123 124 sandbox_binary_ = sandbox_cmd.c_str(); 125 126 // A non empty sandbox_cmd means we want a SUID sandbox. 127 using_suid_sandbox_ = !sandbox_cmd.empty(); 128 129 if (using_suid_sandbox_) { 130 struct stat st; 131 if (stat(sandbox_binary_.c_str(), &st) != 0) { 132 LOG(FATAL) << "The SUID sandbox helper binary is missing: " 133 << sandbox_binary_ << " Aborting now."; 134 } 135 136 if (access(sandbox_binary_.c_str(), X_OK) == 0 && 137 (st.st_uid == 0) && 138 (st.st_mode & S_ISUID) && 139 (st.st_mode & S_IXOTH)) { 140 cmd_line.PrependWrapper(sandbox_binary_); 141 142 scoped_ptr<sandbox::SetuidSandboxClient> 143 sandbox_client(sandbox::SetuidSandboxClient::Create()); 144 sandbox_client->SetupLaunchEnvironment(); 145 } else { 146 LOG(FATAL) << "The SUID sandbox helper binary was found, but is not " 147 "configured correctly. Rather than run without sandboxing " 148 "I'm aborting now. You need to make sure that " 149 << sandbox_binary_ << " is owned by root and has mode 4755."; 150 } 151 } 152 153 // Start up the sandbox host process and get the file descriptor for the 154 // renderers to talk to it. 155 const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); 156 fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD())); 157 158 int dummy_fd = -1; 159 if (using_suid_sandbox_) { 160 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); 161 CHECK(dummy_fd >= 0); 162 fds_to_map.push_back(std::make_pair(dummy_fd, kZygoteIdFd)); 163 } 164 165 base::ProcessHandle process = -1; 166 base::LaunchOptions options; 167 options.fds_to_remap = &fds_to_map; 168 base::LaunchProcess(cmd_line.argv(), options, &process); 169 CHECK(process != -1) << "Failed to launch zygote process"; 170 171 if (using_suid_sandbox_) { 172 // In the SUID sandbox, the real zygote is forked from the sandbox. 173 // We need to look for it. 174 // But first, wait for the zygote to tell us it's running. 175 // The sending code is in content/browser/zygote_main_linux.cc. 176 std::vector<int> fds_vec; 177 const int kExpectedLength = sizeof(kZygoteHelloMessage); 178 char buf[kExpectedLength]; 179 const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf), 180 &fds_vec); 181 CHECK(len == kExpectedLength) << "Incorrect zygote magic length"; 182 CHECK(0 == strcmp(buf, kZygoteHelloMessage)) 183 << "Incorrect zygote hello"; 184 185 std::string inode_output; 186 ino_t inode = 0; 187 // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end, 188 // and find the zygote process holding |dummy_fd|. 189 if (base::FileDescriptorGetInode(&inode, dummy_fd)) { 190 close(dummy_fd); 191 std::vector<std::string> get_inode_cmdline; 192 get_inode_cmdline.push_back(sandbox_binary_); 193 get_inode_cmdline.push_back(base::kFindInodeSwitch); 194 get_inode_cmdline.push_back(base::Int64ToString(inode)); 195 CommandLine get_inode_cmd(get_inode_cmdline); 196 if (base::GetAppOutput(get_inode_cmd, &inode_output)) { 197 base::StringToInt(inode_output, &pid_); 198 } 199 } 200 CHECK(pid_ > 0) << "Did not find zygote process (using sandbox binary " 201 << sandbox_binary_ << ")"; 202 203 if (process != pid_) { 204 // Reap the sandbox. 205 base::EnsureProcessGetsReaped(process); 206 } 207 } else { 208 // Not using the SUID sandbox. 209 pid_ = process; 210 } 211 212 close(fds[1]); 213 control_fd_ = fds[0]; 214 215 Pickle pickle; 216 pickle.WriteInt(kZygoteCommandGetSandboxStatus); 217 if (!SendMessage(pickle, NULL)) 218 LOG(FATAL) << "Cannot communicate with zygote"; 219 // We don't wait for the reply. We'll read it in ReadReply. 220 } 221 222 void ZygoteHostImpl::TearDownAfterLastChild() { 223 bool do_teardown = false; 224 { 225 base::AutoLock lock(child_tracking_lock_); 226 should_teardown_after_last_child_exits_ = true; 227 do_teardown = list_of_running_zygote_children_.empty(); 228 } 229 if (do_teardown) { 230 TearDown(); 231 } 232 } 233 234 // Note: this is also called from the destructor. 235 void ZygoteHostImpl::TearDown() { 236 base::AutoLock lock(control_lock_); 237 if (control_fd_ > -1) { 238 // Closing the IPC channel will act as a notification to exit 239 // to the Zygote. 240 if (IGNORE_EINTR(close(control_fd_))) { 241 PLOG(ERROR) << "Could not close Zygote control channel."; 242 NOTREACHED(); 243 } 244 control_fd_ = -1; 245 } 246 } 247 248 void ZygoteHostImpl::ZygoteChildBorn(pid_t process) { 249 base::AutoLock lock(child_tracking_lock_); 250 bool new_element_inserted = 251 list_of_running_zygote_children_.insert(process).second; 252 DCHECK(new_element_inserted); 253 } 254 255 void ZygoteHostImpl::ZygoteChildDied(pid_t process) { 256 bool do_teardown = false; 257 { 258 base::AutoLock lock(child_tracking_lock_); 259 size_t num_erased = list_of_running_zygote_children_.erase(process); 260 DCHECK_EQ(1U, num_erased); 261 do_teardown = should_teardown_after_last_child_exits_ && 262 list_of_running_zygote_children_.empty(); 263 } 264 if (do_teardown) { 265 TearDown(); 266 } 267 } 268 269 bool ZygoteHostImpl::SendMessage(const Pickle& data, 270 const std::vector<int>* fds) { 271 DCHECK_NE(-1, control_fd_); 272 CHECK(data.size() <= kZygoteMaxMessageLength) 273 << "Trying to send too-large message to zygote (sending " << data.size() 274 << " bytes, max is " << kZygoteMaxMessageLength << ")"; 275 CHECK(!fds || fds->size() <= UnixDomainSocket::kMaxFileDescriptors) 276 << "Trying to send message with too many file descriptors to zygote " 277 << "(sending " << fds->size() << ", max is " 278 << UnixDomainSocket::kMaxFileDescriptors << ")"; 279 280 return UnixDomainSocket::SendMsg(control_fd_, 281 data.data(), data.size(), 282 fds ? *fds : std::vector<int>()); 283 } 284 285 ssize_t ZygoteHostImpl::ReadReply(void* buf, size_t buf_len) { 286 DCHECK_NE(-1, control_fd_); 287 // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, 288 // but don't wait for the reply. Thus, the first time that we read from the 289 // zygote, we get the reply to that request. 290 if (!have_read_sandbox_status_word_) { 291 if (HANDLE_EINTR(read(control_fd_, &sandbox_status_, 292 sizeof(sandbox_status_))) != 293 sizeof(sandbox_status_)) { 294 return -1; 295 } 296 have_read_sandbox_status_word_ = true; 297 } 298 299 return HANDLE_EINTR(read(control_fd_, buf, buf_len)); 300 } 301 302 pid_t ZygoteHostImpl::ForkRequest( 303 const std::vector<std::string>& argv, 304 const std::vector<FileDescriptorInfo>& mapping, 305 const std::string& process_type) { 306 DCHECK(init_); 307 Pickle pickle; 308 309 pickle.WriteInt(kZygoteCommandFork); 310 pickle.WriteString(process_type); 311 pickle.WriteInt(argv.size()); 312 for (std::vector<std::string>::const_iterator 313 i = argv.begin(); i != argv.end(); ++i) 314 pickle.WriteString(*i); 315 316 pickle.WriteInt(mapping.size()); 317 318 std::vector<int> fds; 319 // Scoped pointers cannot be stored in containers, so we have to use a 320 // linked_ptr. 321 std::vector<linked_ptr<file_util::ScopedFD> > autodelete_fds; 322 for (std::vector<FileDescriptorInfo>::const_iterator 323 i = mapping.begin(); i != mapping.end(); ++i) { 324 pickle.WriteUInt32(i->id); 325 fds.push_back(i->fd.fd); 326 if (i->fd.auto_close) { 327 // Auto-close means we need to close the FDs after they have been passed 328 // to the other process. 329 linked_ptr<file_util::ScopedFD> ptr( 330 new file_util::ScopedFD(&(fds.back()))); 331 autodelete_fds.push_back(ptr); 332 } 333 } 334 335 pid_t pid; 336 { 337 base::AutoLock lock(control_lock_); 338 if (!SendMessage(pickle, &fds)) 339 return base::kNullProcessHandle; 340 341 // Read the reply, which pickles the PID and an optional UMA enumeration. 342 static const unsigned kMaxReplyLength = 2048; 343 char buf[kMaxReplyLength]; 344 const ssize_t len = ReadReply(buf, sizeof(buf)); 345 346 Pickle reply_pickle(buf, len); 347 PickleIterator iter(reply_pickle); 348 if (len <= 0 || !reply_pickle.ReadInt(&iter, &pid)) 349 return base::kNullProcessHandle; 350 351 // If there is a nonempty UMA name string, then there is a UMA 352 // enumeration to record. 353 std::string uma_name; 354 int uma_sample; 355 int uma_boundary_value; 356 if (reply_pickle.ReadString(&iter, &uma_name) && 357 !uma_name.empty() && 358 reply_pickle.ReadInt(&iter, &uma_sample) && 359 reply_pickle.ReadInt(&iter, &uma_boundary_value)) { 360 // We cannot use the UMA_HISTOGRAM_ENUMERATION macro here, 361 // because that's only for when the name is the same every time. 362 // Here we're using whatever name we got from the other side. 363 // But since it's likely that the same one will be used repeatedly 364 // (even though it's not guaranteed), we cache it here. 365 static base::HistogramBase* uma_histogram; 366 if (!uma_histogram || uma_histogram->histogram_name() != uma_name) { 367 uma_histogram = base::LinearHistogram::FactoryGet( 368 uma_name, 1, 369 uma_boundary_value, 370 uma_boundary_value + 1, 371 base::HistogramBase::kUmaTargetedHistogramFlag); 372 } 373 uma_histogram->Add(uma_sample); 374 } 375 376 if (pid <= 0) 377 return base::kNullProcessHandle; 378 } 379 380 #if !defined(OS_OPENBSD) 381 // This is just a starting score for a renderer or extension (the 382 // only types of processes that will be started this way). It will 383 // get adjusted as time goes on. (This is the same value as 384 // chrome::kLowestRendererOomScore in chrome/chrome_constants.h, but 385 // that's not something we can include here.) 386 const int kLowestRendererOomScore = 300; 387 AdjustRendererOOMScore(pid, kLowestRendererOomScore); 388 #endif 389 390 ZygoteChildBorn(pid); 391 return pid; 392 } 393 394 #if !defined(OS_OPENBSD) 395 void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid, 396 int score) { 397 // 1) You can't change the oom_score_adj of a non-dumpable process 398 // (EPERM) unless you're root. Because of this, we can't set the 399 // oom_adj from the browser process. 400 // 401 // 2) We can't set the oom_score_adj before entering the sandbox 402 // because the zygote is in the sandbox and the zygote is as 403 // critical as the browser process. Its oom_adj value shouldn't 404 // be changed. 405 // 406 // 3) A non-dumpable process can't even change its own oom_score_adj 407 // because it's root owned 0644. The sandboxed processes don't 408 // even have /proc, but one could imagine passing in a descriptor 409 // from outside. 410 // 411 // So, in the normal case, we use the SUID binary to change it for us. 412 // However, Fedora (and other SELinux systems) don't like us touching other 413 // process's oom_score_adj (or oom_adj) values 414 // (https://bugzilla.redhat.com/show_bug.cgi?id=581256). 415 // 416 // The offical way to get the SELinux mode is selinux_getenforcemode, but I 417 // don't want to add another library to the build as it's sure to cause 418 // problems with other, non-SELinux distros. 419 // 420 // So we just check for files in /selinux. This isn't foolproof, but it's not 421 // bad and it's easy. 422 423 static bool selinux; 424 static bool selinux_valid = false; 425 426 if (!selinux_valid) { 427 const base::FilePath kSelinuxPath("/selinux"); 428 base::FileEnumerator en(kSelinuxPath, false, base::FileEnumerator::FILES); 429 bool has_selinux_files = !en.Next().empty(); 430 431 selinux = access(kSelinuxPath.value().c_str(), X_OK) == 0 && 432 has_selinux_files; 433 selinux_valid = true; 434 } 435 436 if (using_suid_sandbox_ && !selinux) { 437 #if defined(USE_TCMALLOC) 438 // If heap profiling is running, these processes are not exiting, at least 439 // on ChromeOS. The easiest thing to do is not launch them when profiling. 440 // TODO(stevenjb): Investigate further and fix. 441 if (IsHeapProfilerRunning()) 442 return; 443 #endif 444 std::vector<std::string> adj_oom_score_cmdline; 445 adj_oom_score_cmdline.push_back(sandbox_binary_); 446 adj_oom_score_cmdline.push_back(sandbox::kAdjustOOMScoreSwitch); 447 adj_oom_score_cmdline.push_back(base::Int64ToString(pid)); 448 adj_oom_score_cmdline.push_back(base::IntToString(score)); 449 450 base::ProcessHandle sandbox_helper_process; 451 if (base::LaunchProcess(adj_oom_score_cmdline, base::LaunchOptions(), 452 &sandbox_helper_process)) { 453 base::EnsureProcessGetsReaped(sandbox_helper_process); 454 } 455 } else if (!using_suid_sandbox_) { 456 if (!base::AdjustOOMScore(pid, score)) 457 PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid; 458 } 459 } 460 #endif 461 462 void ZygoteHostImpl::EnsureProcessTerminated(pid_t process) { 463 DCHECK(init_); 464 Pickle pickle; 465 466 pickle.WriteInt(kZygoteCommandReap); 467 pickle.WriteInt(process); 468 if (!SendMessage(pickle, NULL)) 469 LOG(ERROR) << "Failed to send Reap message to zygote"; 470 ZygoteChildDied(process); 471 } 472 473 base::TerminationStatus ZygoteHostImpl::GetTerminationStatus( 474 base::ProcessHandle handle, 475 bool known_dead, 476 int* exit_code) { 477 DCHECK(init_); 478 Pickle pickle; 479 pickle.WriteInt(kZygoteCommandGetTerminationStatus); 480 pickle.WriteBool(known_dead); 481 pickle.WriteInt(handle); 482 483 static const unsigned kMaxMessageLength = 128; 484 char buf[kMaxMessageLength]; 485 ssize_t len; 486 { 487 base::AutoLock lock(control_lock_); 488 if (!SendMessage(pickle, NULL)) 489 LOG(ERROR) << "Failed to send GetTerminationStatus message to zygote"; 490 len = ReadReply(buf, sizeof(buf)); 491 } 492 493 // Set this now to handle the error cases. 494 if (exit_code) 495 *exit_code = RESULT_CODE_NORMAL_EXIT; 496 int status = base::TERMINATION_STATUS_NORMAL_TERMINATION; 497 498 if (len == -1) { 499 LOG(WARNING) << "Error reading message from zygote: " << errno; 500 } else if (len == 0) { 501 LOG(WARNING) << "Socket closed prematurely."; 502 } else { 503 Pickle read_pickle(buf, len); 504 int tmp_status, tmp_exit_code; 505 PickleIterator iter(read_pickle); 506 if (!read_pickle.ReadInt(&iter, &tmp_status) || 507 !read_pickle.ReadInt(&iter, &tmp_exit_code)) { 508 LOG(WARNING) 509 << "Error parsing GetTerminationStatus response from zygote."; 510 } else { 511 if (exit_code) 512 *exit_code = tmp_exit_code; 513 status = tmp_status; 514 } 515 } 516 517 if (status != base::TERMINATION_STATUS_STILL_RUNNING) { 518 ZygoteChildDied(handle); 519 } 520 return static_cast<base::TerminationStatus>(status); 521 } 522 523 pid_t ZygoteHostImpl::GetPid() const { 524 return pid_; 525 } 526 527 pid_t ZygoteHostImpl::GetSandboxHelperPid() const { 528 return RenderSandboxHostLinux::GetInstance()->pid(); 529 } 530 531 int ZygoteHostImpl::GetSandboxStatus() const { 532 if (have_read_sandbox_status_word_) 533 return sandbox_status_; 534 return 0; 535 } 536 537 } // namespace content 538