diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-15 18:15:08 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-15 18:15:08 +0000 |
commit | abe3ad93b1996ad1b1aff121dbce6be533e579c3 (patch) | |
tree | 635542dd62d6b7df4c05a3c39f2c737730e23376 /chrome/browser | |
parent | 89d156b666af4d4f4c83579be31e9092155bbaf7 (diff) | |
download | chromium_src-abe3ad93b1996ad1b1aff121dbce6be533e579c3.zip chromium_src-abe3ad93b1996ad1b1aff121dbce6be533e579c3.tar.gz chromium_src-abe3ad93b1996ad1b1aff121dbce6be533e579c3.tar.bz2 |
Linux: Add support for chrooted renderers.
http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
Without filesystem access from the renderers, we need another way of
dealing with fontconfig and font loading.
This add support for:
* An "SBX_D" environment variable in the renderers which is used to
signal the end of dynamic linking so that the chroot can be
enforced.
* A sandbox_host process, running outside the sandbox, to deal with
fontconfig requests from the renderers. See the wiki page for
the reasoning behind making it a separate process.
* A new, custom SkFontHost for Skia. Because this is Chrome
specific, it will live outside the upstream Skia tree. This
FontHost can be configured either to drive fontconfig directly
(for the browser process and for any unsandboxed renderers) or to
use an IPC system. Since the same SkFontHost has to be linked into
both the browser and renderer (they are the same binary), this
switch has to be made at run time.
Sandbox IPC calls are rare (a couple of dozen at page load time) and
add about 50us of overhead for each call.
(Reland of r17575 which was reverted in r17577)
http://codereview.chromium.org/112074
BUG=8081
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18405 0039d316-1c4b-4281-b951-d872f2087c98
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(); } |