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