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