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