OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 "chrome/browser/renderer_host/render_sandbox_host_linux.h" |
| 6 |
| 7 #include <stdint.h> |
| 8 #include <unistd.h> |
| 9 #include <sys/uio.h> |
| 10 #include <sys/socket.h> |
| 11 #include <sys/epoll.h> |
| 12 |
| 13 #include "base/eintr_wrapper.h" |
| 14 #include "base/process_util.h" |
| 15 #include "base/logging.h" |
| 16 #include "base/message_loop.h" |
| 17 |
| 18 #include "SkFontHost_fontconfig_direct.h" |
| 19 #include "SkFontHost_fontconfig_ipc.h" |
| 20 |
| 21 // http://code.google.com/p/chromium/wiki/LinuxSandboxIPC |
| 22 |
| 23 // BEWARE: code in this file run across *processes* (not just threads). |
| 24 |
| 25 // This code runs in a child process |
| 26 class SandboxIPCProcess { |
| 27 public: |
| 28 // lifeline_fd: this is the read end of a pipe which the browser process |
| 29 // holds the other end of. If the browser process dies, it's descriptors are |
| 30 // closed and we will noticed an EOF on the pipe. That's our signal to exit. |
| 31 // browser_socket: the 'browser's end of the sandbox IPC socketpair. From the |
| 32 // point of view of the renderer's, it's talking to the browser but this |
| 33 // object actually services the requests. |
| 34 SandboxIPCProcess(int lifeline_fd, int browser_socket) |
| 35 : lifeline_fd_(lifeline_fd), |
| 36 browser_socket_(browser_socket), |
| 37 font_config_(new FontConfigDirect()) { |
| 38 base::InjectiveMultimap multimap; |
| 39 multimap.push_back(base::InjectionArc(0, lifeline_fd, false)); |
| 40 multimap.push_back(base::InjectionArc(0, browser_socket, false)); |
| 41 |
| 42 base::CloseSuperfluousFds(multimap); |
| 43 } |
| 44 |
| 45 void Run() { |
| 46 const int epollfd = epoll_create(2); |
| 47 CHECK(epollfd >= 0); |
| 48 struct epoll_event ev; |
| 49 |
| 50 ev.events = EPOLLIN; |
| 51 ev.data.fd = lifeline_fd_; |
| 52 CHECK(0 == epoll_ctl(epollfd, EPOLL_CTL_ADD, lifeline_fd_, &ev)); |
| 53 |
| 54 ev.events = EPOLLIN; |
| 55 ev.data.fd = browser_socket_; |
| 56 CHECK(0 == epoll_ctl(epollfd, EPOLL_CTL_ADD, browser_socket_, &ev)); |
| 57 |
| 58 for (;;) { |
| 59 CHECK(1 == HANDLE_EINTR(epoll_wait(epollfd, &ev, 1, -1))); |
| 60 if (ev.data.fd == lifeline_fd_) { |
| 61 // our parent died so we should too. |
| 62 _exit(0); |
| 63 } else { |
| 64 CHECK(ev.data.fd == browser_socket_); |
| 65 HandleRequest(browser_socket_); |
| 66 } |
| 67 } |
| 68 } |
| 69 |
| 70 void HandleRequest(int fd) { |
| 71 struct msghdr msg = {0}; |
| 72 struct iovec iov; |
| 73 uint8_t buf[1024]; |
| 74 uint8_t control_buf[CMSG_SPACE(sizeof(int))]; |
| 75 iov.iov_base = buf; |
| 76 iov.iov_len = sizeof(buf); |
| 77 msg.msg_iov = &iov; |
| 78 msg.msg_iovlen = 1; |
| 79 msg.msg_control = control_buf; |
| 80 msg.msg_controllen = sizeof(control_buf); |
| 81 |
| 82 const ssize_t n = HANDLE_EINTR(recvmsg(fd, &msg, 0)); |
| 83 |
| 84 if (n < 1) { |
| 85 LOG(ERROR) << "Error reading from sandbox IPC socket. Sandbox IPC is" |
| 86 << " disabled." |
| 87 << " n:" << n |
| 88 << " errno:" << errno; |
| 89 _exit(1); |
| 90 return; |
| 91 } |
| 92 |
| 93 if (msg.msg_controllen != sizeof(control_buf) || |
| 94 n < static_cast<ssize_t>(sizeof(uint16_t)) || |
| 95 msg.msg_flags) { |
| 96 LOG(ERROR) << "Sandbox IPC: missing control message or truncated message:" |
| 97 << " n:" << n |
| 98 << " msg.msg_controllen:" << msg.msg_controllen |
| 99 << " msg.msg_flags:" << msg.msg_flags; |
| 100 return; |
| 101 } |
| 102 |
| 103 // Get the reply socket from the control message |
| 104 int reply_fd = -1; |
| 105 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); |
| 106 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { |
| 107 // The client cannot send us additional descriptors because the control |
| 108 // message buffer is only sized for a single int. |
| 109 reply_fd = *reinterpret_cast<int*>(CMSG_DATA(cmsg)); |
| 110 } else { |
| 111 LOG(ERROR) << "Sandbox IPC: message without reply descriptor:" |
| 112 << " n:" << n |
| 113 << " msg.msg_controllen:" << msg.msg_controllen |
| 114 << " cmsg->cmsg_level:" << cmsg->cmsg_level |
| 115 << " cmsg->cmsg_type:" << cmsg->cmsg_type; |
| 116 return; |
| 117 } |
| 118 |
| 119 const uint16_t request_type = *reinterpret_cast<uint16_t*>(buf); |
| 120 switch (request_type) { |
| 121 case FontConfigIPC::METHOD_MATCH: |
| 122 return FontConfigMatch(reply_fd, buf, n); |
| 123 case FontConfigIPC::METHOD_OPEN: |
| 124 return FontConfigOpen(reply_fd, buf, n); |
| 125 default: |
| 126 LOG(ERROR) << "Sandbox IPC: message with unknown type:" |
| 127 << " request_type:" << request_type; |
| 128 HANDLE_EINTR(close(reply_fd)); |
| 129 } |
| 130 } |
| 131 |
| 132 // Send a reply to a client |
| 133 // reply_fd: the reply channel given to us by the client |
| 134 // iov, iov_len: the contents of the reply message |
| 135 // extra_fd: an fd to include in the reply, or -1 |
| 136 // |
| 137 // Both reply_fd and extra_fd (if any) are closed. |
| 138 void SendReplyAndClose(int reply_fd, const struct iovec* iov, |
| 139 unsigned iov_len, int extra_fd) { |
| 140 struct msghdr msg = {0}; |
| 141 msg.msg_iov = const_cast<struct iovec*>(iov); |
| 142 msg.msg_iovlen = iov_len; |
| 143 |
| 144 uint8_t control_buf[CMSG_SPACE(sizeof(int))]; |
| 145 |
| 146 if (extra_fd >= 0) { |
| 147 msg.msg_control = control_buf; |
| 148 msg.msg_controllen = sizeof(control_buf); |
| 149 |
| 150 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); |
| 151 cmsg->cmsg_level = SOL_SOCKET; |
| 152 cmsg->cmsg_type = SCM_RIGHTS; |
| 153 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
| 154 *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = extra_fd; |
| 155 } |
| 156 |
| 157 HANDLE_EINTR(sendmsg(reply_fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT)); |
| 158 HANDLE_EINTR(close(reply_fd)); |
| 159 if (extra_fd >= 0) |
| 160 HANDLE_EINTR(close(extra_fd)); |
| 161 } |
| 162 |
| 163 void FontConfigMatch(int reply_fd, const uint8_t* request_bytes, |
| 164 unsigned request_len) { |
| 165 if (request_len < sizeof(FontConfigIPC::MatchRequest)) |
| 166 return (void) HANDLE_EINTR(close(reply_fd)); |
| 167 |
| 168 const FontConfigIPC::MatchRequest* request = |
| 169 reinterpret_cast<const FontConfigIPC::MatchRequest*>(request_bytes); |
| 170 |
| 171 if (request_len != sizeof(FontConfigIPC::MatchRequest) + request->family_len
) |
| 172 return (void) HANDLE_EINTR(close(reply_fd)); |
| 173 |
| 174 const std::string family( |
| 175 reinterpret_cast<const char*>(request_bytes + sizeof(*request)), |
| 176 request->family_len); |
| 177 std::string result_family; |
| 178 unsigned result_fileid; |
| 179 |
| 180 const bool r = font_config_->Match( |
| 181 &result_family, &result_fileid, request->fileid_valid, request->fileid, |
| 182 family, request->is_bold, request->is_italic); |
| 183 |
| 184 struct iovec iov[2]; |
| 185 FontConfigIPC::MatchReply reply; |
| 186 memset(&reply, 0, sizeof(reply)); |
| 187 |
| 188 iov[0].iov_base = &reply; |
| 189 iov[0].iov_len = sizeof(reply); |
| 190 |
| 191 if (r) { |
| 192 reply.result = 1; |
| 193 reply.result_fileid = result_fileid; |
| 194 reply.filename_len = result_family.size(); |
| 195 |
| 196 iov[1].iov_base = const_cast<char*>(result_family.data()); |
| 197 iov[1].iov_len = result_family.size(); |
| 198 } |
| 199 |
| 200 SendReplyAndClose(reply_fd, iov, r ? 2 : 1, -1 /* no fd */); |
| 201 } |
| 202 |
| 203 void FontConfigOpen(int reply_fd, const uint8_t* request_bytes, |
| 204 unsigned request_len) { |
| 205 if (request_len < sizeof(FontConfigIPC::OpenRequest)) |
| 206 return (void) HANDLE_EINTR(close(reply_fd)); |
| 207 |
| 208 const FontConfigIPC::OpenRequest* request = |
| 209 reinterpret_cast<const FontConfigIPC::OpenRequest*>(request_bytes); |
| 210 |
| 211 FontConfigDirect* fc = reinterpret_cast<FontConfigDirect*>(font_config_); |
| 212 |
| 213 const int result_fd = fc->Open(request->fileid); |
| 214 |
| 215 FontConfigIPC::OpenReply reply; |
| 216 reply.result = result_fd >= 0 ? 1 : 0; |
| 217 |
| 218 struct iovec iov; |
| 219 iov.iov_base = &reply; |
| 220 iov.iov_len = sizeof(reply); |
| 221 |
| 222 SendReplyAndClose(reply_fd, &iov, 1, result_fd); |
| 223 } |
| 224 |
| 225 private: |
| 226 const int lifeline_fd_; |
| 227 const int browser_socket_; |
| 228 FontConfigDirect* const font_config_; |
| 229 }; |
| 230 |
| 231 // ----------------------------------------------------------------------------- |
| 232 |
| 233 // Runs on the main thread at startup. |
| 234 RenderSandboxHostLinux::RenderSandboxHostLinux() { |
| 235 int fds[2]; |
| 236 CHECK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0); |
| 237 |
| 238 renderer_socket_ = fds[0]; |
| 239 const int browser_socket = fds[1]; |
| 240 |
| 241 int pipefds[2]; |
| 242 CHECK(0 == pipe(pipefds)); |
| 243 const int child_lifeline_fd = pipefds[0]; |
| 244 childs_lifeline_fd_ = pipefds[1]; |
| 245 |
| 246 const pid_t child = fork(); |
| 247 if (child == 0) { |
| 248 SandboxIPCProcess handler(child_lifeline_fd, browser_socket); |
| 249 handler.Run(); |
| 250 _exit(0); |
| 251 } |
| 252 } |
| 253 |
| 254 RenderSandboxHostLinux::~RenderSandboxHostLinux() { |
| 255 HANDLE_EINTR(close(renderer_socket_)); |
| 256 HANDLE_EINTR(close(childs_lifeline_fd_)); |
| 257 } |
OLD | NEW |