LCOV - code coverage report
Current view: directory - src/trusted/desc - nacl_desc_imc_shm.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 147 81 55.1 %
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 Service Runtime.  Transferrable shared memory objects.
       9                 :  */
      10                 : 
      11                 : #include "native_client/src/include/portability.h"
      12                 : #include "native_client/src/include/nacl_platform.h"
      13                 : 
      14                 : #include <stdlib.h>
      15                 : #include <string.h>
      16                 : 
      17                 : #include "native_client/src/shared/imc/nacl_imc_c.h"
      18                 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
      19                 : #include "native_client/src/trusted/desc/nacl_desc_effector.h"
      20                 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
      21                 : #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
      22                 : 
      23                 : #include "native_client/src/shared/platform/nacl_find_addrsp.h"
      24                 : #include "native_client/src/shared/platform/nacl_host_desc.h"
      25                 : #include "native_client/src/shared/platform/nacl_log.h"
      26                 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
      27                 : 
      28                 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
      29                 : #include "native_client/src/trusted/service_runtime/include/sys/mman.h"
      30                 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
      31                 : #include "native_client/src/trusted/service_runtime/internal_errno.h"
      32                 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
      33                 : 
      34                 : #ifndef SIZE_T_MAX
      35                 : # define SIZE_T_MAX   (~(size_t) 0)
      36                 : #endif
      37                 : 
      38                 : /*
      39                 :  * This file contains the implementation of the NaClDescImcShm
      40                 :  * subclass of NaClDesc.
      41                 :  *
      42                 :  * NaClDescImcShm is the subclass that wraps IMC shm descriptors.
      43                 :  */
      44                 : 
      45                 : static struct NaClDescVtbl const kNaClDescImcShmVtbl;  /* fwd */
      46                 : 
      47                 : int NaClDescImcShmCtor(struct NaClDescImcShm  *self,
      48                 :                        NaClHandle             h,
      49               9 :                        nacl_off64_t           size) {
      50               9 :   struct NaClDesc *basep = (struct NaClDesc *) self;
      51                 : 
      52                 :   /*
      53                 :    * off_t is signed, but size_t are not; historically size_t is for
      54                 :    * sizeof and similar, and off_t is also used for stat structure
      55                 :    * st_size member.  This runtime test detects large object sizes
      56                 :    * that are silently converted to negative values.
      57                 :    */
      58               9 :   basep->base.vtbl = (struct NaClRefCountVtbl const *) NULL;
      59               9 :   if (size < 0 || SIZE_T_MAX < (uint64_t) size) {
      60               0 :     return 0;
      61                 :   }
      62                 : 
      63               9 :   if (!NaClDescCtor(basep)) {
      64               0 :     return 0;
      65                 :   }
      66               9 :   self->h = h;
      67               9 :   self->size = size;
      68               9 :   basep->base.vtbl = (struct NaClRefCountVtbl const *) &kNaClDescImcShmVtbl;
      69               9 :   return 1;
      70                 : }
      71                 : 
      72                 : int NaClDescImcShmAllocCtor(struct NaClDescImcShm  *self,
      73                 :                             nacl_off64_t           size,
      74               9 :                             int                    executable) {
      75                 :   NaClHandle h;
      76                 :   int        rv;
      77                 : 
      78               9 :   if (size < 0 || SIZE_T_MAX < (uint64_t) size) {
      79               0 :     NaClLog(4,
      80                 :             "NaClDescImcShmAllocCtor: requested size 0x%08"NACL_PRIx64
      81                 :             " (0x%08"NACL_PRId64") too large\n",
      82                 :             size, size);
      83               0 :     return 0;
      84                 :   }
      85               9 :   h = NaClCreateMemoryObject((size_t) size, executable);
      86               9 :   if (NACL_INVALID_HANDLE == h) {
      87               0 :     return 0;
      88                 :   }
      89               9 :   if (0 == (rv = NaClDescImcShmCtor(self, h, size))) {
      90               0 :     (void) NaClClose(h);
      91                 :   }
      92               9 :   return rv;
      93                 : }
      94                 : 
      95               1 : static void NaClDescImcShmDtor(struct NaClRefCount *vself) {
      96               1 :   struct NaClDescImcShm  *self = (struct NaClDescImcShm *) vself;
      97                 : 
      98               1 :   (void) NaClClose(self->h);
      99               1 :   self->h = NACL_INVALID_HANDLE;
     100               1 :   vself->vtbl = (struct NaClRefCountVtbl const *) &kNaClDescVtbl;
     101               1 :   (*vself->vtbl->Dtor)(vself);
     102               1 : }
     103                 : 
     104                 : static uintptr_t NaClDescImcShmMap(struct NaClDesc         *vself,
     105                 :                                    struct NaClDescEffector *effp,
     106                 :                                    void                    *start_addr,
     107                 :                                    size_t                  len,
     108                 :                                    int                     prot,
     109                 :                                    int                     flags,
     110              11 :                                    nacl_off64_t            offset) {
     111              11 :   struct NaClDescImcShm  *self = (struct NaClDescImcShm *) vself;
     112                 : 
     113                 :   int           rv;
     114                 :   int           nacl_imc_prot;
     115                 :   int           nacl_imc_flags;
     116                 :   uintptr_t     addr;
     117                 :   uintptr_t     end_addr;
     118                 :   void          *result;
     119                 :   nacl_off64_t  tmp_off64;
     120                 :   off_t         tmp_off;
     121                 : 
     122              11 :   NaClLog(4,
     123                 :           "NaClDescImcShmMmap(,,0x%08"NACL_PRIxPTR",0x%"NACL_PRIxS","
     124                 :           "0x%x,0x%x,0x%08"NACL_PRIxNACL_OFF64")\n",
     125                 :           (uintptr_t) start_addr, len, prot, flags, offset);
     126                 :   /*
     127                 :    * shm must have NACL_ABI_MAP_SHARED in flags, and all calls through
     128                 :    * this API must supply a start_addr, so NACL_ABI_MAP_FIXED is
     129                 :    * assumed.
     130                 :    */
     131              11 :   if (NACL_ABI_MAP_SHARED != (flags & NACL_ABI_MAP_SHARING_MASK)) {
     132               0 :     NaClLog(LOG_INFO,
     133                 :             ("NaClDescImcShmMap: Mapping not NACL_ABI_MAP_SHARED,"
     134                 :              " flags 0x%x\n"),
     135                 :             flags);
     136               0 :     return -NACL_ABI_EINVAL;
     137                 :   }
     138              11 :   if (0 != (NACL_ABI_MAP_FIXED & flags) && NULL == start_addr) {
     139               0 :     NaClLog(LOG_INFO,
     140                 :             ("NaClDescImcShmMap: Mapping NACL_ABI_MAP_FIXED"
     141                 :              " but start_addr is NULL\n"));
     142                 :   }
     143                 :   /* post-condition: if NULL == start_addr, then NACL_ABI_MAP_FIXED not set */
     144                 : 
     145                 :   /*
     146                 :    * prot must not contain bits other than PROT_{READ|WRITE|EXEC}.
     147                 :    */
     148              11 :   if (0 != (~(NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE | NACL_ABI_PROT_EXEC)
     149                 :             & prot)) {
     150               0 :     NaClLog(LOG_INFO,
     151                 :             "NaClDescImcShmMap: prot has other bits than"
     152                 :             " PROT_{READ|WRITE|EXEC}\n");
     153               0 :     return -NACL_ABI_EINVAL;
     154                 :   }
     155                 :   /*
     156                 :    * Map from NACL_ABI_ prot and flags bits to IMC library flags,
     157                 :    * which will later map back into posix-style prot/flags on *x
     158                 :    * boxen, and to MapViewOfFileEx arguments on Windows.
     159                 :    */
     160              11 :   nacl_imc_prot = 0;
     161              11 :   if (NACL_ABI_PROT_READ & prot) {
     162               8 :     nacl_imc_prot |= NACL_PROT_READ;
     163                 :   }
     164              11 :   if (NACL_ABI_PROT_WRITE & prot) {
     165               8 :     nacl_imc_prot |= NACL_PROT_WRITE;
     166                 :   }
     167              11 :   if (NACL_ABI_PROT_EXEC & prot) {
     168               0 :     nacl_imc_prot |= NACL_PROT_EXEC;
     169                 :   }
     170              11 :   nacl_imc_flags = NACL_MAP_SHARED;
     171              11 :   if (0 == (NACL_ABI_MAP_FIXED & flags)) {
     172                 :     /* start_addr is a hint, and we just ignore the hint... */
     173               8 :     if (!NaClFindAddressSpace(&addr, len)) {
     174               0 :       NaClLog(1, "NaClDescImcShmMap: no address space?!?\n");
     175               0 :       return -NACL_ABI_ENOMEM;
     176                 :     }
     177               8 :     start_addr = (void *) addr;
     178                 :   }
     179              11 :   nacl_imc_flags |= NACL_MAP_FIXED;
     180                 : 
     181              11 :   tmp_off64 = offset + len;
     182                 :   /* just NaClRoundAllocPage, but in 64 bits */
     183              11 :   tmp_off64 = ((tmp_off64 + NACL_MAP_PAGESIZE - 1)
     184                 :              & ~(uint64_t) (NACL_MAP_PAGESIZE - 1));
     185              11 :   if (tmp_off64 > INT32_MAX) {
     186               0 :     NaClLog(LOG_INFO,
     187                 :             "NaClDescImcShmMap: total offset exceeds 32-bits\n");
     188               0 :     return -NACL_ABI_EOVERFLOW;
     189                 :   }
     190                 : 
     191                 :   /*
     192                 :    * For *x, we just map with MAP_FIXED and the kernel takes care of
     193                 :    * atomically unmapping any existing memory.  For Windows, we must
     194                 :    * unmap existing memory first, which creates a race condition,
     195                 :    * where some other innocent thread puts some other memory into the
     196                 :    * hole, and that memory becomes vulnerable to attack by the
     197                 :    * untrusted NaCl application.
     198                 :    *
     199                 :    * For now, abort the process.  We will need to figure out how to
     200                 :    * re-architect this code to do the address space move, since it is
     201                 :    * deep surgery and we'll need to ensure that all threads have
     202                 :    * stopped and any addresses derived from the old address space
     203                 :    * would not be on any thread's call stack, i.e., stop the thread in
     204                 :    * user space or before entering real service runtime code.  This
     205                 :    * means that no application thread may be indefinitely blocked
     206                 :    * performing a service call in the service runtime, since otherwise
     207                 :    * there is no way for us to stop all threads.
     208                 :    *
     209                 :    * TODO(bsy): We will probably return an internal error code
     210                 :    * -NACL_ABI_E_MOVE_ADDRESS_SPACE to ask the caller to do the address space
     211                 :    * dance.
     212                 :    */
     213                 :   for (addr = (uintptr_t) start_addr,
     214                 :            end_addr = addr + len,
     215              11 :            tmp_off = (off_t) offset;
     216           12340 :        addr < end_addr;
     217           12318 :        addr += NACL_MAP_PAGESIZE, tmp_off += NACL_MAP_PAGESIZE) {
     218                 : 
     219                 :     /*
     220                 :      * Minimize the time between the unmap and the map for the same
     221                 :      * page: we interleave the unmap and map for the pages, rather
     222                 :      * than do all the unmap first and then do all of the map
     223                 :      * operations.
     224                 :      */
     225           12318 :     if (0 !=
     226                 :         (rv = (*effp->vtbl->UnmapMemory)(effp,
     227                 :                                          addr,
     228                 :                                          NACL_MAP_PAGESIZE))) {
     229               0 :       NaClLog(LOG_FATAL,
     230                 :               ("NaClDescImcShmMap: error %d --"
     231                 :                " could not unmap 0x%08"NACL_PRIxPTR", length 0x%x\n"),
     232                 :               rv,
     233                 :               addr,
     234                 :               NACL_MAP_PAGESIZE);
     235                 :     }
     236                 : 
     237           12318 :     result = NaClMap((void *) addr,
     238                 :                      NACL_MAP_PAGESIZE,
     239                 :                      nacl_imc_prot,
     240                 :                      nacl_imc_flags,
     241                 :                      self->h,
     242                 :                      tmp_off);
     243           12318 :     if (NACL_MAP_FAILED == result) {
     244               0 :       return -NACL_ABI_E_MOVE_ADDRESS_SPACE;
     245                 :     }
     246           12318 :     if (0 != (NACL_ABI_MAP_FIXED & flags) && result != (void *) addr) {
     247               0 :       NaClLog(LOG_FATAL,
     248                 :               ("NaClDescImcShmMap: NACL_MAP_FIXED but"
     249                 :                " got 0x%08"NACL_PRIxPTR" instead of 0x%08"NACL_PRIxPTR"\n"),
     250                 :               (uintptr_t) result, addr);
     251                 :     }
     252                 :   }
     253              11 :   return (uintptr_t) start_addr;
     254                 : }
     255                 : 
     256                 : static int NaClDescImcShmUnmapCommon(struct NaClDesc         *vself,
     257                 :                                      struct NaClDescEffector *effp,
     258                 :                                      void                    *start_addr,
     259                 :                                      size_t                  len,
     260               6 :                                      int                     safe_mode) {
     261                 :   int       retval;
     262                 :   uintptr_t addr;
     263                 :   uintptr_t end_addr;
     264                 : 
     265                 :   UNREFERENCED_PARAMETER(vself);
     266                 : 
     267               6 :   retval = -NACL_ABI_EINVAL;
     268                 : 
     269               6 :   for (addr = (uintptr_t) start_addr, end_addr = addr + len;
     270              20 :        addr < end_addr;
     271               8 :        addr += NACL_MAP_PAGESIZE) {
     272                 :     int       status;
     273                 : 
     274                 : #if NACL_WINDOWS
     275                 :     /*
     276                 :      * On windows, we must unmap "properly", since overmapping will
     277                 :      * not tear down existing page mappings.
     278                 :      */
     279                 : #elif NACL_LINUX || NACL_OSX
     280               8 :     if (!safe_mode) {
     281                 :       /*
     282                 :        * unsafe unmap always unmaps, w/o overmapping with anonymous
     283                 :        * memory.  this is not necessary (nor desired) in safe_mode,
     284                 :        * since overmapping with anonymous memory will atomically tear
     285                 :        * down the mappings for these pages without leaving a timing
     286                 :        * window open where the untrusted address space has unoccupied
     287                 :        * page table entries.
     288                 :        */
     289                 : #else
     290                 : # error "what platform?"
     291                 : #endif
     292                 :       /*
     293                 :        * Do the unmap "properly" through NaClUnmap.
     294                 :        */
     295               8 :       status = NaClUnmap((void *) addr, NACL_MAP_PAGESIZE);
     296               8 :       if (0 != status) {
     297               0 :         NaClLog(LOG_FATAL, "NaClDescImcShmUnmapCommon: NaClUnmap failed\n");
     298               0 :         goto done;
     299                 :       }
     300                 : #if NACL_LINUX || NACL_OSX
     301                 :     }
     302                 : #endif
     303                 :     /* there's still a race condition */
     304               8 :     if (safe_mode) {
     305                 :       uintptr_t result = (*effp->vtbl->MapAnonymousMemory)(effp,
     306                 :                                                            addr,
     307                 :                                                            NACL_MAP_PAGESIZE,
     308               0 :                                                            PROT_NONE);
     309               0 :       if (NaClPtrIsNegErrno(&result)) {
     310               0 :         NaClLog(LOG_ERROR, "NaClDescImcShmUnmapCommon: could not fill hole\n");
     311               0 :         retval = -NACL_ABI_E_MOVE_ADDRESS_SPACE;
     312               0 :         goto done;
     313                 :       }
     314                 :     }
     315                 :   }
     316               6 :   retval = 0;
     317               6 : done:
     318               6 :   return retval;
     319                 : }
     320                 : 
     321                 : static int NaClDescImcShmUnmapUnsafe(struct NaClDesc         *vself,
     322                 :                                      struct NaClDescEffector *effp,
     323                 :                                      void                    *start_addr,
     324               6 :                                      size_t                  len) {
     325               6 :   return NaClDescImcShmUnmapCommon(vself, effp, start_addr, len, 0);
     326                 : }
     327                 : 
     328                 : static int NaClDescImcShmUnmap(struct NaClDesc         *vself,
     329                 :                                struct NaClDescEffector *effp,
     330                 :                                void                    *start_addr,
     331               0 :                                size_t                  len) {
     332               0 :   return NaClDescImcShmUnmapCommon(vself, effp, start_addr, len, 1);
     333                 : }
     334                 : 
     335                 : static int NaClDescImcShmFstat(struct NaClDesc         *vself,
     336               6 :                                struct nacl_abi_stat    *stbp) {
     337               6 :   struct NaClDescImcShm  *self = (struct NaClDescImcShm *) vself;
     338                 : 
     339               6 :   if (self->size > INT32_MAX) {
     340               0 :     return -NACL_ABI_EOVERFLOW;
     341                 :   }
     342                 : 
     343               6 :   stbp->nacl_abi_st_dev = 0;
     344               6 :   stbp->nacl_abi_st_ino = 0x6c43614e;
     345               6 :   stbp->nacl_abi_st_mode = (NACL_ABI_S_IFSHM |
     346                 :                             NACL_ABI_S_IRUSR |
     347                 :                             NACL_ABI_S_IWUSR);
     348               6 :   stbp->nacl_abi_st_nlink = 1;
     349               6 :   stbp->nacl_abi_st_uid = -1;
     350               6 :   stbp->nacl_abi_st_gid = -1;
     351               6 :   stbp->nacl_abi_st_rdev = 0;
     352               6 :   stbp->nacl_abi_st_size = (nacl_abi_off_t) self->size;
     353               6 :   stbp->nacl_abi_st_blksize = 0;
     354               6 :   stbp->nacl_abi_st_blocks = 0;
     355               6 :   stbp->nacl_abi_st_atime = 0;
     356               6 :   stbp->nacl_abi_st_mtime = 0;
     357               6 :   stbp->nacl_abi_st_ctime = 0;
     358                 : 
     359               6 :   return 0;
     360                 : }
     361                 : 
     362                 : static int NaClDescImcShmExternalizeSize(struct NaClDesc *vself,
     363                 :                                          size_t          *nbytes,
     364               0 :                                          size_t          *nhandles) {
     365               0 :   struct NaClDescImcShm  *self = (struct NaClDescImcShm *) vself;
     366                 : 
     367               0 :   *nbytes = sizeof self->size;
     368               0 :   *nhandles = 1;
     369                 : 
     370               0 :   return 0;
     371                 : }
     372                 : 
     373                 : static int NaClDescImcShmExternalize(struct NaClDesc           *vself,
     374               0 :                                      struct NaClDescXferState  *xfer) {
     375               0 :   struct NaClDescImcShm  *self = (struct NaClDescImcShm *) vself;
     376                 : 
     377               0 :   *xfer->next_handle++ = self->h;
     378               0 :   memcpy(xfer->next_byte, &self->size, sizeof self->size);
     379               0 :   xfer->next_byte += sizeof self->size;
     380               0 :   return 0;
     381                 : }
     382                 : 
     383                 : static struct NaClDescVtbl const kNaClDescImcShmVtbl = {
     384                 :   {
     385                 :     NaClDescImcShmDtor,
     386                 :   },
     387                 :   NaClDescImcShmMap,
     388                 :   NaClDescImcShmUnmapUnsafe,
     389                 :   NaClDescImcShmUnmap,
     390                 :   NaClDescReadNotImplemented,
     391                 :   NaClDescWriteNotImplemented,
     392                 :   NaClDescSeekNotImplemented,
     393                 :   NaClDescIoctlNotImplemented,
     394                 :   NaClDescImcShmFstat,
     395                 :   NaClDescGetdentsNotImplemented,
     396                 :   NACL_DESC_SHM,
     397                 :   NaClDescImcShmExternalizeSize,
     398                 :   NaClDescImcShmExternalize,
     399                 :   NaClDescLockNotImplemented,
     400                 :   NaClDescTryLockNotImplemented,
     401                 :   NaClDescUnlockNotImplemented,
     402                 :   NaClDescWaitNotImplemented,
     403                 :   NaClDescTimedWaitAbsNotImplemented,
     404                 :   NaClDescSignalNotImplemented,
     405                 :   NaClDescBroadcastNotImplemented,
     406                 :   NaClDescSendMsgNotImplemented,
     407                 :   NaClDescRecvMsgNotImplemented,
     408                 :   NaClDescConnectAddrNotImplemented,
     409                 :   NaClDescAcceptConnNotImplemented,
     410                 :   NaClDescPostNotImplemented,
     411                 :   NaClDescSemWaitNotImplemented,
     412                 :   NaClDescGetValueNotImplemented,
     413                 : };
     414                 : 
     415                 : int NaClDescImcShmInternalize(struct NaClDesc               **out_desc,
     416                 :                               struct NaClDescXferState      *xfer,
     417               0 :                               struct NaClDescQuotaInterface *quota_interface) {
     418                 :   int                   rv;
     419                 :   struct NaClDescImcShm *ndisp;
     420                 :   NaClHandle            h;
     421                 :   nacl_off64_t          hsize;
     422                 : 
     423                 :   UNREFERENCED_PARAMETER(quota_interface);
     424               0 :   rv = -NACL_ABI_EIO;
     425               0 :   ndisp = NULL;
     426                 : 
     427               0 :   if (xfer->next_handle == xfer->handle_buffer_end) {
     428               0 :     rv = -NACL_ABI_EIO;
     429               0 :     goto cleanup;
     430                 :   }
     431               0 :   if (xfer->next_byte + sizeof ndisp->size > xfer->byte_buffer_end) {
     432               0 :     rv = -NACL_ABI_EIO;
     433               0 :     goto cleanup;
     434                 :   }
     435                 : 
     436               0 :   ndisp = malloc(sizeof *ndisp);
     437               0 :   if (NULL == ndisp) {
     438               0 :     rv = -NACL_ABI_ENOMEM;
     439               0 :     goto cleanup;
     440                 :   }
     441                 : 
     442               0 :   h = *xfer->next_handle;
     443               0 :   *xfer->next_handle++ = NACL_INVALID_HANDLE;
     444               0 :   memcpy(&hsize, xfer->next_byte, sizeof hsize);
     445               0 :   xfer->next_byte += sizeof hsize;
     446                 : 
     447               0 :   if (0 == NaClDescImcShmCtor(ndisp, h, hsize)) {
     448               0 :     rv = -NACL_ABI_EIO;
     449               0 :     goto cleanup;
     450                 :   }
     451                 : 
     452               0 :   *out_desc = (struct NaClDesc *) ndisp;
     453               0 :   rv = 0;
     454                 : 
     455               0 : cleanup:
     456               0 :   if (rv < 0) {
     457               0 :     free(ndisp);
     458                 :   }
     459               0 :   return rv;
     460                 : }

Generated by: LCOV version 1.7