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 : /* NaCl inter-module communication primitives. */
8 :
9 : #include "native_client/src/shared/imc/nacl_imc_c.h"
10 : #include <assert.h>
11 : #include <ctype.h>
12 : #include <errno.h>
13 : #include <fcntl.h>
14 : #include <limits.h>
15 : #include <poll.h>
16 : #include <stdio.h>
17 : #include <string.h>
18 : #include <unistd.h>
19 : #include <sys/mman.h>
20 : #include <sys/socket.h>
21 : #include <sys/types.h>
22 : #include <sys/un.h>
23 :
24 : #include "native_client/src/public/imc_types.h"
25 : #include "native_client/src/shared/platform/nacl_log.h"
26 :
27 :
28 : /*
29 : * TODO(bsy,bradnelson): remove SIGPIPE_FIX. It is needed for future
30 : * testing because our test framework appears to not see the SIGPIPE
31 : * on OSX when the fix is not in place. We've tracked it down to the
32 : * Python subprocess module, where if we manually run
33 : * subprocess.Popen('...path-to-sigpipe_test...') the SIGPIPE doesn't
34 : * actually occur(!); however, when running the same sigpipe_test
35 : * executable from the shell it's apparent that the SIGPIPE *does*
36 : * occur. Presumably it's some weird code path in subprocess that is
37 : * leaving the signal handler for SIGPIPE as SIG_IGN rather than
38 : * SIG_DFL. Unfortunately, we could not create a simpler test of our
39 : * test infrastructure (writing to a pipe that's closed) -- perhaps
40 : * the multithreaded nature of sigpipe_test is involved.
41 : *
42 : * In production code, SIGPIPE_FIX should be 1. The old behavior is
43 : * only needed to help us track down the problem in python.
44 : */
45 : #define SIGPIPE_FIX 1
46 :
47 : /*
48 : * The code guarded by SIGPIPE_FIX has been found to still raise
49 : * SIGPIPE in certain situations. Until we can boil this down to a
50 : * small test case and, possibly, file a bug against the OS, we need
51 : * to forcibly suppress these signals.
52 : */
53 : #define SIGPIPE_ALT_FIX 1
54 :
55 : #if SIGPIPE_ALT_FIX
56 : # include <signal.h>
57 : #endif /* SIGPIPE_ALT_FIX */
58 :
59 : #include <algorithm>
60 :
61 :
62 : /*
63 : * The number of recvmsg retries to perform to determine --
64 : * heuristically, unfortunately -- if the remote end of the socketpair
65 : * had actually closed. This is a (new) hacky workaround for an OSX
66 : * blemish that replaces the older, buggier workaround.
67 : */
68 : static const int kRecvMsgRetries = 8;
69 :
70 : /*
71 : * The maximum number of NaClIOVec elements sent by SendDatagram(). Plus one for
72 : * NaClInternalHeader with the descriptor data bytes.
73 : */
74 : static const size_t kIovLengthMax = NACL_ABI_IMC_IOVEC_MAX + 1;
75 :
76 : /*
77 : * The IMC datagram header followed by a message_bytes of data sent over the
78 : * a stream-oriented socket. We need to use stream-oriented socket for OS X
79 : * since it doesn't support file descriptor transfer over SOCK_DGRAM socket
80 : * like Linux.
81 : */
82 : struct Header {
83 : /*
84 : * The total bytes of data in the IMC datagram excluding the size of
85 : * Header.
86 : */
87 : size_t message_bytes;
88 : /* The total number of handles to be transferred with IMC datagram. */
89 : size_t handle_count;
90 : };
91 :
92 :
93 : /*
94 : * Gets an array of file descriptors stored in msg.
95 : * The fdv parameter must be an int array of kHandleCountMax elements.
96 : * GetRights() returns the number of file descriptors copied into fdv.
97 : */
98 19077 : static size_t GetRights(struct msghdr* msg, int* fdv) {
99 19077 : struct cmsghdr* cmsg;
100 19077 : size_t count = 0;
101 19077 : if (msg->msg_controllen == 0) {
102 18812 : return 0;
103 : }
104 1325 : for (cmsg = CMSG_FIRSTHDR(msg);
105 : cmsg != 0;
106 1325 : cmsg = CMSG_NXTHDR(msg, cmsg)) {
107 530 : if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
108 836 : while (CMSG_LEN((1 + count) * sizeof(int)) <= cmsg->cmsg_len) {
109 306 : *fdv++ = ((int *) CMSG_DATA(cmsg))[count];
110 306 : ++count;
111 306 : }
112 265 : }
113 265 : }
114 265 : return count;
115 19077 : }
116 :
117 : /*
118 : * Skips the specified length of octets when reading from a handle. Skipped
119 : * octets are discarded.
120 : * On success, true is returned. On error, false is returned.
121 : */
122 0 : static bool SkipFile(int handle, size_t length) {
123 0 : while (0 < length) {
124 0 : char scratch[1024];
125 0 : size_t count = std::min(sizeof scratch, length);
126 0 : count = read(handle, scratch, count);
127 0 : if ((ssize_t) count == -1 || count == 0) {
128 0 : return false;
129 : }
130 0 : length -= count;
131 0 : }
132 0 : return true;
133 0 : }
134 :
135 : #if SIGPIPE_ALT_FIX
136 : /*
137 : * TODO(kbr): move this to an Init() function so it isn't called all
138 : * the time.
139 : */
140 : static bool IgnoreSIGPIPE() {
141 1075 : struct sigaction sa;
142 1075 : sigset_t mask;
143 1075 : sigemptyset(&mask);
144 1075 : sa.sa_handler = SIG_IGN;
145 1075 : sa.sa_mask = mask;
146 1075 : sa.sa_flags = 0;
147 1075 : return sigaction(SIGPIPE, &sa, NULL) == 0;
148 : }
149 : #endif
150 :
151 : /*
152 : * We keep these no-op implementations of SocketAddress-based
153 : * functions so that sigpipe_test continues to link.
154 : */
155 0 : NaClHandle NaClBoundSocket(const NaClSocketAddress* address) {
156 0 : UNREFERENCED_PARAMETER(address);
157 0 : NaClLog(LOG_FATAL, "BoundSocket(): Not used on OSX\n");
158 0 : return -1;
159 : }
160 :
161 0 : int NaClSendDatagramTo(const NaClMessageHeader* message, int flags,
162 0 : const NaClSocketAddress* name) {
163 0 : UNREFERENCED_PARAMETER(message);
164 0 : UNREFERENCED_PARAMETER(flags);
165 0 : UNREFERENCED_PARAMETER(name);
166 0 : NaClLog(LOG_FATAL, "SendDatagramTo(): Not used on OSX\n");
167 0 : return -1;
168 : }
169 :
170 1075 : int NaClSocketPair(NaClHandle pair[2]) {
171 1075 : int result = socketpair(AF_UNIX, SOCK_STREAM, 0, pair);
172 1075 : if (result == 0) {
173 : #if SIGPIPE_FIX
174 1075 : int nosigpipe = 1;
175 : #endif
176 : #if SIGPIPE_ALT_FIX
177 1075 : if (!IgnoreSIGPIPE()) {
178 0 : close(pair[0]);
179 0 : close(pair[1]);
180 0 : return -1;
181 : }
182 : #endif
183 : #if SIGPIPE_FIX
184 1075 : if (0 != setsockopt(pair[0], SOL_SOCKET, SO_NOSIGPIPE,
185 : &nosigpipe, sizeof nosigpipe) ||
186 1075 : 0 != setsockopt(pair[1], SOL_SOCKET, SO_NOSIGPIPE,
187 : &nosigpipe, sizeof nosigpipe)) {
188 0 : close(pair[0]);
189 0 : close(pair[1]);
190 0 : return -1;
191 : }
192 : #endif
193 1075 : }
194 1075 : return result;
195 1075 : }
196 :
197 1235 : int NaClClose(NaClHandle handle) {
198 1235 : return close(handle);
199 : }
200 :
201 19067 : int NaClSendDatagram(NaClHandle handle, const NaClMessageHeader* message,
202 19067 : int flags) {
203 19067 : struct msghdr msg;
204 19067 : struct iovec vec[kIovLengthMax + 1];
205 19067 : unsigned char buf[CMSG_SPACE_KHANDLE_COUNT_MAX_INTS];
206 19067 : Header header = { 0, 0 };
207 19067 : int result;
208 19067 : size_t i;
209 38134 : UNREFERENCED_PARAMETER(flags);
210 :
211 : assert(CMSG_SPACE(NACL_HANDLE_COUNT_MAX * sizeof(int))
212 : <= CMSG_SPACE_KHANDLE_COUNT_MAX_INTS);
213 :
214 : /*
215 : * The following assert was an earlier attempt to remember/check the
216 : * assumption that our struct NaClIOVec -- which we must define to be
217 : * cross platform -- is compatible with struct iovec on *x systems.
218 : * The length field of NaClIOVec was switched to be uint32_t at oen point
219 : * to use concrete types, which introduced a problem on 64-bit systems.
220 : *
221 : * Clearly, the assert does not check a strong-enough condition,
222 : * since structure padding would make the two sizes the same.
223 : *
224 : assert(sizeof(struct iovec) == sizeof(NaClIOVec));
225 : *
226 : * Don't do this again!
227 : */
228 :
229 19067 : if (!NaClMessageSizeIsValid(message)) {
230 0 : errno = EMSGSIZE;
231 0 : return -1;
232 : }
233 :
234 38134 : if (NACL_HANDLE_COUNT_MAX < message->handle_count ||
235 : kIovLengthMax < message->iov_length) {
236 0 : errno = EMSGSIZE;
237 0 : return -1;
238 : }
239 :
240 19067 : memmove(&vec[1], message->iov, sizeof(NaClIOVec) * message->iov_length);
241 :
242 19067 : msg.msg_name = 0;
243 19067 : msg.msg_namelen = 0;
244 19067 : msg.msg_iov = vec;
245 19067 : msg.msg_iovlen = 1 + message->iov_length;
246 19315 : if (0 < message->handle_count && message->handles != NULL) {
247 248 : struct cmsghdr *cmsg;
248 248 : int size = message->handle_count * sizeof(int);
249 248 : msg.msg_control = buf;
250 248 : msg.msg_controllen = CMSG_SPACE(size);
251 744 : cmsg = CMSG_FIRSTHDR(&msg);
252 248 : cmsg->cmsg_level = SOL_SOCKET;
253 248 : cmsg->cmsg_type = SCM_RIGHTS;
254 248 : cmsg->cmsg_len = CMSG_LEN(size);
255 248 : memcpy(CMSG_DATA(cmsg), message->handles, size);
256 248 : msg.msg_controllen = cmsg->cmsg_len;
257 248 : header.handle_count = message->handle_count;
258 248 : } else {
259 18819 : msg.msg_control = 0;
260 18819 : msg.msg_controllen = 0;
261 : }
262 19067 : msg.msg_flags = 0;
263 :
264 : /*
265 : * Send data with the header atomically. Note to send file descriptors we need
266 : * to send at least one byte of data.
267 : */
268 157266 : for (i = 0; i < message->iov_length; ++i) {
269 59566 : header.message_bytes += message->iov[i].length;
270 59566 : }
271 19067 : vec[0].iov_base = &header;
272 19067 : vec[0].iov_len = sizeof header;
273 19067 : result = sendmsg(handle, &msg, 0);
274 19067 : if (result == -1) {
275 1 : return -1;
276 : }
277 19066 : if ((size_t) result < sizeof header) {
278 0 : errno = EMSGSIZE;
279 0 : return -1;
280 : }
281 19066 : return result - sizeof header;
282 19067 : }
283 :
284 19109 : int NaClReceiveDatagram(NaClHandle handle, NaClMessageHeader* message,
285 19109 : int flags) {
286 19109 : struct msghdr msg;
287 19109 : struct iovec vec[kIovLengthMax];
288 19109 : unsigned char buf[CMSG_SPACE_KHANDLE_COUNT_MAX_INTS];
289 19109 : struct Header header;
290 19109 : struct iovec header_vec = { &header, sizeof header };
291 19109 : int count;
292 19109 : int retry_count;
293 19109 : size_t handle_count = 0;
294 19109 : size_t buffer_bytes = 0;
295 19109 : size_t i;
296 :
297 : assert(CMSG_SPACE(NACL_HANDLE_COUNT_MAX * sizeof(int))
298 : <= CMSG_SPACE_KHANDLE_COUNT_MAX_INTS);
299 :
300 38218 : if (NACL_HANDLE_COUNT_MAX < message->handle_count ||
301 : kIovLengthMax < message->iov_length) {
302 0 : errno = EMSGSIZE;
303 0 : return -1;
304 : }
305 :
306 : /*
307 : * The following assert was an earlier attempt to remember/check the
308 : * assumption that our struct NaClIOVec -- which we must define to be
309 : * cross platform -- is compatible with struct iovec on *x systems.
310 : * The length field of NaClIOVec was switched to be uint32_t at oen point
311 : * to use concrete types, which introduced a problem on 64-bit systems.
312 : *
313 : * Clearly, the assert does not check a strong-enough condition,
314 : * since structure padding would make the two sizes the same.
315 : *
316 : assert(sizeof(struct iovec) == sizeof(NaClIOVec));
317 : *
318 : * Don't do this again!
319 : */
320 :
321 19109 : if (!NaClMessageSizeIsValid(message)) {
322 0 : errno = EMSGSIZE;
323 0 : return -1;
324 : }
325 :
326 19109 : message->flags = 0;
327 : /* Receive the header of the message and handles first. */
328 19109 : msg.msg_iov = &header_vec;
329 19109 : msg.msg_iovlen = 1;
330 19109 : msg.msg_name = 0;
331 19109 : msg.msg_namelen = 0;
332 38207 : if (0 < message->handle_count && message->handles != NULL) {
333 19098 : msg.msg_control = buf;
334 19098 : msg.msg_controllen = CMSG_SPACE(message->handle_count * sizeof(int));
335 19098 : } else {
336 11 : msg.msg_control = 0;
337 11 : msg.msg_controllen = 0;
338 : }
339 19109 : msg.msg_flags = 0;
340 38586 : for (retry_count = 0; retry_count < kRecvMsgRetries; ++retry_count) {
341 19261 : if (0 != (count = recvmsg(handle, &msg,
342 : (flags & NACL_DONT_WAIT) ? MSG_DONTWAIT : 0))) {
343 19077 : break;
344 : }
345 184 : }
346 19123 : if (0 != retry_count && kRecvMsgRetries != retry_count) {
347 0 : printf("OSX_BLEMISH_HEURISTIC: retry_count = %d, count = %d\n",
348 : retry_count, count);
349 0 : }
350 19100 : if (0 < count) {
351 19077 : handle_count = GetRights(&msg, message->handles);
352 19077 : }
353 19100 : if (count != sizeof header) {
354 46 : while (0 < handle_count) {
355 : /*
356 : * Note if the sender has sent one end of a socket pair here,
357 : * ReceiveDatagram() for that socket will result in a zero length read
358 : * return henceforth.
359 : */
360 0 : close(message->handles[--handle_count]);
361 0 : }
362 23 : if (count == 0) {
363 23 : message->handle_count = 0;
364 23 : return 0;
365 : }
366 0 : if (count != -1) {
367 : /*
368 : * TODO(shiki): We should call recvmsg() again here since it could get to
369 : * wake up with a partial header since the SOCK_STREAM socket does not
370 : * required to maintain message boundaries.
371 : */
372 0 : errno = EMSGSIZE;
373 0 : }
374 0 : return -1;
375 : }
376 :
377 19077 : message->handle_count = handle_count;
378 :
379 : /*
380 : * OS X seems not to set the MSG_CTRUNC flag in msg.msg_flags as we expect,
381 : * and we don't rely on it.
382 : */
383 19077 : if (message->handle_count < header.handle_count) {
384 0 : message->flags |= NACL_HANDLES_TRUNCATED;
385 0 : }
386 :
387 19077 : if (header.message_bytes == 0) {
388 200 : return 0;
389 : }
390 :
391 : /* Update message->iov to receive just message_bytes. */
392 18877 : memmove(vec, message->iov, sizeof(NaClIOVec) * message->iov_length);
393 18877 : msg.msg_iov = vec;
394 18877 : msg.msg_iovlen = message->iov_length;
395 37754 : for (i = 0; i < message->iov_length; ++i) {
396 18877 : buffer_bytes += vec[i].iov_len;
397 18877 : if (header.message_bytes <= buffer_bytes) {
398 18877 : vec[i].iov_len -= buffer_bytes - header.message_bytes;
399 18877 : buffer_bytes = header.message_bytes;
400 18877 : msg.msg_iovlen = i + 1;
401 18877 : break;
402 : }
403 0 : }
404 18877 : if (buffer_bytes < header.message_bytes) {
405 0 : message->flags |= NACL_MESSAGE_TRUNCATED;
406 0 : }
407 :
408 : /* Receive the sent data. */
409 18877 : msg.msg_name = 0;
410 18877 : msg.msg_namelen = 0;
411 :
412 18877 : msg.msg_control = 0;
413 18877 : msg.msg_controllen = 0;
414 18877 : msg.msg_flags = 0;
415 37754 : for (retry_count = 0; retry_count < kRecvMsgRetries; ++retry_count) {
416 : /*
417 : * We have to pass MSG_WAITALL here, because we have already consumed
418 : * the header. If we returned EAGAIN here, subsequent calls would read
419 : * data as a header, and much hilarity would ensue.
420 : */
421 18877 : if (0 != (count = recvmsg(handle, &msg, MSG_WAITALL))) {
422 18877 : break;
423 : }
424 0 : }
425 18877 : if (0 != retry_count && kRecvMsgRetries != retry_count) {
426 0 : printf("OSX_BLEMISH_HEURISTIC (2): retry_count = %d, count = %d\n",
427 : retry_count, count);
428 0 : }
429 18877 : if (0 < count) {
430 : /*
431 : * If the caller requested fewer bytes than the message contained, we need
432 : * to read the remaining bytes, discard them, and report message truncated.
433 : */
434 18877 : if ((size_t) count < header.message_bytes) {
435 0 : if (!SkipFile(handle, header.message_bytes - count)) {
436 0 : return -1;
437 : }
438 0 : message->flags |= NACL_MESSAGE_TRUNCATED;
439 0 : }
440 18877 : }
441 18877 : return count;
442 19100 : }
|