diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/renderer_host/render_sandbox_host_linux.cc | 226 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_sandbox_host_linux.h | 34 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.cc | 6 | ||||
-rw-r--r-- | chrome/browser/zygote_main_linux.cc | 57 |
4 files changed, 321 insertions, 2 deletions
diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.cc b/chrome/browser/renderer_host/render_sandbox_host_linux.cc new file mode 100644 index 0000000..c0ca6f6 --- /dev/null +++ b/chrome/browser/renderer_host/render_sandbox_host_linux.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2009 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 "chrome/browser/renderer_host/render_sandbox_host_linux.h" + +#include <stdint.h> +#include <unistd.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/poll.h> + +#include "base/eintr_wrapper.h" +#include "base/process_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/pickle.h" +#include "base/unix_domain_socket_posix.h" + +#include "SkFontHost_fontconfig_direct.h" +#include "SkFontHost_fontconfig_ipc.h" + +// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + +// BEWARE: code in this file run across *processes* (not just threads). + +// This code runs in a child process +class SandboxIPCProcess { + public: + // lifeline_fd: this is the read end of a pipe which the browser process + // holds the other end of. If the browser process dies, it's descriptors are + // closed and we will noticed an EOF on the pipe. That's our signal to exit. + // browser_socket: the 'browser's end of the sandbox IPC socketpair. From the + // point of view of the renderer's, it's talking to the browser but this + // object actually services the requests. + SandboxIPCProcess(int lifeline_fd, int browser_socket) + : lifeline_fd_(lifeline_fd), + browser_socket_(browser_socket), + font_config_(new FontConfigDirect()) { + base::InjectiveMultimap multimap; + multimap.push_back(base::InjectionArc(0, lifeline_fd, false)); + multimap.push_back(base::InjectionArc(0, browser_socket, false)); + + base::CloseSuperfluousFds(multimap); + } + + void Run() { + struct pollfd pfds[2]; + pfds[0].fd = lifeline_fd_; + pfds[0].events = POLLIN; + pfds[1].fd = browser_socket_; + pfds[1].events = POLLIN; + + bool failed_polls = 0; + for (;;) { + const int r = HANDLE_EINTR(poll(pfds, 2, -1)); + if (r < 1) { + LOG(WARNING) << "poll errno:" << errno; + if (failed_polls++ == 3) { + LOG(FATAL) << "poll failing. Sandbox host aborting."; + return; + } + continue; + } + + failed_polls = 0; + + if (pfds[0].revents) { + // our parent died so we should too. + _exit(0); + } + + if (pfds[1].revents) { + HandleRequestFromRenderer(browser_socket_); + } + } + } + + private: + // --------------------------------------------------------------------------- + // Requests from the renderer... + + void HandleRequestFromRenderer(int fd) { + std::vector<int> fds; + static const unsigned kMaxMessageLength = 2048; + char buf[kMaxMessageLength]; + const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds); + if (len == -1) + return; + if (fds.size() == 0) + return; + + Pickle pickle(buf, len); + void* iter = NULL; + + int kind; + if (!pickle.ReadInt(&iter, &kind)) + goto error; + + if (kind == FontConfigIPC::METHOD_MATCH) { + HandleFontMatchRequest(fd, pickle, iter, fds); + } else if (kind == FontConfigIPC::METHOD_OPEN) { + HandleFontOpenRequest(fd, pickle, iter, fds); + } + + error: + for (std::vector<int>::const_iterator + i = fds.begin(); i != fds.end(); ++i) { + close(*i); + } + } + + void HandleFontMatchRequest(int fd, Pickle& pickle, void* iter, + std::vector<int>& fds) { + bool fileid_valid; + uint32_t fileid; + bool is_bold, is_italic; + std::string family; + + if (!pickle.ReadBool(&iter, &fileid_valid)) + return; + if (fileid_valid) { + if (!pickle.ReadUInt32(&iter, &fileid)) + return; + } + if (!pickle.ReadBool(&iter, &is_bold) || + !pickle.ReadBool(&iter, &is_italic) || + !pickle.ReadString(&iter, &family)) { + return; + } + + std::string result_family; + unsigned result_fileid; + + const bool r = font_config_->Match( + &result_family, &result_fileid, fileid_valid, fileid, family, is_bold, + is_italic); + + Pickle reply; + if (!r) { + reply.WriteBool(false); + } else { + reply.WriteBool(true); + reply.WriteUInt32(result_fileid); + reply.WriteString(result_family); + } + SendRendererReply(fds, reply, -1); + } + + void HandleFontOpenRequest(int fd, Pickle& pickle, void* iter, + std::vector<int>& fds) { + uint32_t fileid; + if (!pickle.ReadUInt32(&iter, &fileid)) + return; + const int result_fd = font_config_->Open(fileid); + + Pickle reply; + if (result_fd == -1) { + reply.WriteBool(false); + } else { + reply.WriteBool(true); + } + + SendRendererReply(fds, reply, result_fd); + } + + void SendRendererReply(const std::vector<int>& fds, const Pickle& reply, + int reply_fd) { + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + struct iovec iov = {const_cast<void*>(reply.data()), reply.size()}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char control_buffer[CMSG_SPACE(sizeof(int))]; + + if (reply_fd != -1) { + struct cmsghdr *cmsg; + + msg.msg_control = control_buffer; + msg.msg_controllen = sizeof(control_buffer); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(int)); + msg.msg_controllen = cmsg->cmsg_len; + } + + HANDLE_EINTR(sendmsg(fds[0], &msg, MSG_DONTWAIT)); + } + + // --------------------------------------------------------------------------- + + const int lifeline_fd_; + const int browser_socket_; + FontConfigDirect* const font_config_; +}; + +// ----------------------------------------------------------------------------- + +// Runs on the main thread at startup. +RenderSandboxHostLinux::RenderSandboxHostLinux() { + int fds[2]; + CHECK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0); + + renderer_socket_ = fds[0]; + const int browser_socket = fds[1]; + + int pipefds[2]; + CHECK(0 == pipe(pipefds)); + const int child_lifeline_fd = pipefds[0]; + childs_lifeline_fd_ = pipefds[1]; + + const pid_t child = fork(); + if (child == 0) { + SandboxIPCProcess handler(child_lifeline_fd, browser_socket); + handler.Run(); + _exit(0); + } +} + +RenderSandboxHostLinux::~RenderSandboxHostLinux() { + HANDLE_EINTR(close(renderer_socket_)); + HANDLE_EINTR(close(childs_lifeline_fd_)); +} diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.h b/chrome/browser/renderer_host/render_sandbox_host_linux.h new file mode 100644 index 0000000..43fa447 --- /dev/null +++ b/chrome/browser/renderer_host/render_sandbox_host_linux.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 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. + +// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + +#ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_ +#define CHROME_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_ + +#include "base/singleton.h" +#include "base/thread.h" +#include "base/message_loop.h" + +// This is a singleton object which handles sandbox requests from the +// renderers. +class RenderSandboxHostLinux { + public: + // Get the file descriptor which renderers should be given in order to signal + // crashes to the browser. + int GetRendererSocket() const { return renderer_socket_; } + + private: + friend struct DefaultSingletonTraits<RenderSandboxHostLinux>; + // This object must be constructed on the main thread. + RenderSandboxHostLinux(); + ~RenderSandboxHostLinux(); + + int renderer_socket_; + int childs_lifeline_fd_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderSandboxHostLinux); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_ diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc index f56e6e93..1c582a3 100644 --- a/chrome/browser/zygote_host_linux.cc +++ b/chrome/browser/zygote_host_linux.cc @@ -16,6 +16,7 @@ #include "base/process_util.h" #include "base/unix_domain_socket_posix.h" +#include "chrome/browser/renderer_host/render_sandbox_host_linux.h" #include "chrome/common/chrome_switches.h" ZygoteHost::ZygoteHost() { @@ -38,6 +39,11 @@ ZygoteHost::ZygoteHost() { cmd_line.PrependWrapper(prefix); } + // Start up the sandbox host process and get the file descriptor for the + // renderers to talk to it. + const int sfd = Singleton<RenderSandboxHostLinux>()->GetRendererSocket(); + fds_to_map.push_back(std::make_pair(sfd, 4)); + base::ProcessHandle process; base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process); CHECK(process != -1) << "Failed to launch zygote process"; diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc index d450c39..6d15c0f 100644 --- a/chrome/browser/zygote_main_linux.cc +++ b/chrome/browser/zygote_main_linux.cc @@ -7,6 +7,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <sys/signal.h> +#include <sys/prctl.h> #include "base/command_line.h" #include "base/eintr_wrapper.h" @@ -19,6 +20,8 @@ #include "chrome/common/main_function_params.h" #include "chrome/common/process_watcher.h" +#include "skia/ext/SkFontHost_fontconfig_control.h" + // http://code.google.com/p/chromium/wiki/LinuxZygote // This is the object which implements the zygote. The ZygoteMain function, @@ -29,6 +32,8 @@ class Zygote { bool ProcessRequests() { // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the // browser on it. + // A SOCK_DGRAM is installed in fd 4. This is the sandbox IPC channel. + // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC // We need to accept SIGCHLD, even though our handler is a no-op because // otherwise we cannot wait on children. (According to POSIX 2001.) @@ -54,7 +59,7 @@ class Zygote { // new process and thus need to unwind back into ChromeMain. bool HandleRequestFromBrowser(int fd) { std::vector<int> fds; - static const unsigned kMaxMessageLength = 2048; + static const unsigned kMaxMessageLength = 1024; char buf[kMaxMessageLength]; const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds); if (len == -1) { @@ -135,6 +140,9 @@ class Zygote { mapping.push_back(std::make_pair(key, fds[i])); } + mapping.push_back(std::make_pair( + static_cast<uint32_t>(kSandboxIPCChannel), 4)); + child = fork(); if (!child) { @@ -159,10 +167,55 @@ class Zygote { close(*i); return false; } - // --------------------------------------------------------------------------- }; +static bool MaybeEnterChroot() { + const char* const sandbox_fd_string = getenv("SBX_D"); + if (sandbox_fd_string) { + // The SUID sandbox sets this environment variable to a file descriptor + // over which we can signal that we have completed our startup and can be + // chrooted. + + char* endptr; + const long fd_long = strtol(sandbox_fd_string, &endptr, 10); + if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) + return false; + const int fd = fd_long; + + static const char kChrootMe = 'C'; + static const char kChrootMeSuccess = 'O'; + + if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) + return false; + + char reply; + if (HANDLE_EINTR(read(fd, &reply, 1)) != 1) + return false; + if (reply != kChrootMeSuccess) + return false; + if (chdir("/") == -1) + return false; + + static const int kMagicSandboxIPCDescriptor = 4; + SkiaFontConfigUseIPCImplementation(kMagicSandboxIPCDescriptor); + + prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); + if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) + return false; + } else { + SkiaFontConfigUseDirectImplementation(); + } + + return true; +} + bool ZygoteMain(const MainFunctionParams& params) { + if (!MaybeEnterChroot()) { + LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " + << errno << ")"; + return false; + } + Zygote zygote; return zygote.ProcessRequests(); } |