LCOV - code coverage report
Current view: directory - src/shared/imc/osx - nacl_imc.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 204 146 71.6 %
Date: 2014-06-18 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                 : /* 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 : }

Generated by: LCOV version 1.7