1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : /*
8 : * NaCl Service Runtime. I/O Descriptor / Handle abstraction.
9 : * Connection capabilities.
10 : */
11 :
12 : #include <assert.h>
13 : #include <errno.h>
14 : #include <poll.h>
15 : #include <stdlib.h>
16 : #include <string.h>
17 : #include <sys/socket.h>
18 :
19 : #include "native_client/src/include/nacl_macros.h"
20 : #include "native_client/src/include/portability.h"
21 :
22 : #include "native_client/src/shared/imc/nacl_imc_c.h"
23 : #include "native_client/src/shared/platform/nacl_check.h"
24 : #include "native_client/src/shared/platform/nacl_log.h"
25 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
26 : #include "native_client/src/trusted/desc/nacl_desc_conn_cap.h"
27 : #include "native_client/src/trusted/desc/nacl_desc_imc.h"
28 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
29 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
30 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
31 :
32 : static struct NaClDescVtbl const kNaClDescConnCapFdVtbl; /* fwd */
33 :
34 639 : static int NaClDescConnCapFdSubclassCtor(struct NaClDescConnCapFd *self,
35 639 : NaClHandle endpt) {
36 639 : struct NaClDesc *basep = (struct NaClDesc *) self;
37 :
38 639 : self->connect_fd = endpt;
39 639 : basep->base.vtbl = (struct NaClRefCountVtbl const *) &kNaClDescConnCapFdVtbl;
40 639 : return 1;
41 : }
42 :
43 586 : int NaClDescConnCapFdCtor(struct NaClDescConnCapFd *self,
44 586 : NaClHandle endpt) {
45 586 : struct NaClDesc *basep = (struct NaClDesc *) self;
46 586 : int rv;
47 :
48 586 : basep->base.vtbl = (struct NaClRefCountVtbl const *) NULL;
49 586 : if (!NaClDescCtor(basep)) {
50 0 : return 0;
51 : }
52 586 : rv = NaClDescConnCapFdSubclassCtor(self, endpt);
53 586 : if (!rv) {
54 0 : (*NACL_VTBL(NaClRefCount, basep)->Dtor)((struct NaClRefCount *) self);
55 0 : }
56 586 : return rv;
57 586 : }
58 :
59 46 : static void NaClDescConnCapFdDtor(struct NaClRefCount *vself) {
60 46 : struct NaClDescConnCapFd *self = (struct NaClDescConnCapFd *) vself;
61 :
62 46 : (void) NaClClose(self->connect_fd);
63 46 : self->connect_fd = NACL_INVALID_HANDLE;
64 46 : vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl;
65 46 : (*vself->vtbl->Dtor)(vself);
66 46 : return;
67 : }
68 :
69 0 : static int NaClDescConnCapFdFstat(struct NaClDesc *vself,
70 0 : struct nacl_abi_stat *statbuf) {
71 0 : UNREFERENCED_PARAMETER(vself);
72 :
73 0 : memset(statbuf, 0, sizeof *statbuf);
74 0 : statbuf->nacl_abi_st_mode = NACL_ABI_S_IFSOCKADDR | NACL_ABI_S_IRWXU;
75 0 : return 0;
76 : }
77 :
78 30 : static int NaClDescConnCapFdExternalizeSize(struct NaClDesc *vself,
79 30 : size_t *nbytes,
80 30 : size_t *nhandles) {
81 30 : int rv;
82 :
83 30 : rv = NaClDescExternalizeSize(vself, nbytes, nhandles);
84 30 : if (0 != rv) {
85 0 : return rv;
86 : }
87 :
88 30 : *nhandles += 1;
89 30 : return 0;
90 30 : }
91 :
92 30 : static int NaClDescConnCapFdExternalize(struct NaClDesc *vself,
93 30 : struct NaClDescXferState *xfer) {
94 30 : struct NaClDescConnCapFd *self;
95 30 : int rv;
96 :
97 30 : rv = NaClDescExternalize(vself, xfer);
98 30 : if (0 != rv) {
99 0 : return rv;
100 : }
101 30 : self = (struct NaClDescConnCapFd *) vself;
102 30 : *xfer->next_handle++ = self->connect_fd;
103 :
104 30 : return 0;
105 30 : }
106 :
107 60 : static int NaClDescConnCapFdConnectAddr(struct NaClDesc *vself,
108 60 : struct NaClDesc **out_desc) {
109 60 : struct NaClDescConnCapFd *self = (struct NaClDescConnCapFd *) vself;
110 60 : NaClHandle sock_pair[2];
111 60 : struct NaClDescImcDesc *connected_socket;
112 60 : char control_buf[CMSG_SPACE_KHANDLE_COUNT_MAX_INTS];
113 60 : struct iovec iovec;
114 60 : struct msghdr connect_msg;
115 60 : struct cmsghdr *cmsg;
116 60 : int sent;
117 60 : int retval;
118 :
119 : assert(CMSG_SPACE(sizeof(int)) <= CMSG_SPACE_KHANDLE_COUNT_MAX_INTS);
120 :
121 60 : sock_pair[0] = NACL_INVALID_HANDLE;
122 60 : sock_pair[1] = NACL_INVALID_HANDLE;
123 60 : connected_socket = (struct NaClDescImcDesc *) NULL;
124 :
125 60 : retval = -NACL_ABI_EINVAL;
126 :
127 60 : if (0 != NaClSocketPair(sock_pair)) {
128 0 : retval = -NACL_ABI_EMFILE;
129 0 : goto cleanup;
130 : }
131 :
132 60 : iovec.iov_base = "c";
133 60 : iovec.iov_len = 1;
134 60 : connect_msg.msg_iov = &iovec;
135 60 : connect_msg.msg_iovlen = 1;
136 60 : connect_msg.msg_name = NULL;
137 60 : connect_msg.msg_namelen = 0;
138 60 : connect_msg.msg_control = control_buf;
139 60 : connect_msg.msg_controllen = sizeof(control_buf);
140 60 : connect_msg.msg_flags = 0;
141 :
142 180 : cmsg = CMSG_FIRSTHDR(&connect_msg);
143 60 : cmsg->cmsg_len = CMSG_LEN(sizeof(int));
144 60 : cmsg->cmsg_level = SOL_SOCKET;
145 60 : cmsg->cmsg_type = SCM_RIGHTS;
146 : /*
147 : * We use memcpy() rather than assignment through a cast to avoid
148 : * strict-aliasing warnings
149 : */
150 180 : memcpy(CMSG_DATA(cmsg), &sock_pair[0], sizeof(int));
151 : /* Set msg_controllen to the actual size of the cmsg. */
152 60 : connect_msg.msg_controllen = cmsg->cmsg_len;
153 :
154 60 : sent = sendmsg(self->connect_fd, &connect_msg, 0);
155 60 : if (1 != sent) {
156 1 : retval = -NACL_ABI_EIO;
157 1 : goto cleanup;
158 : }
159 :
160 : if (NACL_OSX) {
161 : /*
162 : * Mac OS X has a kernel bug in which a socket descriptor that is
163 : * referenced only from the message queue of another socket can
164 : * get garbage collected. This causes the socket descriptor not
165 : * to work properly. To work around this, we don't close our
166 : * reference to the socket until we receive an acknowledgement
167 : * that it has been successfully received.
168 : *
169 : * We cannot receive the acknowledgement through self->connect_fd
170 : * because this FD could be shared between multiple processes. So
171 : * we receive the acknowledgement through the socket pair that we
172 : * have just created.
173 : *
174 : * However, this creates a risk that we are left hanging if the
175 : * other process dies after our sendmsg() call, because we are
176 : * holding on to the socket that it would use to send the ack. To
177 : * avoid this problem, we use poll() so that we will be notified
178 : * if self->connect_fd becomes unwritable.
179 : * TODO(mseaborn): Add a test case to simulate that scenario.
180 : *
181 : * See http://code.google.com/p/nativeclient/issues/detail?id=1796
182 : *
183 : * Note that we are relying on a corner case of poll() here.
184 : * Using POLLHUP in "events" is not meaningful on Linux, which is
185 : * documented as ignoring POLLHUP as an input argument and will
186 : * return POLLHUP in "revents" even if it not present in "events".
187 : * On Mac OS X, however, passing events == 0 does not work if we
188 : * want to get POLLHUP. We are in the unusual situation of
189 : * waiting for a socket to become *un*writable.
190 : */
191 55 : struct pollfd poll_fds[2];
192 55 : poll_fds[0].fd = self->connect_fd;
193 55 : poll_fds[0].events = POLLHUP;
194 55 : poll_fds[1].fd = sock_pair[1];
195 55 : poll_fds[1].events = POLLIN;
196 55 : if (poll(poll_fds, 2, -1) < 0) {
197 0 : NaClLog(LOG_ERROR,
198 0 : "NaClDescConnCapFdConnectAddr: poll() failed, errno %d\n", errno);
199 0 : retval = -NACL_ABI_EIO;
200 0 : goto cleanup;
201 : }
202 : /*
203 : * It is not necessarily an error if POLLHUP fires on
204 : * self->connect_fd: The other process could have done
205 : * imc_accept(S) and then closed S. This means it will have
206 : * received sock_pair[0] successfully, so we can close our
207 : * reference to sock_pair[0] and then receive the ack.
208 : * TODO(mseaborn): Add a test case to cover this scenario.
209 : */
210 : }
211 :
212 55 : (void) NaClClose(sock_pair[0]);
213 55 : sock_pair[0] = NACL_INVALID_HANDLE;
214 :
215 : if (NACL_OSX) {
216 : /* Receive the acknowledgement. We do not expect this to block. */
217 55 : char ack_buffer[1];
218 55 : ssize_t received = recv(sock_pair[1], ack_buffer, sizeof(ack_buffer), 0);
219 110 : if (received != 1 || ack_buffer[0] != 'a') {
220 0 : retval = -NACL_ABI_EIO;
221 0 : goto cleanup;
222 : }
223 : }
224 :
225 55 : connected_socket = malloc(sizeof(*connected_socket));
226 55 : if (NULL == connected_socket ||
227 55 : !NaClDescImcDescCtor(connected_socket, sock_pair[1])) {
228 0 : retval = -NACL_ABI_ENOMEM;
229 0 : goto cleanup;
230 : }
231 55 : sock_pair[1] = NACL_INVALID_HANDLE;
232 :
233 55 : *out_desc = (struct NaClDesc *) connected_socket;
234 55 : connected_socket = NULL;
235 55 : retval = 0;
236 :
237 : cleanup:
238 56 : NaClSafeCloseNaClHandle(sock_pair[0]);
239 56 : NaClSafeCloseNaClHandle(sock_pair[1]);
240 56 : free(connected_socket);
241 :
242 56 : return retval;
243 : }
244 :
245 0 : static int NaClDescConnCapFdAcceptConn(struct NaClDesc *vself,
246 0 : struct NaClDesc **out_desc) {
247 0 : UNREFERENCED_PARAMETER(vself);
248 0 : UNREFERENCED_PARAMETER(out_desc);
249 :
250 0 : NaClLog(LOG_ERROR, "NaClDescConnCapFdAcceptConn: not IMC\n");
251 0 : return -NACL_ABI_EINVAL;
252 : }
253 :
254 : static struct NaClDescVtbl const kNaClDescConnCapFdVtbl = {
255 : {
256 : NaClDescConnCapFdDtor,
257 : },
258 : NaClDescMapNotImplemented,
259 : NACL_DESC_UNMAP_NOT_IMPLEMENTED
260 : NaClDescReadNotImplemented,
261 : NaClDescWriteNotImplemented,
262 : NaClDescSeekNotImplemented,
263 : NaClDescPReadNotImplemented,
264 : NaClDescPWriteNotImplemented,
265 : NaClDescConnCapFdFstat,
266 : NaClDescGetdentsNotImplemented,
267 : NaClDescConnCapFdExternalizeSize,
268 : NaClDescConnCapFdExternalize,
269 : NaClDescLockNotImplemented,
270 : NaClDescTryLockNotImplemented,
271 : NaClDescUnlockNotImplemented,
272 : NaClDescWaitNotImplemented,
273 : NaClDescTimedWaitAbsNotImplemented,
274 : NaClDescSignalNotImplemented,
275 : NaClDescBroadcastNotImplemented,
276 : NaClDescSendMsgNotImplemented,
277 : NaClDescRecvMsgNotImplemented,
278 : NaClDescLowLevelSendMsgNotImplemented,
279 : NaClDescLowLevelRecvMsgNotImplemented,
280 : NaClDescConnCapFdConnectAddr,
281 : NaClDescConnCapFdAcceptConn,
282 : NaClDescPostNotImplemented,
283 : NaClDescSemWaitNotImplemented,
284 : NaClDescGetValueNotImplemented,
285 : NaClDescSetMetadata,
286 : NaClDescGetMetadata,
287 : NaClDescSetFlags,
288 : NaClDescGetFlags,
289 : NaClDescIsattyNotImplemented,
290 : NACL_DESC_CONN_CAP_FD,
291 : };
292 :
293 : int NaClDescConnCapFdInternalize(
294 53 : struct NaClDesc **out_desc,
295 53 : struct NaClDescXferState *xfer,
296 53 : struct NaClDescQuotaInterface *quota_interface) {
297 53 : struct NaClDescConnCapFd *conn_cap;
298 53 : int rv;
299 :
300 106 : UNREFERENCED_PARAMETER(quota_interface);
301 53 : conn_cap = malloc(sizeof(*conn_cap));
302 53 : if (NULL == conn_cap) {
303 0 : return -NACL_ABI_ENOMEM;
304 : }
305 53 : if (!NaClDescInternalizeCtor(&conn_cap->base, xfer)) {
306 0 : free(conn_cap);
307 0 : conn_cap = NULL;
308 0 : rv = -NACL_ABI_ENOMEM;
309 0 : goto cleanup;
310 : }
311 53 : if (xfer->next_handle == xfer->handle_buffer_end) {
312 0 : rv = -NACL_ABI_EIO;
313 0 : goto cleanup;
314 : }
315 53 : rv = NaClDescConnCapFdSubclassCtor(conn_cap, *xfer->next_handle);
316 53 : if (!rv) {
317 0 : rv = -NACL_ABI_ENOMEM;
318 0 : goto cleanup;
319 : }
320 53 : *xfer->next_handle++ = NACL_INVALID_HANDLE;
321 53 : *out_desc = &conn_cap->base;
322 53 : rv = 0;
323 : cleanup:
324 53 : if (rv < 0) {
325 0 : NaClDescSafeUnref((struct NaClDesc *) conn_cap);
326 0 : }
327 53 : return rv;
328 53 : }
|