| Index: chrome/browser/renderer_host/render_sandbox_host_linux.cc
|
| 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 0000000000000000000000000000000000000000..13d96a80556fbb24869fb6a05033256dc9e77cc1
|
| --- /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_));
|
| +}
|
|
|