// Copyright 2014 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 "content/browser/sandbox_ipc_linux.h" #include #include #include #include #include #include #include #include "base/command_line.h" #include "base/files/scoped_file.h" #include "base/linux_util.h" #include "base/logging.h" #include "base/memory/platform_shared_memory_region.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/unix_domain_socket.h" #include "base/process/launch.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "content/public/common/content_switches.h" #include "sandbox/linux/services/libc_interceptor.h" #include "sandbox/policy/linux/sandbox_linux.h" namespace content { const size_t kMaxSandboxIPCMessagePayloadSize = 64; // static SandboxIPCHandler::SandboxIPCHandler(int lifeline_fd, int browser_socket) : lifeline_fd_(lifeline_fd), browser_socket_(browser_socket) {} void SandboxIPCHandler::Run() { struct pollfd pfds[2]; pfds[0].fd = lifeline_fd_; pfds[0].events = POLLIN; pfds[1].fd = browser_socket_; pfds[1].events = POLLIN; int failed_polls = 0; for (;;) { const int r = HANDLE_EINTR(poll(pfds, base::size(pfds), -1 /* no timeout */)); // '0' is not a possible return value with no timeout. DCHECK_NE(0, r); if (r < 0) { PLOG(WARNING) << "poll"; if (failed_polls++ == 3) { LOG(FATAL) << "poll(2) failing. SandboxIPCHandler aborting."; return; } continue; } failed_polls = 0; // The browser process will close the other end of this pipe on shutdown, // so we should exit. if (pfds[0].revents) { break; } // If poll(2) reports an error condition in this fd, // we assume the zygote is gone and we exit the loop. if (pfds[1].revents & (POLLERR | POLLHUP)) { break; } if (pfds[1].revents & POLLIN) { HandleRequestFromChild(browser_socket_); } } VLOG(1) << "SandboxIPCHandler stopping."; } void SandboxIPCHandler::HandleRequestFromChild(int fd) { std::vector fds; // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength // bytes long (this is the largest message type). // The size limit used to be FontConfigIPC::kMaxFontFamilyLength which was // 2048, but we do not receive FontConfig IPC here anymore. The only payloads // here are sandbox::policy::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT // and HandleLocalTime from libc_interceptor for which // kMaxSandboxIPCMessagePayloadSize set to 64 should be plenty. // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC // error for a maximum length message. char buf[kMaxSandboxIPCMessagePayloadSize + 128]; const ssize_t len = base::UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds); if (len == -1) { // TODO: should send an error reply, or the sender might block forever. if (errno == EMSGSIZE) { NOTREACHED() << "Sandbox host message is larger than " "kMaxSandboxIPCMessagePayloadSize"; } else { PLOG(ERROR) << "Recvmsg failed"; NOTREACHED(); } return; } if (fds.empty()) return; base::Pickle pickle(buf, len); base::PickleIterator iter(pickle); int kind; if (!iter.ReadInt(&kind)) return; // Give sandbox first shot at request, if it is not handled, then // false is returned and we continue on. if (sandbox::HandleInterceptedCall(kind, fd, iter, fds)) return; if (kind == sandbox::policy::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT) { HandleMakeSharedMemorySegment(fd, iter, fds); return; } NOTREACHED(); } void SandboxIPCHandler::HandleMakeSharedMemorySegment( int fd, base::PickleIterator iter, const std::vector& fds) { uint32_t size; if (!iter.ReadUInt32(&size)) return; // TODO(crbug.com/982879): executable shared memory should be removed when // NaCl is unshipped. bool executable; if (!iter.ReadBool(&executable)) return; base::ScopedFD shm_fd; if (executable) { shm_fd = base::subtle::PlatformSharedMemoryRegion::ExecutableRegion::CreateFD( size); } else { base::subtle::PlatformSharedMemoryRegion region = base::subtle::PlatformSharedMemoryRegion::CreateUnsafe(size); shm_fd = std::move(region.PassPlatformHandle().fd); } base::Pickle reply; SendRendererReply(fds, reply, shm_fd.get()); // shm_fd will close the handle which is no longer needed by this process. } void SandboxIPCHandler::SendRendererReply( const std::vector& fds, const base::Pickle& reply, int reply_fd) { struct msghdr msg; memset(&msg, 0, sizeof(msg)); struct iovec iov = {const_cast(reply.data()), reply.size()}; msg.msg_iov = &iov; msg.msg_iovlen = 1; char control_buffer[CMSG_SPACE(sizeof(reply_fd))]; if (reply_fd != -1) { struct stat st; if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) { LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC"; // We must never send directory descriptors to a sandboxed process // because they can use openat with ".." elements in the path in order // to escape the sandbox and reach the real filesystem. } 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(reply_fd)); memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd)); msg.msg_controllen = cmsg->cmsg_len; } if (HANDLE_EINTR(sendmsg(fds[0].get(), &msg, MSG_DONTWAIT)) < 0) PLOG(ERROR) << "sendmsg"; } SandboxIPCHandler::~SandboxIPCHandler() { if (IGNORE_EINTR(close(lifeline_fd_)) < 0) PLOG(ERROR) << "close"; if (IGNORE_EINTR(close(browser_socket_)) < 0) PLOG(ERROR) << "close"; } } // namespace content