• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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