1 // Copyright 2014 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 "content/browser/sandbox_ipc_linux.h"
6
7 #include <fcntl.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <string.h>
11 #include <sys/poll.h>
12 #include <sys/socket.h>
13 #include <sys/stat.h>
14
15 #include "base/command_line.h"
16 #include "base/files/scoped_file.h"
17 #include "base/linux_util.h"
18 #include "base/logging.h"
19 #include "base/memory/platform_shared_memory_region.h"
20 #include "base/posix/eintr_wrapper.h"
21 #include "base/posix/unix_domain_socket.h"
22 #include "base/process/launch.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "content/public/common/content_switches.h"
26 #include "sandbox/linux/services/libc_interceptor.h"
27 #include "sandbox/policy/linux/sandbox_linux.h"
28
29 namespace content {
30
31 const size_t kMaxSandboxIPCMessagePayloadSize = 64;
32
33 // static
SandboxIPCHandler(int lifeline_fd,int browser_socket)34 SandboxIPCHandler::SandboxIPCHandler(int lifeline_fd, int browser_socket)
35 : lifeline_fd_(lifeline_fd), browser_socket_(browser_socket) {}
36
Run()37 void SandboxIPCHandler::Run() {
38 struct pollfd pfds[2];
39 pfds[0].fd = lifeline_fd_;
40 pfds[0].events = POLLIN;
41 pfds[1].fd = browser_socket_;
42 pfds[1].events = POLLIN;
43
44 int failed_polls = 0;
45 for (;;) {
46 const int r =
47 HANDLE_EINTR(poll(pfds, base::size(pfds), -1 /* no timeout */));
48 // '0' is not a possible return value with no timeout.
49 DCHECK_NE(0, r);
50 if (r < 0) {
51 PLOG(WARNING) << "poll";
52 if (failed_polls++ == 3) {
53 LOG(FATAL) << "poll(2) failing. SandboxIPCHandler aborting.";
54 return;
55 }
56 continue;
57 }
58
59 failed_polls = 0;
60
61 // The browser process will close the other end of this pipe on shutdown,
62 // so we should exit.
63 if (pfds[0].revents) {
64 break;
65 }
66
67 // If poll(2) reports an error condition in this fd,
68 // we assume the zygote is gone and we exit the loop.
69 if (pfds[1].revents & (POLLERR | POLLHUP)) {
70 break;
71 }
72
73 if (pfds[1].revents & POLLIN) {
74 HandleRequestFromChild(browser_socket_);
75 }
76 }
77
78 VLOG(1) << "SandboxIPCHandler stopping.";
79 }
80
HandleRequestFromChild(int fd)81 void SandboxIPCHandler::HandleRequestFromChild(int fd) {
82 std::vector<base::ScopedFD> fds;
83
84 // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength
85 // bytes long (this is the largest message type).
86 // The size limit used to be FontConfigIPC::kMaxFontFamilyLength which was
87 // 2048, but we do not receive FontConfig IPC here anymore. The only payloads
88 // here are sandbox::policy::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT
89 // and HandleLocalTime from libc_interceptor for which
90 // kMaxSandboxIPCMessagePayloadSize set to 64 should be plenty.
91 // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC
92 // error for a maximum length message.
93 char buf[kMaxSandboxIPCMessagePayloadSize + 128];
94
95 const ssize_t len =
96 base::UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
97 if (len == -1) {
98 // TODO: should send an error reply, or the sender might block forever.
99 if (errno == EMSGSIZE) {
100 NOTREACHED() << "Sandbox host message is larger than "
101 "kMaxSandboxIPCMessagePayloadSize";
102 } else {
103 PLOG(ERROR) << "Recvmsg failed";
104 NOTREACHED();
105 }
106 return;
107 }
108 if (fds.empty())
109 return;
110
111 base::Pickle pickle(buf, len);
112 base::PickleIterator iter(pickle);
113
114 int kind;
115 if (!iter.ReadInt(&kind))
116 return;
117
118 // Give sandbox first shot at request, if it is not handled, then
119 // false is returned and we continue on.
120 if (sandbox::HandleInterceptedCall(kind, fd, iter, fds))
121 return;
122
123 if (kind ==
124 sandbox::policy::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT) {
125 HandleMakeSharedMemorySegment(fd, iter, fds);
126 return;
127 }
128 NOTREACHED();
129 }
130
HandleMakeSharedMemorySegment(int fd,base::PickleIterator iter,const std::vector<base::ScopedFD> & fds)131 void SandboxIPCHandler::HandleMakeSharedMemorySegment(
132 int fd,
133 base::PickleIterator iter,
134 const std::vector<base::ScopedFD>& fds) {
135 uint32_t size;
136 if (!iter.ReadUInt32(&size))
137 return;
138 // TODO(crbug.com/982879): executable shared memory should be removed when
139 // NaCl is unshipped.
140 bool executable;
141 if (!iter.ReadBool(&executable))
142 return;
143 base::ScopedFD shm_fd;
144 if (executable) {
145 shm_fd =
146 base::subtle::PlatformSharedMemoryRegion::ExecutableRegion::CreateFD(
147 size);
148 } else {
149 base::subtle::PlatformSharedMemoryRegion region =
150 base::subtle::PlatformSharedMemoryRegion::CreateUnsafe(size);
151 shm_fd = std::move(region.PassPlatformHandle().fd);
152 }
153 base::Pickle reply;
154 SendRendererReply(fds, reply, shm_fd.get());
155 // shm_fd will close the handle which is no longer needed by this process.
156 }
157
SendRendererReply(const std::vector<base::ScopedFD> & fds,const base::Pickle & reply,int reply_fd)158 void SandboxIPCHandler::SendRendererReply(
159 const std::vector<base::ScopedFD>& fds,
160 const base::Pickle& reply,
161 int reply_fd) {
162 struct msghdr msg;
163 memset(&msg, 0, sizeof(msg));
164 struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
165 msg.msg_iov = &iov;
166 msg.msg_iovlen = 1;
167
168 char control_buffer[CMSG_SPACE(sizeof(reply_fd))];
169
170 if (reply_fd != -1) {
171 struct stat st;
172 if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) {
173 LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC";
174 // We must never send directory descriptors to a sandboxed process
175 // because they can use openat with ".." elements in the path in order
176 // to escape the sandbox and reach the real filesystem.
177 }
178
179 struct cmsghdr* cmsg;
180 msg.msg_control = control_buffer;
181 msg.msg_controllen = sizeof(control_buffer);
182 cmsg = CMSG_FIRSTHDR(&msg);
183 cmsg->cmsg_level = SOL_SOCKET;
184 cmsg->cmsg_type = SCM_RIGHTS;
185 cmsg->cmsg_len = CMSG_LEN(sizeof(reply_fd));
186 memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd));
187 msg.msg_controllen = cmsg->cmsg_len;
188 }
189
190 if (HANDLE_EINTR(sendmsg(fds[0].get(), &msg, MSG_DONTWAIT)) < 0)
191 PLOG(ERROR) << "sendmsg";
192 }
193
~SandboxIPCHandler()194 SandboxIPCHandler::~SandboxIPCHandler() {
195 if (IGNORE_EINTR(close(lifeline_fd_)) < 0)
196 PLOG(ERROR) << "close";
197 if (IGNORE_EINTR(close(browser_socket_)) < 0)
198 PLOG(ERROR) << "close";
199 }
200
201 } // namespace content
202