1 // Copyright (c) 2011 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 "base/posix/unix_domain_socket_linux.h" 6 7 #include <errno.h> 8 #include <sys/socket.h> 9 #include <sys/uio.h> 10 #include <unistd.h> 11 12 #include <vector> 13 14 #include "base/files/scoped_file.h" 15 #include "base/logging.h" 16 #include "base/memory/scoped_vector.h" 17 #include "base/pickle.h" 18 #include "base/posix/eintr_wrapper.h" 19 #include "base/stl_util.h" 20 21 const size_t UnixDomainSocket::kMaxFileDescriptors = 16; 22 23 // Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes 24 // ownership of the newly allocated file descriptors to |one| and |two|. 25 // Returns true on success. 26 static bool CreateSocketPair(base::ScopedFD* one, base::ScopedFD* two) { 27 int raw_socks[2]; 28 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks) == -1) 29 return false; 30 one->reset(raw_socks[0]); 31 two->reset(raw_socks[1]); 32 return true; 33 } 34 35 // static 36 bool UnixDomainSocket::EnableReceiveProcessId(int fd) { 37 const int enable = 1; 38 return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0; 39 } 40 41 // static 42 bool UnixDomainSocket::SendMsg(int fd, 43 const void* buf, 44 size_t length, 45 const std::vector<int>& fds) { 46 struct msghdr msg = {}; 47 struct iovec iov = { const_cast<void*>(buf), length }; 48 msg.msg_iov = &iov; 49 msg.msg_iovlen = 1; 50 51 char* control_buffer = NULL; 52 if (fds.size()) { 53 const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size()); 54 control_buffer = new char[control_len]; 55 56 struct cmsghdr* cmsg; 57 msg.msg_control = control_buffer; 58 msg.msg_controllen = control_len; 59 cmsg = CMSG_FIRSTHDR(&msg); 60 cmsg->cmsg_level = SOL_SOCKET; 61 cmsg->cmsg_type = SCM_RIGHTS; 62 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size()); 63 memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size()); 64 msg.msg_controllen = cmsg->cmsg_len; 65 } 66 67 // Avoid a SIGPIPE if the other end breaks the connection. 68 // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't 69 // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by 70 // POSIX. 71 const int flags = MSG_NOSIGNAL; 72 const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags)); 73 const bool ret = static_cast<ssize_t>(length) == r; 74 delete[] control_buffer; 75 return ret; 76 } 77 78 // static 79 ssize_t UnixDomainSocket::RecvMsg(int fd, 80 void* buf, 81 size_t length, 82 ScopedVector<base::ScopedFD>* fds) { 83 return UnixDomainSocket::RecvMsgWithPid(fd, buf, length, fds, NULL); 84 } 85 86 // static 87 ssize_t UnixDomainSocket::RecvMsgWithPid(int fd, 88 void* buf, 89 size_t length, 90 ScopedVector<base::ScopedFD>* fds, 91 base::ProcessId* pid) { 92 return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds, pid); 93 } 94 95 // static 96 ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, 97 void* buf, 98 size_t length, 99 int flags, 100 ScopedVector<base::ScopedFD>* fds, 101 base::ProcessId* out_pid) { 102 fds->clear(); 103 104 struct msghdr msg = {}; 105 struct iovec iov = { buf, length }; 106 msg.msg_iov = &iov; 107 msg.msg_iovlen = 1; 108 109 char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors) + 110 CMSG_SPACE(sizeof(struct ucred))]; 111 msg.msg_control = control_buffer; 112 msg.msg_controllen = sizeof(control_buffer); 113 114 const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags)); 115 if (r == -1) 116 return -1; 117 118 int* wire_fds = NULL; 119 unsigned wire_fds_len = 0; 120 base::ProcessId pid = -1; 121 122 if (msg.msg_controllen > 0) { 123 struct cmsghdr* cmsg; 124 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 125 const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); 126 if (cmsg->cmsg_level == SOL_SOCKET && 127 cmsg->cmsg_type == SCM_RIGHTS) { 128 DCHECK(payload_len % sizeof(int) == 0); 129 DCHECK(wire_fds == NULL); 130 wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); 131 wire_fds_len = payload_len / sizeof(int); 132 } 133 if (cmsg->cmsg_level == SOL_SOCKET && 134 cmsg->cmsg_type == SCM_CREDENTIALS) { 135 DCHECK(payload_len == sizeof(struct ucred)); 136 DCHECK(pid == -1); 137 pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid; 138 } 139 } 140 } 141 142 if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) { 143 for (unsigned i = 0; i < wire_fds_len; ++i) 144 close(wire_fds[i]); 145 errno = EMSGSIZE; 146 return -1; 147 } 148 149 if (wire_fds) { 150 for (unsigned i = 0; i < wire_fds_len; ++i) 151 fds->push_back(new base::ScopedFD(wire_fds[i])); 152 } 153 154 if (out_pid) { 155 // |pid| will legitimately be -1 if we read EOF, so only DCHECK if we 156 // actually received a message. Unfortunately, Linux allows sending zero 157 // length messages, which are indistinguishable from EOF, so this check 158 // has false negatives. 159 if (r > 0 || msg.msg_controllen > 0) 160 DCHECK_GE(pid, 0); 161 162 *out_pid = pid; 163 } 164 165 return r; 166 } 167 168 // static 169 ssize_t UnixDomainSocket::SendRecvMsg(int fd, 170 uint8_t* reply, 171 unsigned max_reply_len, 172 int* result_fd, 173 const Pickle& request) { 174 return UnixDomainSocket::SendRecvMsgWithFlags(fd, reply, max_reply_len, 175 0, /* recvmsg_flags */ 176 result_fd, request); 177 } 178 179 // static 180 ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd, 181 uint8_t* reply, 182 unsigned max_reply_len, 183 int recvmsg_flags, 184 int* result_fd, 185 const Pickle& request) { 186 // This socketpair is only used for the IPC and is cleaned up before 187 // returning. 188 base::ScopedFD recv_sock, send_sock; 189 if (!CreateSocketPair(&recv_sock, &send_sock)) 190 return -1; 191 192 { 193 std::vector<int> send_fds; 194 send_fds.push_back(send_sock.get()); 195 if (!SendMsg(fd, request.data(), request.size(), send_fds)) 196 return -1; 197 } 198 199 // Close the sending end of the socket right away so that if our peer closes 200 // it before sending a response (e.g., from exiting), RecvMsgWithFlags() will 201 // return EOF instead of hanging. 202 send_sock.reset(); 203 204 ScopedVector<base::ScopedFD> recv_fds; 205 // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the 206 // sender might get a SIGPIPE. 207 const ssize_t reply_len = RecvMsgWithFlags( 208 recv_sock.get(), reply, max_reply_len, recvmsg_flags, &recv_fds, NULL); 209 recv_sock.reset(); 210 if (reply_len == -1) 211 return -1; 212 213 // If we received more file descriptors than caller expected, then we treat 214 // that as an error. 215 if (recv_fds.size() > (result_fd != NULL ? 1 : 0)) { 216 NOTREACHED(); 217 return -1; 218 } 219 220 if (result_fd) 221 *result_fd = recv_fds.empty() ? -1 : recv_fds[0]->release(); 222 223 return reply_len; 224 } 225