LCOV - code coverage report
Current view: directory - src/trusted/gio - gio_shm.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 208 153 73.6 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 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                 : #include <errno.h>
       8                 : #include <string.h>
       9                 : 
      10                 : #include "native_client/src/include/portability.h"
      11                 : 
      12                 : #include "native_client/src/shared/platform/nacl_check.h"
      13                 : #include "native_client/src/shared/platform/nacl_host_desc.h"
      14                 : #include "native_client/src/shared/platform/nacl_log.h"
      15                 : 
      16                 : #include "native_client/src/trusted/gio/gio_shm.h"
      17                 : #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
      18                 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
      19                 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
      20                 : #include "native_client/src/trusted/service_runtime/sel_util.h"
      21                 : 
      22                 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
      23                 : #include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
      24                 : #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
      25                 : 
      26                 : /*
      27                 :  * This code maps in GIO_SHM_WINDOWSIZE bytes at a time for doing
      28                 :  * "I/O" from/to the shared memory object.  This value must be an
      29                 :  * integer multiple of NACL_MAP_PAGESIZE.
      30                 :  */
      31                 : #define GIO_SHM_WINDOWSIZE  (16 * NACL_MAP_PAGESIZE)
      32                 : 
      33                 : /*
      34                 :  * Release current window if it exists, then map in window at the
      35                 :  * provided new_window_offset.  This is akin to filbuf.
      36                 :  *
      37                 :  * Preconditions: 0 == (new_win_offset & (NACL_MAP_PAGESIZE - 1))
      38                 :  *                new_win_offset < self->shm_sz
      39                 :  */
      40               6 : static int NaClGioShmSetWindow(struct NaClGioShm  *self,
      41               6 :                                size_t             new_win_offset) {
      42               6 :   uintptr_t map_result;
      43               6 :   size_t    actual_len;
      44                 : 
      45               6 :   NaClLog(4,
      46                 :           "NaClGioShmSetWindow: new_win_offset 0x%"NACL_PRIxS"\n",
      47                 :           new_win_offset);
      48               6 :   if (0 != (new_win_offset & (NACL_MAP_PAGESIZE - 1))) {
      49               0 :     NaClLog(LOG_FATAL,
      50                 :             ("NaClGioShmSetWindow: internal error, requested"
      51                 :              " new window offset 0x%"NACL_PRIxS" is not aligned.\n"),
      52                 :             new_win_offset);
      53               0 :   }
      54                 : 
      55               6 :   if (new_win_offset >= self->shm_sz) {
      56               0 :     NaClLog(LOG_FATAL,
      57                 :             ("NaClGioShmSetWindow: setting window beyond end of shm object"
      58                 :              " offset 0x%"NACL_PRIxS", size 0x%"NACL_PRIxS"\n"),
      59                 :             new_win_offset, self->shm_sz);
      60               0 :   }
      61                 : 
      62               6 :   if (NULL != self->cur_window) {
      63               0 :     NaClDescUnmapUnsafe(self->shmp, (void *) self->cur_window,
      64                 :                         self->window_size);
      65               0 :   }
      66               6 :   self->cur_window = NULL;
      67               6 :   self->window_size = 0;
      68                 : 
      69                 :   /*
      70                 :    * The Map virtual function will NOT pad space beyond the end of the
      71                 :    * memory mapping object with zero-filled pages.  This is done for
      72                 :    * user code in nacl_syscall_common.c (NaClSysMmap), and the Map
      73                 :    * virtual function exposes the behavioral inconsistencies wrt
      74                 :    * allowing but ignoring mapping an offset beyond the end of file
      75                 :    * (linux) versus disallowing the mapping (MapViewOfFileEx).
      76                 :    *
      77                 :    * Here, we know the actual size of the shm object, and can deal
      78                 :    * with it.
      79                 :    */
      80               6 :   actual_len = GIO_SHM_WINDOWSIZE;
      81               6 :   if (actual_len > self->shm_sz - new_win_offset) {
      82               5 :     actual_len = self->shm_sz - new_win_offset;
      83               5 :   }
      84                 :   map_result =
      85              12 :       (*((struct NaClDescVtbl const *) self->shmp->base.vtbl)->
      86                 :        Map)(self->shmp,
      87               6 :             NaClDescEffectorTrustedMem(),
      88                 :             (void *) NULL,
      89                 :             actual_len,
      90                 :             NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
      91                 :             NACL_ABI_MAP_SHARED,
      92                 :             (nacl_off64_t) new_win_offset);
      93               6 :   NaClLog(4,
      94                 :           "NaClGioShmSetWindow: Map returned 0x%"NACL_PRIxPTR"\n",
      95                 :           map_result);
      96               6 :   if (NaClPtrIsNegErrno(&map_result)) {
      97               0 :     return 0;
      98                 :   }
      99                 : 
     100               6 :   self->cur_window = (char *) map_result;
     101               6 :   self->window_size = actual_len;
     102               6 :   self->window_offset = new_win_offset;
     103                 : 
     104               6 :   return 1;
     105               6 : }
     106                 : 
     107          997892 : static ssize_t NaClGioShmReadOrWrite(struct Gio *vself,
     108          997892 :                                      void       *buf,
     109          997892 :                                      size_t     count,
     110          997892 :                                      int        is_write) {
     111          997892 :   struct NaClGioShm *self = (struct NaClGioShm *) vself;
     112          997892 :   size_t            new_window_offset;
     113          997892 :   size_t            transfer;
     114          997892 :   size_t            window_end;
     115          997892 :   size_t            window_remain;
     116          997892 :   size_t            sofar;
     117                 : 
     118          997892 :   NaClLog(4,
     119                 :           ("NaClGioShmReadOrWrite: 0x%"NACL_PRIxPTR","
     120                 :            " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS", %d\n"),
     121                 :           (uintptr_t) vself,
     122                 :           (uintptr_t) buf,
     123                 :           count,
     124                 :           is_write);
     125          997892 :   sofar = 0;
     126         2993675 :   while (count > 0) {
     127          997891 :     NaClLog(4, "NaClGioShmReadOrWrite: count 0x%"NACL_PRIxS"\n", count);
     128          997891 :     if (self->io_offset >= self->shm_sz) {
     129               0 :       break;
     130                 :     }
     131          997891 :     NaClLog(4, "   cur_window 0x%"NACL_PRIxPTR"\n",
     132                 :             (uintptr_t) self->cur_window);
     133          997891 :     NaClLog(4, "    io_offset 0x%"NACL_PRIxS"\n", self->io_offset);
     134          997891 :     NaClLog(4, "window_offset 0x%"NACL_PRIxS"\n", self->window_offset);
     135         2993673 :     if (NULL == self->cur_window
     136                 :         || self->io_offset < self->window_offset
     137                 :         || self->window_offset + self->window_size <= self->io_offset) {
     138                 :       /*
     139                 :        * io_offset is outside the window.  move the window so that
     140                 :        * it's within.
     141                 :        */
     142               0 :       NaClLog(4, "Seek required\n");
     143                 : 
     144               0 :       new_window_offset = (self->io_offset
     145                 :                            & (~(((size_t) NACL_MAP_PAGESIZE) - 1)));
     146               0 :       NaClLog(4, "new_window_offset 0x%"NACL_PRIxS"\n", new_window_offset);
     147               0 :       CHECK(0 == (new_window_offset &
     148                 :                   (((size_t) NACL_MAP_PAGESIZE)-1)));
     149               0 :       if (!NaClGioShmSetWindow(self, new_window_offset)) {
     150               0 :         if (0 == sofar) {
     151               0 :           errno = EIO;
     152               0 :           sofar = -1;
     153               0 :         }
     154               0 :         return sofar;
     155                 :       }
     156               0 :     } else {
     157          997891 :       NaClLog(4, "no seek required\n");
     158                 :     }
     159          997891 :     NaClLog(4, "   cur_window 0x%"NACL_PRIxPTR"\n",
     160                 :             (uintptr_t) self->cur_window);
     161          997891 :     NaClLog(4, "    io_offset 0x%"NACL_PRIxS"\n", self->io_offset);
     162          997891 :     NaClLog(4, "window_offset 0x%"NACL_PRIxS"\n", self->window_offset);
     163                 : 
     164         2993673 :     CHECK(self->window_offset <= self->io_offset);
     165         2993673 :     CHECK(self->io_offset < self->window_offset + self->window_size);
     166                 : 
     167          997891 :     transfer = count;
     168          997891 :     window_end = self->window_offset + self->window_size;
     169          997891 :     if (window_end > self->shm_sz) {
     170               0 :       window_end = self->shm_sz;
     171               0 :     }
     172          997891 :     window_remain = window_end - self->io_offset;
     173                 : 
     174          997891 :     NaClLog(4, "remaining in window 0x%"NACL_PRIxS"\n", window_remain);
     175                 : 
     176         2993673 :     CHECK(window_remain <= GIO_SHM_WINDOWSIZE);
     177                 : 
     178          997891 :     if (transfer > window_remain) {
     179               0 :       transfer = window_remain;
     180               0 :     }
     181                 : 
     182          997891 :     NaClLog(4, "transfer 0x%"NACL_PRIxS"\n", transfer);
     183                 : 
     184          997891 :     if (is_write) {
     185          400642 :       NaClLog(4,
     186                 :               ("about to \"write\" memcpy(0x%"NACL_PRIxPTR", "
     187                 :                " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS" bytes)\n"),
     188                 :               (uintptr_t) (self->cur_window
     189                 :                            + (self->io_offset - self->window_offset)),
     190                 :               (uintptr_t) buf,
     191                 :               transfer);
     192                 : 
     193         1201926 :       memcpy(self->cur_window + (self->io_offset - self->window_offset),
     194                 :              buf,
     195                 :              transfer);
     196          400642 :     } else {
     197          597249 :       NaClLog(4,
     198                 :               ("about to \"read\" memcpy(0x%"NACL_PRIxPTR", "
     199                 :                " 0x%"NACL_PRIxPTR", 0x%"NACL_PRIxS" bytes)\n"),
     200                 :               (uintptr_t) buf,
     201                 :               (uintptr_t) (self->cur_window
     202                 :                            + (self->io_offset - self->window_offset)),
     203                 :               transfer);
     204                 : 
     205         1791747 :       memcpy(buf,
     206                 :              self->cur_window + (self->io_offset - self->window_offset),
     207                 :              transfer);
     208                 :     }
     209          997891 :     self->io_offset += transfer;
     210          997891 :     sofar += transfer;
     211                 : 
     212          997891 :     buf = (void *)((uintptr_t) buf + transfer);
     213          997891 :     count -= transfer;
     214          997891 :   }
     215                 : 
     216          997892 :   return sofar;
     217          997892 : }
     218                 : 
     219          597250 : static ssize_t NaClGioShmRead(struct Gio *vself,
     220          597250 :                               void       *buf,
     221          597250 :                               size_t     count) {
     222          597250 :   return NaClGioShmReadOrWrite(vself, buf, count, 0);
     223                 : }
     224                 : 
     225          400642 : static ssize_t NaClGioShmWrite(struct Gio *vself,
     226          400642 :                                const void *buf,
     227          400642 :                                size_t     count) {
     228          400642 :   return NaClGioShmReadOrWrite(vself, (void *) buf, count, 1);
     229                 : }
     230                 : 
     231         1974308 : static off_t NaClGioShmSeek(struct Gio  *vself,
     232         1974308 :                             off_t       offset,
     233         1974308 :                             int         whence) {
     234         1974308 :   struct NaClGioShm *self = (struct NaClGioShm *) vself;
     235         1974308 :   size_t new_pos = (size_t) -1;
     236                 : 
     237         1974308 :   NaClLog(4, "NaClGioShmSeek(0x%"NACL_PRIxPTR", %ld (0x%lx), %d)\n",
     238                 :           (uintptr_t) vself, (long) offset, (long) offset, whence);
     239                 :   /*
     240                 :    * Note that if sizeof(new_pos) < sizeof(offset), we are dropping
     241                 :    * high-order bits and we do not detect this.  However, the check
     242                 :    * after the switch keeps the values somewhat sane: we will never
     243                 :    * set the I/O offset to be outside the range [0, self->shm_sz].
     244                 :    */
     245         1974308 :   switch (whence) {
     246                 :     case SEEK_SET:
     247          987150 :       new_pos = (size_t) offset;
     248          987150 :       break;
     249                 :     case SEEK_CUR:
     250               4 :       new_pos = self->io_offset + offset;
     251               4 :       break;
     252                 :     case SEEK_END:
     253               0 :       new_pos = self->shm_sz + offset;
     254               0 :       break;
     255                 :   }
     256                 :   /* allow equality, so setting to the end of file is okay */
     257          987154 :   if (self->shm_sz < new_pos) {
     258               0 :     NaClLog(4, " invalid offset\n");
     259               0 :     errno = EINVAL;
     260               0 :     return -1;
     261                 :   }
     262          987154 :   NaClLog(4, " setting to %ld (0x%lx)\n", (long) new_pos, (long) new_pos);
     263                 :   /* sizeof(off_t) >= sizeof(size_t) */
     264          987154 :   self->io_offset = new_pos;
     265          987154 :   return (off_t) self->io_offset;
     266          987154 : }
     267                 : 
     268               0 : static int NaClGioShmFlush(struct Gio *vself) {
     269               0 :   UNREFERENCED_PARAMETER(vself);
     270               0 :   return 0;
     271                 : }
     272                 : 
     273               6 : static int NaClGioShmClose(struct Gio *vself) {
     274               6 :   struct NaClGioShm *self = (struct NaClGioShm *) vself;
     275                 : 
     276               6 :   if (NULL != self->cur_window) {
     277               6 :     NaClDescUnmapUnsafe(self->shmp, (void *) self->cur_window,
     278                 :                         NACL_MAP_PAGESIZE);
     279               6 :   }
     280               6 :   self->cur_window = NULL;
     281                 : 
     282               6 :   if (NULL == self->shmp) {
     283               0 :     NaClLog(LOG_ERROR, "NaClGioShmClose: double close detected\n");
     284               0 :     errno = EIO;
     285               0 :     return -1;
     286                 :   }
     287                 : 
     288               6 :   NaClDescUnref(self->shmp);
     289               6 :   self->shmp = NULL;  /* double close will fault */
     290               6 :   return 0;
     291               6 : }
     292                 : 
     293               6 : static void NaClGioShmDtor(struct Gio *vself) {
     294               6 :   struct NaClGioShm *self = (struct NaClGioShm *) vself;
     295                 : 
     296                 :   /*
     297                 :    * Users of Gio objects are expected to Close then Dtor, but Dtor
     298                 :    * should cleanup regardless.
     299                 :    */
     300               6 :   if (NULL != self->shmp) {
     301               1 :     if (-1 == (*vself->vtbl->Close)(vself)) {
     302               0 :       NaClLog(LOG_ERROR, "NaClGioShmDtor: auto Close failed!\n");
     303               0 :     }
     304               1 :   }
     305                 : 
     306               6 :   self->shmp = NULL;
     307               6 :   self->base.vtbl = NULL;
     308               6 : }
     309                 : 
     310                 : const struct GioVtbl kNaClGioShmVtbl = {
     311                 :   NaClGioShmDtor,
     312                 :   NaClGioShmRead,
     313                 :   NaClGioShmWrite,
     314                 :   NaClGioShmSeek,
     315                 :   NaClGioShmFlush,
     316                 :   NaClGioShmClose,
     317                 : };
     318                 : 
     319                 : 
     320               6 : static int NaClGioShmCtorIntern(struct NaClGioShm  *self,
     321               6 :                                 struct NaClDesc    *shmp,
     322               6 :                                 size_t             shm_size) {
     323               6 :   struct nacl_abi_stat  stbuf;
     324               6 :   int                   vfret;
     325               6 :   int                   rval = 0;
     326                 : 
     327               6 :   self->base.vtbl = NULL;
     328                 : 
     329               6 :   self->shmp = NULL;
     330               6 :   self->cur_window = NULL;
     331                 : 
     332               6 :   if (0 != (vfret = (*((struct NaClDescVtbl const *) shmp->base.vtbl)->
     333                 :                      Fstat)(shmp, &stbuf))) {
     334               0 :     NaClLog(1, "NaClGioShmCtorIntern: Fstat virtual function returned %d\n",
     335                 :             vfret);
     336               0 :     goto cleanup;
     337                 :   }
     338                 :   /*
     339                 :    * nacl_abi_off_t is signed 32-bit quantity, but we don't want to
     340                 :    * hardwire in that knowledge here.
     341                 :    *
     342                 :    * size_t is unsigned, and may be 32-bits or 64-bits, depending on
     343                 :    * the underlying host OS.
     344                 :    *
     345                 :    * we want to ensure that the shm's size, as reported by the desc
     346                 :    * abstraction and thus is in nacl_abi_off_t, is at least that
     347                 :    * claimed by the ctor argument.  so, if (as Integers)
     348                 :    *
     349                 :    *  stbuf.nacl_abi_st_size < shm_size
     350                 :    *
     351                 :    * holds, this is an error.  however, the value-preserving cast rule
     352                 :    * makes this harder.
     353                 :    *
     354                 :    * Note that for signed sizes (ssize_t), the kernel ABI generally
     355                 :    * only reserve -1 for error, and asking for an I/O operation via a
     356                 :    * size_t that would succeed but yield a ssize_t return value that
     357                 :    * is negative is okay, since -1 is never valid as an I/O size on a
     358                 :    * von Neuman machine (except for a writev where the iov entries
     359                 :    * overlap): there just isn't that much data to read/write, when the
     360                 :    * instructions also take up space in the process address space.
     361                 :    * Whether requiring the programmer to detect this corner case is
     362                 :    * advisable is a different argument -- similar to negative ssize_t
     363                 :    * sizes, the syscall can just succeed with a partial transfer to
     364                 :    * avoid returning -1 on a success, just as we could avoid returning
     365                 :    * negative values; in practice, we do the latter, since we often
     366                 :    * see code written that tests for syscall error by comparing the
     367                 :    * return value to see if it is less than zero, rather than if it is
     368                 :    * equal to -1.
     369                 :    */
     370               6 :   if (stbuf.nacl_abi_st_size < 0) {
     371               0 :     NaClLog(LOG_ERROR,
     372                 :             ("NaClGioShmCtorIntern: actual shm size negative"
     373                 :              " %"NACL_PRIdNACL_OFF"\n"),
     374                 :             stbuf.nacl_abi_st_size);
     375               0 :     goto cleanup;
     376                 :   }
     377               6 :   if (stbuf.nacl_abi_st_size <= (nacl_abi_off_t) SIZE_T_MAX
     378                 :       && (size_t) stbuf.nacl_abi_st_size < shm_size) {
     379               0 :     NaClLog(LOG_ERROR,
     380                 :             ("NaClGioShmCtorIntern: claimed shm file size greater than"
     381                 :              " actual shm segment size, %"NACL_PRIuS" vs"
     382                 :              " %"NACL_PRIuNACL_OFF"\n"),
     383                 :             shm_size,
     384                 :             stbuf.nacl_abi_st_size);
     385               0 :     goto cleanup;
     386                 :   }
     387               6 :   if (OFF_T_MAX < SIZE_T_MAX && (size_t) OFF_T_MAX < shm_size) {
     388               0 :     NaClLog(LOG_ERROR,
     389                 :             ("NaClGioShmCtorIntern: claimed shm file size greater than"
     390                 :              " off_t max value, %"NACL_PRId64"\n"),
     391                 :             (int64_t) OFF_T_MAX);
     392               0 :     goto cleanup;
     393                 :   }
     394                 : 
     395               6 :   self->shmp = NaClDescRef(shmp);
     396                 : 
     397               6 :   self->io_offset = 0;
     398               6 :   self->shm_sz = shm_size;
     399               6 :   self->window_offset = 0;
     400                 : 
     401               6 :   self->base.vtbl = &kNaClGioShmVtbl;
     402                 : 
     403               6 :   if (!NaClGioShmSetWindow(self, 0)) {
     404               0 :     NaClLog(LOG_ERROR,
     405                 :             ("NaClGioShmCtorIntern: initial seek to beginning failed\n"));
     406               0 :     NaClDescUnref(self->shmp);
     407               0 :     self->shmp = NULL;
     408               0 :     self->shm_sz = 0;
     409               0 :     self->base.vtbl = NULL;
     410               0 :     goto cleanup;
     411                 :   }
     412                 : 
     413               6 :   rval = 1;
     414                 :  cleanup:
     415               6 :   return rval;
     416                 : }
     417                 : 
     418               1 : int NaClGioShmCtor(struct NaClGioShm  *self,
     419               1 :                    struct NaClDesc    *shmp,
     420               1 :                    size_t             shm_size) {
     421                 : 
     422               1 :   int rv;
     423                 : 
     424               3 :   CHECK(shm_size == NaClRoundAllocPage(shm_size));
     425                 : 
     426               1 :   rv = NaClGioShmCtorIntern(self, shmp, shm_size);
     427                 : 
     428               1 :   return rv;
     429                 : }
     430                 : 
     431               5 : int NaClGioShmAllocCtor(struct NaClGioShm *self,
     432               5 :                         size_t            shm_size) {
     433               5 :   struct NaClDescImcShm *shmp;
     434               5 :   int                   rv;
     435                 : 
     436              15 :   CHECK(shm_size == NaClRoundAllocPage(shm_size));
     437                 : 
     438               5 :   shmp = malloc(sizeof *shmp);
     439               5 :   if (NULL == shmp) {
     440               0 :     return 0;
     441                 :   }
     442               5 :   if (!NaClDescImcShmAllocCtor(shmp, shm_size, /* executable= */ 0)) {
     443               0 :     free(shmp);
     444               0 :     return 0;
     445                 :   }
     446                 : 
     447               5 :   rv = NaClGioShmCtorIntern(self, (struct NaClDesc *) shmp, shm_size);
     448               5 :   NaClDescUnref((struct NaClDesc *) shmp);
     449                 : 
     450               5 :   if (!rv) {
     451               0 :     free(shmp);
     452               0 :   }
     453               5 :   return rv;
     454               5 : }

Generated by: LCOV version 1.7