summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-04 00:04:07 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-04 00:04:07 +0000
commit998e781dfa85253775ac18ed6e1334ee13928d24 (patch)
tree31a4b3fef07141649fc8cca48ae56a053aee31f4 /chrome
parent085bdc928b2ecdac1148b14539e079a85e96f4c3 (diff)
downloadchromium_src-998e781dfa85253775ac18ed6e1334ee13928d24.zip
chromium_src-998e781dfa85253775ac18ed6e1334ee13928d24.tar.gz
chromium_src-998e781dfa85253775ac18ed6e1334ee13928d24.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. http://codereview.chromium.org/112074 BUG=8081 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17575 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/chrome_dll_main.cc14
-rw-r--r--chrome/browser/browser_main.cc11
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc5
-rw-r--r--chrome/browser/renderer_host/render_sandbox_host_linux.cc257
-rw-r--r--chrome/browser/renderer_host/render_sandbox_host_linux.h34
-rw-r--r--chrome/chrome.gyp3
-rw-r--r--chrome/renderer/renderer_main_platform_delegate_linux.cc45
7 files changed, 352 insertions, 17 deletions
diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc
index 006d923..b6c8b51 100644
--- a/chrome/app/chrome_dll_main.cc
+++ b/chrome/app/chrome_dll_main.cc
@@ -399,20 +399,6 @@ int ChromeMain(int argc, const char** argv) {
InitCrashReporter();
#endif
-#if defined(OS_POSIX)
- // Bug 11776: we mistakenly created directories world-readable.
- // Fix old instances of these directories manually.
- // TODO(evanm): remove this code in a month or two.
- if (user_data_dir.empty()) {
- FilePath fix_dir;
- CHECK(PathService::Get(chrome::DIR_USER_DATA, &fix_dir));
- struct stat statbuf;
- CHECK(stat(fix_dir.value().c_str(), &statbuf) == 0);
- if ((statbuf.st_mode & 0077) != 0)
- CHECK(chmod(fix_dir.value().c_str(), 0700) == 0);
- }
-#endif
-
bool single_process =
#if defined (GOOGLE_CHROME_BUILD)
// This is an unsupported and not fully tested mode, so don't enable it for
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index f7145a4..0b4ffc3 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -64,6 +64,7 @@
#if defined(OS_LINUX)
#include "chrome/app/breakpad_linux.h"
+#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
#endif
// TODO(port): several win-only methods have been pulled out of this, but
@@ -218,6 +219,8 @@ void AddFirstRunNewTabs(BrowserInit* browser_init,
} // namespace
+extern void SkiaFontConfigUseDirectImplementation();
+
// Main routine for running as the Browser process.
int BrowserMain(const MainFunctionParams& parameters) {
const CommandLine& parsed_command_line = parameters.command_line_;
@@ -246,6 +249,14 @@ int BrowserMain(const MainFunctionParams& parameters) {
CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
#endif
+#if defined(OS_LINUX)
+ // Construct the sandbox host on the UI thread.
+ Singleton<RenderSandboxHostLinux>::get();
+
+ // Configure Skia in this process to use fontconfig directly.
+ SkiaFontConfigUseDirectImplementation();
+#endif
+
// Do platform-specific things (such as finishing initializing Cocoa)
// prior to instantiating the message loop. This could be turned into a
// broadcast notification.
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 8cee7ae..7e8d30e 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -36,6 +36,7 @@
#include "chrome/browser/profile.h"
#if defined(OS_LINUX)
#include "chrome/browser/renderer_host/render_crash_handler_host_linux.h"
+#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
#endif
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_widget_helper.h"
@@ -345,6 +346,10 @@ bool BrowserRenderProcessHost::Init() {
Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
if (crash_signal_fd >= 0)
fds_to_map.push_back(std::make_pair(crash_signal_fd, 4));
+ const int sandbox_ipc_fd =
+ Singleton<RenderSandboxHostLinux>()->GetRendererSocket();
+ if (sandbox_ipc_fd >= 0)
+ fds_to_map.push_back(std::make_pair(sandbox_ipc_fd, 5));
#endif
base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
#endif
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..13d96a8
--- /dev/null
+++ b/chrome/browser/renderer_host/render_sandbox_host_linux.cc
@@ -0,0 +1,257 @@
+// 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/epoll.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/process_util.h"
+#include "base/logging.h"
+#include "base/message_loop.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() {
+ const int epollfd = epoll_create(2);
+ CHECK(epollfd >= 0);
+ struct epoll_event ev;
+
+ ev.events = EPOLLIN;
+ ev.data.fd = lifeline_fd_;
+ CHECK(0 == epoll_ctl(epollfd, EPOLL_CTL_ADD, lifeline_fd_, &ev));
+
+ ev.events = EPOLLIN;
+ ev.data.fd = browser_socket_;
+ CHECK(0 == epoll_ctl(epollfd, EPOLL_CTL_ADD, browser_socket_, &ev));
+
+ for (;;) {
+ CHECK(1 == HANDLE_EINTR(epoll_wait(epollfd, &ev, 1, -1)));
+ if (ev.data.fd == lifeline_fd_) {
+ // our parent died so we should too.
+ _exit(0);
+ } else {
+ CHECK(ev.data.fd == browser_socket_);
+ HandleRequest(browser_socket_);
+ }
+ }
+ }
+
+ void HandleRequest(int fd) {
+ struct msghdr msg = {0};
+ struct iovec iov;
+ uint8_t buf[1024];
+ uint8_t control_buf[CMSG_SPACE(sizeof(int))];
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control_buf;
+ msg.msg_controllen = sizeof(control_buf);
+
+ const ssize_t n = HANDLE_EINTR(recvmsg(fd, &msg, 0));
+
+ if (n < 1) {
+ LOG(ERROR) << "Error reading from sandbox IPC socket. Sandbox IPC is"
+ << " disabled."
+ << " n:" << n
+ << " errno:" << errno;
+ _exit(1);
+ return;
+ }
+
+ if (msg.msg_controllen != sizeof(control_buf) ||
+ n < static_cast<ssize_t>(sizeof(uint16_t)) ||
+ msg.msg_flags) {
+ LOG(ERROR) << "Sandbox IPC: missing control message or truncated message:"
+ << " n:" << n
+ << " msg.msg_controllen:" << msg.msg_controllen
+ << " msg.msg_flags:" << msg.msg_flags;
+ return;
+ }
+
+ // Get the reply socket from the control message
+ int reply_fd = -1;
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ // The client cannot send us additional descriptors because the control
+ // message buffer is only sized for a single int.
+ reply_fd = *reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ } else {
+ LOG(ERROR) << "Sandbox IPC: message without reply descriptor:"
+ << " n:" << n
+ << " msg.msg_controllen:" << msg.msg_controllen
+ << " cmsg->cmsg_level:" << cmsg->cmsg_level
+ << " cmsg->cmsg_type:" << cmsg->cmsg_type;
+ return;
+ }
+
+ const uint16_t request_type = *reinterpret_cast<uint16_t*>(buf);
+ switch (request_type) {
+ case FontConfigIPC::METHOD_MATCH:
+ return FontConfigMatch(reply_fd, buf, n);
+ case FontConfigIPC::METHOD_OPEN:
+ return FontConfigOpen(reply_fd, buf, n);
+ default:
+ LOG(ERROR) << "Sandbox IPC: message with unknown type:"
+ << " request_type:" << request_type;
+ HANDLE_EINTR(close(reply_fd));
+ }
+ }
+
+ // Send a reply to a client
+ // reply_fd: the reply channel given to us by the client
+ // iov, iov_len: the contents of the reply message
+ // extra_fd: an fd to include in the reply, or -1
+ //
+ // Both reply_fd and extra_fd (if any) are closed.
+ void SendReplyAndClose(int reply_fd, const struct iovec* iov,
+ unsigned iov_len, int extra_fd) {
+ struct msghdr msg = {0};
+ msg.msg_iov = const_cast<struct iovec*>(iov);
+ msg.msg_iovlen = iov_len;
+
+ uint8_t control_buf[CMSG_SPACE(sizeof(int))];
+
+ if (extra_fd >= 0) {
+ msg.msg_control = control_buf;
+ msg.msg_controllen = sizeof(control_buf);
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = extra_fd;
+ }
+
+ HANDLE_EINTR(sendmsg(reply_fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT));
+ HANDLE_EINTR(close(reply_fd));
+ if (extra_fd >= 0)
+ HANDLE_EINTR(close(extra_fd));
+ }
+
+ void FontConfigMatch(int reply_fd, const uint8_t* request_bytes,
+ unsigned request_len) {
+ if (request_len < sizeof(FontConfigIPC::MatchRequest))
+ return (void) HANDLE_EINTR(close(reply_fd));
+
+ const FontConfigIPC::MatchRequest* request =
+ reinterpret_cast<const FontConfigIPC::MatchRequest*>(request_bytes);
+
+ if (request_len != sizeof(FontConfigIPC::MatchRequest) + request->family_len)
+ return (void) HANDLE_EINTR(close(reply_fd));
+
+ const std::string family(
+ reinterpret_cast<const char*>(request_bytes + sizeof(*request)),
+ request->family_len);
+ std::string result_family;
+ unsigned result_fileid;
+
+ const bool r = font_config_->Match(
+ &result_family, &result_fileid, request->fileid_valid, request->fileid,
+ family, request->is_bold, request->is_italic);
+
+ struct iovec iov[2];
+ FontConfigIPC::MatchReply reply;
+ memset(&reply, 0, sizeof(reply));
+
+ iov[0].iov_base = &reply;
+ iov[0].iov_len = sizeof(reply);
+
+ if (r) {
+ reply.result = 1;
+ reply.result_fileid = result_fileid;
+ reply.filename_len = result_family.size();
+
+ iov[1].iov_base = const_cast<char*>(result_family.data());
+ iov[1].iov_len = result_family.size();
+ }
+
+ SendReplyAndClose(reply_fd, iov, r ? 2 : 1, -1 /* no fd */);
+ }
+
+ void FontConfigOpen(int reply_fd, const uint8_t* request_bytes,
+ unsigned request_len) {
+ if (request_len < sizeof(FontConfigIPC::OpenRequest))
+ return (void) HANDLE_EINTR(close(reply_fd));
+
+ const FontConfigIPC::OpenRequest* request =
+ reinterpret_cast<const FontConfigIPC::OpenRequest*>(request_bytes);
+
+ FontConfigDirect* fc = reinterpret_cast<FontConfigDirect*>(font_config_);
+
+ const int result_fd = fc->Open(request->fileid);
+
+ FontConfigIPC::OpenReply reply;
+ reply.result = result_fd >= 0 ? 1 : 0;
+
+ struct iovec iov;
+ iov.iov_base = &reply;
+ iov.iov_len = sizeof(reply);
+
+ SendReplyAndClose(reply_fd, &iov, 1, result_fd);
+ }
+
+ private:
+ 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/chrome.gyp b/chrome/chrome.gyp
index 0fc4a1d..20518ea 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1183,6 +1183,8 @@
'browser/renderer_host/download_throttling_resource_handler.h',
'browser/renderer_host/render_process_host.cc',
'browser/renderer_host/render_process_host.h',
+ 'browser/renderer_host/render_sandbox_host_linux.cc',
+ 'browser/renderer_host/render_sandbox_host_linux.h',
'browser/renderer_host/render_view_host.cc',
'browser/renderer_host/render_view_host.h',
'browser/renderer_host/render_view_host_delegate.h',
@@ -1589,6 +1591,7 @@
# '../build/linux/system.gyp:dbus-glib',
# '../build/linux/system.gyp:gnome-keyring',
'../build/linux/system.gyp:gtk',
+ '../skia/skia.gyp:skia',
],
'sources!': [
'browser/extensions/extension_shelf.cc',
diff --git a/chrome/renderer/renderer_main_platform_delegate_linux.cc b/chrome/renderer/renderer_main_platform_delegate_linux.cc
index 6502129..e997bc9 100644
--- a/chrome/renderer/renderer_main_platform_delegate_linux.cc
+++ b/chrome/renderer/renderer_main_platform_delegate_linux.cc
@@ -4,7 +4,10 @@
#include "chrome/renderer/renderer_main_platform_delegate.h"
+#include <stdlib.h>
+
#include "base/debug_util.h"
+#include "base/eintr_wrapper.h"
// This is a no op class because we do not have a sandbox on linux.
@@ -16,6 +19,9 @@ RendererMainPlatformDelegate::RendererMainPlatformDelegate(
RendererMainPlatformDelegate::~RendererMainPlatformDelegate() {
}
+extern void SkiaFontConfigUseIPCImplementation(int fd);
+extern void SkiaFontConfigUseDirectImplementation();
+
void RendererMainPlatformDelegate::PlatformInitialize() {
}
@@ -23,18 +29,51 @@ void RendererMainPlatformDelegate::PlatformUninitialize() {
}
bool RendererMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
- // We have no sandbox.
+ // Our sandbox support is in the very early stages
// http://code.google.com/p/chromium/issues/detail?id=8081
return true;
}
bool RendererMainPlatformDelegate::EnableSandbox() {
- // We have no sandbox.
+ // Our sandbox support is in the very early stages
// http://code.google.com/p/chromium/issues/detail?id=8081
+
+ 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 = 5;
+ SkiaFontConfigUseIPCImplementation(kMagicSandboxIPCDescriptor);
+ } else {
+ SkiaFontConfigUseDirectImplementation();
+ }
+
return true;
}
void RendererMainPlatformDelegate::RunSandboxTests() {
- // We have no sandbox.
+ // Our sandbox support is in the very early stages
// http://code.google.com/p/chromium/issues/detail?id=8081
}