LCOV - code coverage report
Current view: directory - src/shared/imc/osx - nacl_imc.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 153 110 71.9 %
Date: 2012-02-16 Functions: 0 0 -

       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 inter-module communication primitives.
       9                 : 
      10                 : #include "native_client/src/shared/imc/nacl_imc.h"
      11                 : #include <assert.h>
      12                 : #include <ctype.h>
      13                 : #include <errno.h>
      14                 : #include <fcntl.h>
      15                 : #include <limits.h>
      16                 : #include <poll.h>
      17                 : #include <stdio.h>
      18                 : #include <string.h>
      19                 : #include <unistd.h>
      20                 : #include <sys/mman.h>
      21                 : #include <sys/socket.h>
      22                 : #include <sys/types.h>
      23                 : #include <sys/un.h>
      24                 : 
      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                 : #include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h"
      61                 : 
      62                 : namespace nacl {
      63                 : 
      64                 : namespace {
      65                 : 
      66                 : // The number of recvmsg retries to perform to determine --
      67                 : // heuristically, unfortunately -- if the remote end of the socketpair
      68                 : // had actually closed.  This is a (new) hacky workaround for an OSX
      69                 : // blemish that replaces the older, buggier workaround.
      70                 : const int kRecvMsgRetries = 8;
      71                 : 
      72                 : // The maximum number of IOVec elements sent by SendDatagram(). Plus one for
      73                 : // NaClInternalHeader with the descriptor data bytes.
      74                 : const size_t kIovLengthMax = NACL_ABI_IMC_IOVEC_MAX + 1;
      75                 : 
      76                 : // The IMC datagram header followed by a message_bytes of data sent over the
      77                 : // a stream-oriented socket. We need to use stream-oriented socket for OS X
      78                 : // since it doesn't support file descriptor transfer over SOCK_DGRAM socket
      79                 : // like Linux.
      80                 : struct Header {
      81                 :   // The total bytes of data in the IMC datagram excluding the size of Header.
      82                 :   size_t message_bytes;
      83                 :   // The total number of handles to be transferred with IMC datagram.
      84                 :   size_t handle_count;
      85                 : };
      86                 : 
      87                 : 
      88                 : // Gets an array of file descriptors stored in msg.
      89                 : // The fdv parameter must be an int array of kHandleCountMax elements.
      90                 : // GetRights() returns the number of file descriptors copied into fdv.
      91            9738 : size_t GetRights(struct msghdr* msg, int* fdv) {
      92            9738 :   if (msg->msg_controllen == 0) {
      93            9521 :     return 0;
      94                 :   }
      95             217 :   size_t count = 0;
      96             434 :   for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(msg);
      97                 :        cmsg != 0;
      98                 :        cmsg = CMSG_NXTHDR(msg, cmsg)) {
      99             217 :     if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
     100             658 :       while (CMSG_LEN((1 + count) * sizeof(int)) <= cmsg->cmsg_len) {
     101             224 :         *fdv++ = *(reinterpret_cast<int*>(CMSG_DATA(cmsg)) + count);
     102             224 :         ++count;
     103                 :       }
     104                 :     }
     105                 :   }
     106             217 :   return count;
     107                 : }
     108                 : 
     109                 : // Skips the specified length of octets when reading from a handle. Skipped
     110                 : // octets are discarded.
     111                 : // On success, true is returned. On error, false is returned.
     112               0 : bool SkipFile(int handle, size_t length) {
     113               0 :   while (0 < length) {
     114                 :     char scratch[1024];
     115               0 :     size_t count = std::min(sizeof scratch, length);
     116               0 :     count = read(handle, scratch, count);
     117               0 :     if (static_cast<ssize_t>(count) == -1 || count == 0) {
     118               0 :       return false;
     119                 :     }
     120               0 :     length -= count;
     121                 :   }
     122               0 :   return true;
     123                 : }
     124                 : 
     125                 : #if SIGPIPE_ALT_FIX
     126                 : // TODO(kbr): move this to an Init() function so it isn't called all
     127                 : // the time.
     128             500 : bool IgnoreSIGPIPE() {
     129                 :   sigset_t mask;
     130             500 :   sigemptyset(&mask);
     131                 :   struct sigaction sa;
     132             500 :   sa.sa_handler = SIG_IGN;
     133             500 :   sa.sa_mask = mask;
     134             500 :   sa.sa_flags = 0;
     135             500 :   return sigaction(SIGPIPE, &sa, NULL) == 0;
     136                 : }
     137                 : #endif
     138                 : 
     139                 : }  // namespace
     140                 : 
     141                 : // We keep these no-op implementations of SocketAddress-based
     142                 : // functions so that sigpipe_test continues to link.
     143               0 : Handle BoundSocket(const SocketAddress* address) {
     144                 :   UNREFERENCED_PARAMETER(address);
     145               0 :   NaClLog(LOG_FATAL, "BoundSocket(): Not used on OSX\n");
     146               0 :   return -1;
     147                 : }
     148                 : 
     149                 : int SendDatagramTo(const MessageHeader* message, int flags,
     150               0 :                    const SocketAddress* name) {
     151                 :   UNREFERENCED_PARAMETER(message);
     152                 :   UNREFERENCED_PARAMETER(flags);
     153                 :   UNREFERENCED_PARAMETER(name);
     154               0 :   NaClLog(LOG_FATAL, "SendDatagramTo(): Not used on OSX\n");
     155               0 :   return -1;
     156                 : }
     157                 : 
     158             500 : int SocketPair(Handle pair[2]) {
     159             500 :   int result = socketpair(AF_UNIX, SOCK_STREAM, 0, pair);
     160             500 :   if (result == 0) {
     161                 : #if SIGPIPE_ALT_FIX
     162             500 :     if (!IgnoreSIGPIPE()) {
     163               0 :       close(pair[0]);
     164               0 :       close(pair[1]);
     165               0 :       return -1;
     166                 :     }
     167                 : #endif
     168                 : #if SIGPIPE_FIX
     169             500 :     int nosigpipe = 1;
     170             500 :     if (0 != setsockopt(pair[0], SOL_SOCKET, SO_NOSIGPIPE,
     171                 :                         &nosigpipe, sizeof nosigpipe) ||
     172                 :         0 != setsockopt(pair[1], SOL_SOCKET, SO_NOSIGPIPE,
     173                 :                         &nosigpipe, sizeof nosigpipe)) {
     174               0 :       close(pair[0]);
     175               0 :       close(pair[1]);
     176               0 :       return -1;
     177                 :     }
     178                 : #endif
     179                 :   }
     180             500 :   return result;
     181                 : }
     182                 : 
     183            1186 : int Close(Handle handle) {
     184            1186 :   return close(handle);
     185                 : }
     186                 : 
     187            9728 : int SendDatagram(Handle handle, const MessageHeader* message, int flags) {
     188                 :   struct msghdr msg;
     189                 :   struct iovec vec[kIovLengthMax + 1];
     190                 :   unsigned char buf[CMSG_SPACE_KHANDLE_COUNT_MAX_INTS];
     191            9728 :   Header header = { 0, 0 };
     192                 : 
     193                 :   (void) flags;  /* BUG(shiki): unused parameter */
     194                 : 
     195                 :   assert(CMSG_SPACE(kHandleCountMax * sizeof(int))
     196                 :          <= CMSG_SPACE_KHANDLE_COUNT_MAX_INTS);
     197                 : 
     198                 :   /*
     199                 :    * The following assert was an earlier attempt to remember/check the
     200                 :    * assumption that our struct IOVec -- which we must define to be
     201                 :    * cross platform -- is compatible with struct iovec on *x systems.
     202                 :    * The length field of IOVec was switched to be uint32_t at oen point
     203                 :    * to use concrete types, which introduced a problem on 64-bit systems.
     204                 :    *
     205                 :    * Clearly, the assert does not check a strong-enough condition,
     206                 :    * since structure padding would make the two sizes the same.
     207                 :    *
     208                 :   assert(sizeof(struct iovec) == sizeof(IOVec));
     209                 :    *
     210                 :    * Don't do this again!
     211                 :    */
     212                 : 
     213            9728 :   if (!MessageSizeIsValid(message)) {
     214               0 :     errno = EMSGSIZE;
     215               0 :     return -1;
     216                 :   }
     217                 : 
     218            9728 :   if (kHandleCountMax < message->handle_count ||
     219                 :       kIovLengthMax < message->iov_length) {
     220               0 :     errno = EMSGSIZE;
     221               0 :     return -1;
     222                 :   }
     223                 : 
     224            9728 :   memmove(&vec[1], message->iov, sizeof(IOVec) * message->iov_length);
     225                 : 
     226            9728 :   msg.msg_name = 0;
     227            9728 :   msg.msg_namelen = 0;
     228            9728 :   msg.msg_iov = vec;
     229            9728 :   msg.msg_iovlen = 1 + message->iov_length;
     230            9943 :   if (0 < message->handle_count && message->handles != NULL) {
     231             215 :     int size = message->handle_count * sizeof(int);
     232             215 :     msg.msg_control = buf;
     233             215 :     msg.msg_controllen = CMSG_SPACE(size);
     234             215 :     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
     235             215 :     cmsg->cmsg_level = SOL_SOCKET;
     236             215 :     cmsg->cmsg_type = SCM_RIGHTS;
     237             215 :     cmsg->cmsg_len = CMSG_LEN(size);
     238             215 :     memcpy(reinterpret_cast<int*>(CMSG_DATA(cmsg)), message->handles, size);
     239             215 :     msg.msg_controllen = cmsg->cmsg_len;
     240             215 :     header.handle_count = message->handle_count;
     241                 :   } else {
     242            9513 :     msg.msg_control = 0;
     243            9513 :     msg.msg_controllen = 0;
     244                 :   }
     245            9728 :   msg.msg_flags = 0;
     246                 : 
     247                 :   // Send data with the header atomically. Note to send file descriptors we need
     248                 :   // to send at least one byte of data.
     249           39554 :   for (size_t i = 0; i < message->iov_length; ++i) {
     250           29826 :     header.message_bytes += message->iov[i].length;
     251                 :   }
     252            9728 :   vec[0].iov_base = &header;
     253            9728 :   vec[0].iov_len = sizeof header;
     254            9728 :   int result = sendmsg(handle, &msg, 0);
     255            9728 :   if (result == -1) {
     256               1 :     return -1;
     257                 :   }
     258            9727 :   if (static_cast<size_t>(result) < sizeof header) {
     259               0 :     errno = EMSGSIZE;
     260               0 :     return -1;
     261                 :   }
     262            9727 :   return result - sizeof header;
     263                 : }
     264                 : 
     265            9746 : int ReceiveDatagram(Handle handle, MessageHeader* message, int flags) {
     266                 :   struct msghdr msg;
     267                 :   struct iovec vec[kIovLengthMax];
     268                 :   unsigned char buf[CMSG_SPACE_KHANDLE_COUNT_MAX_INTS];
     269                 : 
     270                 :   assert(CMSG_SPACE(kHandleCountMax * sizeof(int))
     271                 :          <= CMSG_SPACE_KHANDLE_COUNT_MAX_INTS);
     272                 : 
     273            9746 :   if (kHandleCountMax < message->handle_count ||
     274                 :       kIovLengthMax < message->iov_length) {
     275               0 :     errno = EMSGSIZE;
     276               0 :     return -1;
     277                 :   }
     278                 : 
     279                 :   /*
     280                 :    * The following assert was an earlier attempt to remember/check the
     281                 :    * assumption that our struct IOVec -- which we must define to be
     282                 :    * cross platform -- is compatible with struct iovec on *x systems.
     283                 :    * The length field of IOVec was switched to be uint32_t at oen point
     284                 :    * to use concrete types, which introduced a problem on 64-bit systems.
     285                 :    *
     286                 :    * Clearly, the assert does not check a strong-enough condition,
     287                 :    * since structure padding would make the two sizes the same.
     288                 :    *
     289                 :   assert(sizeof(struct iovec) == sizeof(IOVec));
     290                 :    *
     291                 :    * Don't do this again!
     292                 :    */
     293                 : 
     294            9746 :   if (!MessageSizeIsValid(message)) {
     295               0 :     errno = EMSGSIZE;
     296               0 :     return -1;
     297                 :   }
     298                 : 
     299            9746 :   message->flags = 0;
     300                 :   // Receive the header of the message and handles first.
     301                 :   Header header;
     302            9746 :   struct iovec header_vec = { &header, sizeof header };
     303            9746 :   msg.msg_iov = &header_vec;
     304            9746 :   msg.msg_iovlen = 1;
     305            9746 :   msg.msg_name = 0;
     306            9746 :   msg.msg_namelen = 0;
     307           19492 :   if (0 < message->handle_count && message->handles != NULL) {
     308            9746 :     msg.msg_control = buf;
     309            9746 :     msg.msg_controllen = CMSG_SPACE(message->handle_count * sizeof(int));
     310                 :   } else {
     311               0 :     msg.msg_control = 0;
     312               0 :     msg.msg_controllen = 0;
     313                 :   }
     314            9746 :   msg.msg_flags = 0;
     315                 :   int count;
     316                 :   int retry_count;
     317            9810 :   for (retry_count = 0; retry_count < kRecvMsgRetries; ++retry_count) {
     318            9802 :     if (0 != (count = recvmsg(handle, &msg,
     319                 :                               (flags & kDontWait) ? MSG_DONTWAIT : 0))) {
     320            9738 :       break;
     321                 :     }
     322                 :   }
     323            9746 :   if (0 != retry_count && kRecvMsgRetries != retry_count) {
     324                 :     printf("OSX_BLEMISH_HEURISTIC: retry_count = %d, count = %d\n",
     325               0 :            retry_count, count);
     326                 :   }
     327            9746 :   size_t handle_count = 0;
     328            9746 :   if (0 < count) {
     329            9738 :     handle_count = GetRights(&msg, message->handles);
     330                 :   }
     331            9746 :   if (count != sizeof header) {
     332              16 :     while (0 < handle_count) {
     333                 :       // Note if the sender has sent one end of a socket pair here,
     334                 :       // ReceiveDatagram() for that socket will result in a zero length read
     335                 :       // return henceforth.
     336               0 :       close(message->handles[--handle_count]);
     337                 :     }
     338               8 :     if (count == 0) {
     339               8 :       message->handle_count = 0;
     340               8 :       return 0;
     341                 :     }
     342               0 :     if (count != -1) {
     343                 :       // TODO(shiki): We should call recvmsg() again here since it could get to
     344                 :       // wake up with a partial header since the SOCK_STREAM socket does not
     345                 :       // required to maintain message boundaries.
     346               0 :       errno = EMSGSIZE;
     347                 :     }
     348               0 :     return -1;
     349                 :   }
     350                 : 
     351            9738 :   message->handle_count = handle_count;
     352                 : 
     353                 :   // OS X seems not to set the MSG_CTRUNC flag in msg.msg_flags as we expect,
     354                 :   // and we don't rely on it.
     355            9738 :   if (message->handle_count < header.handle_count) {
     356               0 :     message->flags |= kHandlesTruncated;
     357                 :   }
     358                 : 
     359            9738 :   if (header.message_bytes == 0) {
     360             200 :     return 0;
     361                 :   }
     362                 : 
     363                 :   // Update message->iov to receive just message_bytes.
     364            9538 :   memmove(vec, message->iov, sizeof(IOVec) * message->iov_length);
     365            9538 :   msg.msg_iov = vec;
     366            9538 :   msg.msg_iovlen = message->iov_length;
     367            9538 :   size_t buffer_bytes = 0;
     368                 :   size_t i;
     369            9538 :   for (i = 0; i < message->iov_length; ++i) {
     370            9538 :     buffer_bytes += vec[i].iov_len;
     371            9538 :     if (header.message_bytes <= buffer_bytes) {
     372            9538 :       vec[i].iov_len -= buffer_bytes - header.message_bytes;
     373            9538 :       buffer_bytes = header.message_bytes;
     374            9538 :       msg.msg_iovlen = i + 1;
     375            9538 :       break;
     376                 :     }
     377                 :   }
     378            9538 :   if (buffer_bytes < header.message_bytes) {
     379               0 :     message->flags |= kMessageTruncated;
     380                 :   }
     381                 : 
     382                 :   // Receive the sent data.
     383            9538 :   msg.msg_name = 0;
     384            9538 :   msg.msg_namelen = 0;
     385                 : 
     386            9538 :   msg.msg_control = 0;
     387            9538 :   msg.msg_controllen = 0;
     388            9538 :   msg.msg_flags = 0;
     389            9538 :   for (retry_count = 0; retry_count < kRecvMsgRetries; ++retry_count) {
     390                 :     // We have to pass MSG_WAITALL here, because we have already consumed
     391                 :     // the header.  If we returned EAGAIN here, subsequent calls would read
     392                 :     // data as a header, and much hilarity would ensue.
     393            9538 :     if (0 != (count = recvmsg(handle, &msg, MSG_WAITALL))) {
     394            9538 :       break;
     395                 :     }
     396                 :   }
     397            9538 :   if (0 != retry_count && kRecvMsgRetries != retry_count) {
     398                 :     printf("OSX_BLEMISH_HEURISTIC (2): retry_count = %d, count = %d\n",
     399               0 :            retry_count, count);
     400                 :   }
     401            9538 :   if (0 < count) {
     402                 :     // If the caller requested fewer bytes than the message contained, we need
     403                 :     // to read the remaining bytes, discard them, and report message truncated.
     404            9538 :     if (static_cast<size_t>(count) < header.message_bytes) {
     405               0 :       if (!SkipFile(handle, header.message_bytes - count)) {
     406               0 :         return -1;
     407                 :       }
     408               0 :       message->flags |= kMessageTruncated;
     409                 :     }
     410                 :   }
     411            9538 :   return count;
     412                 : }
     413                 : 
     414                 : }  // namespace nacl

Generated by: LCOV version 1.7