LCOV - code coverage report
Current view: directory - src/trusted/service_runtime - sys_memory.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 518 421 81.3 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2013 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 "native_client/src/trusted/service_runtime/sys_memory.h"
       8                 : 
       9                 : #include <errno.h>
      10                 : #include <string.h>
      11                 : 
      12                 : #include "native_client/src/include/nacl_assert.h"
      13                 : #include "native_client/src/include/nacl_platform.h"
      14                 : #include "native_client/src/shared/platform/nacl_check.h"
      15                 : #include "native_client/src/shared/platform/nacl_log.h"
      16                 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
      17                 : #include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
      18                 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
      19                 : #include "native_client/src/trusted/fault_injection/fault_injection.h"
      20                 : #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
      21                 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
      22                 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
      23                 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
      24                 : #include "native_client/src/trusted/service_runtime/internal_errno.h"
      25                 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
      26                 : #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
      27                 : #include "native_client/src/trusted/service_runtime/nacl_text.h"
      28                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      29                 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
      30                 : #include "native_client/src/trusted/validator/validation_metadata.h"
      31                 : 
      32                 : #if NACL_WINDOWS
      33                 : # include "native_client/src/shared/platform/win/xlate_system_error.h"
      34                 : #endif
      35                 : 
      36                 : 
      37                 : static int32_t MunmapInternal(struct NaClApp *nap,
      38                 :                               uintptr_t sysaddr, size_t length);
      39                 : 
      40                 : static const size_t kMaxUsableFileSize = (SIZE_T_MAX >> 1);
      41                 : 
      42                 : 
      43           71602 : static INLINE size_t  size_min(size_t a, size_t b) {
      44          214806 :   return (a < b) ? a : b;
      45                 : }
      46                 : 
      47              16 : int32_t NaClSysBrk(struct NaClAppThread *natp,
      48              16 :                    uintptr_t            new_break) {
      49              16 :   struct NaClApp        *nap = natp->nap;
      50              16 :   uintptr_t             break_addr;
      51              16 :   int32_t               rv = -NACL_ABI_EINVAL;
      52              16 :   struct NaClVmmapIter  iter;
      53              16 :   struct NaClVmmapEntry *ent;
      54              16 :   struct NaClVmmapEntry *next_ent;
      55              16 :   uintptr_t             sys_break;
      56              16 :   uintptr_t             sys_new_break;
      57              16 :   uintptr_t             usr_last_data_page;
      58              16 :   uintptr_t             usr_new_last_data_page;
      59              16 :   uintptr_t             last_internal_data_addr;
      60              16 :   uintptr_t             last_internal_page;
      61              16 :   uintptr_t             start_new_region;
      62              16 :   uintptr_t             region_size;
      63                 : 
      64                 :   /*
      65                 :    * The sysbrk() IRT interface is deprecated and is not enabled for
      66                 :    * ABI-stable PNaCl pexes, so for security hardening, disable the
      67                 :    * syscall under PNaCl too.
      68                 :    */
      69              16 :   if (nap->pnacl_mode)
      70               0 :     return -NACL_ABI_ENOSYS;
      71                 : 
      72              16 :   break_addr = nap->break_addr;
      73                 : 
      74              16 :   NaClLog(3, "Entered NaClSysBrk(new_break 0x%08"NACL_PRIxPTR")\n",
      75                 :           new_break);
      76                 : 
      77              16 :   sys_new_break = NaClUserToSysAddr(nap, new_break);
      78              16 :   NaClLog(3, "sys_new_break 0x%08"NACL_PRIxPTR"\n", sys_new_break);
      79                 : 
      80              16 :   if (kNaClBadAddress == sys_new_break) {
      81              13 :     goto cleanup_no_lock;
      82                 :   }
      83               3 :   if (NACL_SYNC_OK != NaClMutexLock(&nap->mu)) {
      84               0 :     NaClLog(LOG_ERROR, "Could not get app lock for 0x%08"NACL_PRIxPTR"\n",
      85                 :             (uintptr_t) nap);
      86               0 :     goto cleanup_no_lock;
      87                 :   }
      88               3 :   if (new_break < nap->data_end) {
      89               0 :     NaClLog(4, "new_break before data_end (0x%"NACL_PRIxPTR")\n",
      90                 :             nap->data_end);
      91               0 :     goto cleanup;
      92                 :   }
      93               3 :   if (new_break <= nap->break_addr) {
      94                 :     /* freeing memory */
      95               0 :     NaClLog(4, "new_break before break (0x%"NACL_PRIxPTR"); freeing\n",
      96                 :             nap->break_addr);
      97               0 :     nap->break_addr = new_break;
      98               0 :     break_addr = new_break;
      99               0 :   } else {
     100                 :     /*
     101                 :      * See if page containing new_break is in mem_map; if so, we are
     102                 :      * essentially done -- just update break_addr.  Otherwise, we
     103                 :      * extend the VM map entry from the page containing the current
     104                 :      * break to the page containing new_break.
     105                 :      */
     106                 : 
     107               3 :     sys_break = NaClUserToSys(nap, nap->break_addr);
     108                 : 
     109               3 :     usr_last_data_page = (nap->break_addr - 1) >> NACL_PAGESHIFT;
     110                 : 
     111               3 :     usr_new_last_data_page = (new_break - 1) >> NACL_PAGESHIFT;
     112                 : 
     113               3 :     last_internal_data_addr = NaClRoundAllocPage(new_break) - 1;
     114               3 :     last_internal_page = last_internal_data_addr >> NACL_PAGESHIFT;
     115                 : 
     116               3 :     NaClLog(4, ("current break sys addr 0x%08"NACL_PRIxPTR", "
     117                 :                 "usr last data page 0x%"NACL_PRIxPTR"\n"),
     118                 :             sys_break, usr_last_data_page);
     119               3 :     NaClLog(4, "new break usr last data page 0x%"NACL_PRIxPTR"\n",
     120                 :             usr_new_last_data_page);
     121               3 :     NaClLog(4, "last internal data addr 0x%08"NACL_PRIxPTR"\n",
     122                 :             last_internal_data_addr);
     123                 : 
     124               3 :     if (NULL == NaClVmmapFindPageIter(&nap->mem_map,
     125                 :                                       usr_last_data_page,
     126                 :                                       &iter)
     127               3 :         || NaClVmmapIterAtEnd(&iter)) {
     128               0 :       NaClLog(LOG_FATAL, ("current break (0x%08"NACL_PRIxPTR", "
     129                 :                           "sys 0x%08"NACL_PRIxPTR") "
     130                 :                           "not in address map\n"),
     131                 :               nap->break_addr, sys_break);
     132               0 :     }
     133               3 :     ent = NaClVmmapIterStar(&iter);
     134               3 :     NaClLog(4, ("segment containing current break"
     135                 :                 ": page_num 0x%08"NACL_PRIxPTR", npages 0x%"NACL_PRIxS"\n"),
     136                 :             ent->page_num, ent->npages);
     137               3 :     if (usr_new_last_data_page < ent->page_num + ent->npages) {
     138               0 :       NaClLog(4, "new break within break segment, just bumping addr\n");
     139               0 :       nap->break_addr = new_break;
     140               0 :       break_addr = new_break;
     141               0 :     } else {
     142               3 :       NaClVmmapIterIncr(&iter);
     143               3 :       if (!NaClVmmapIterAtEnd(&iter)
     144               3 :           && ((next_ent = NaClVmmapIterStar(&iter))->page_num
     145                 :               <= last_internal_page)) {
     146                 :         /* ran into next segment! */
     147               1 :         NaClLog(4,
     148                 :                 ("new break request of usr address "
     149                 :                  "0x%08"NACL_PRIxPTR" / usr page 0x%"NACL_PRIxPTR
     150                 :                  " runs into next region, page_num 0x%"NACL_PRIxPTR", "
     151                 :                  "npages 0x%"NACL_PRIxS"\n"),
     152                 :                 new_break, usr_new_last_data_page,
     153                 :                 next_ent->page_num, next_ent->npages);
     154               1 :         goto cleanup;
     155                 :       }
     156               2 :       NaClLog(4,
     157                 :               "extending segment: page_num 0x%08"NACL_PRIxPTR", "
     158                 :               "npages 0x%"NACL_PRIxS"\n",
     159                 :               ent->page_num, ent->npages);
     160                 :       /* go ahead and extend ent to cover, and make pages accessible */
     161               2 :       start_new_region = (ent->page_num + ent->npages) << NACL_PAGESHIFT;
     162               2 :       ent->npages = (last_internal_page - ent->page_num + 1);
     163               2 :       region_size = (((last_internal_page + 1) << NACL_PAGESHIFT)
     164                 :                      - start_new_region);
     165               2 :       if (0 != NaClMprotect((void *) NaClUserToSys(nap, start_new_region),
     166                 :                             region_size,
     167                 :                             PROT_READ | PROT_WRITE)) {
     168               0 :         NaClLog(LOG_FATAL,
     169                 :                 ("Could not mprotect(0x%08"NACL_PRIxPTR", "
     170                 :                  "0x%08"NACL_PRIxPTR", "
     171                 :                  "PROT_READ|PROT_WRITE)\n"),
     172                 :                 start_new_region,
     173                 :                 region_size);
     174               0 :       }
     175               2 :       NaClLog(4, "segment now: page_num 0x%08"NACL_PRIxPTR", "
     176                 :               "npages 0x%"NACL_PRIxS"\n",
     177                 :               ent->page_num, ent->npages);
     178               2 :       nap->break_addr = new_break;
     179               2 :       break_addr = new_break;
     180                 :     }
     181                 :     /*
     182                 :      * Zero out memory between old break and new break.
     183                 :      */
     184               6 :     ASSERT(sys_new_break > sys_break);
     185               6 :     memset((void *) sys_break, 0, sys_new_break - sys_break);
     186               2 :   }
     187                 : 
     188                 : 
     189                 : 
     190                 : cleanup:
     191               3 :   NaClXMutexUnlock(&nap->mu);
     192                 : cleanup_no_lock:
     193                 : 
     194                 :   /*
     195                 :    * This cast is safe because the incoming value (new_break) cannot
     196                 :    * exceed the user address space--even though its type (uintptr_t)
     197                 :    * theoretically allows larger values.
     198                 :    */
     199              16 :   rv = (int32_t) break_addr;
     200                 : 
     201              16 :   NaClLog(3, "NaClSysBrk: returning 0x%08"NACL_PRIx32"\n", rv);
     202              16 :   return rv;
     203              16 : }
     204                 : 
     205          139811 : int NaClSysCommonAddrRangeContainsExecutablePages(struct NaClApp *nap,
     206          139811 :                                                   uintptr_t usraddr,
     207          139811 :                                                   size_t length) {
     208                 :   /*
     209                 :    * NOTE: currently only trampoline and text region are executable,
     210                 :    * and they are at the beginning of the address space, so this code
     211                 :    * is fine.  We will probably never allow users to mark other pages
     212                 :    * as executable; but if so, we will have to revisit how this check
     213                 :    * is implemented.
     214                 :    *
     215                 :    * nap->static_text_end is a multiple of 4K, the memory protection
     216                 :    * granularity.  Since this routine is used for checking whether
     217                 :    * memory map adjustments / allocations -- which has 64K granularity
     218                 :    * -- is okay, usraddr must be an allocation granularity value.  Our
     219                 :    * callers (as of this writing) does this, but we truncate it down
     220                 :    * to an allocation boundary to be sure.
     221                 :    */
     222          279622 :   UNREFERENCED_PARAMETER(length);
     223          139811 :   usraddr = NaClTruncAllocPage(usraddr);
     224          139811 :   return usraddr < nap->dynamic_text_end;
     225                 : }
     226                 : 
     227              16 : int NaClSysCommonAddrRangeInAllowedDynamicCodeSpace(struct NaClApp *nap,
     228              16 :                                                     uintptr_t usraddr,
     229              16 :                                                     size_t length) {
     230              16 :   uintptr_t usr_region_end = usraddr + length;
     231                 : 
     232              16 :   if (usr_region_end < usraddr) {
     233                 :     /* Check for unsigned addition overflow */
     234               0 :     return 0;
     235                 :   }
     236              16 :   usr_region_end = NaClRoundAllocPage(usr_region_end);
     237              16 :   if (usr_region_end < usraddr) {
     238                 :     /* 32-bit systems only, rounding caused uint32_t overflow */
     239               0 :     return 0;
     240                 :   }
     241              32 :   return (nap->dynamic_text_start <= usraddr &&
     242                 :           usr_region_end <= nap->dynamic_text_end);
     243              16 : }
     244                 : 
     245                 : /* Warning: sizeof(nacl_abi_off_t)!=sizeof(off_t) on OSX */
     246           71527 : int32_t NaClSysMmapIntern(struct NaClApp        *nap,
     247           71527 :                           void                  *start,
     248           71527 :                           size_t                length,
     249           71527 :                           int                   prot,
     250           71527 :                           int                   flags,
     251           71527 :                           int                   d,
     252           71527 :                           nacl_abi_off_t        offset) {
     253           71527 :   int                         allowed_flags;
     254           71527 :   struct NaClDesc             *ndp;
     255           71527 :   uintptr_t                   usraddr;
     256           71527 :   uintptr_t                   usrpage;
     257           71527 :   uintptr_t                   sysaddr;
     258           71527 :   uintptr_t                   endaddr;
     259           71527 :   int                         mapping_code;
     260           71527 :   uintptr_t                   map_result;
     261           71527 :   int                         holding_app_lock;
     262           71527 :   struct nacl_abi_stat        stbuf;
     263           71527 :   size_t                      alloc_rounded_length;
     264           71527 :   nacl_off64_t                file_size;
     265           71527 :   nacl_off64_t                file_bytes;
     266           71527 :   nacl_off64_t                host_rounded_file_bytes;
     267           71527 :   size_t                      alloc_rounded_file_bytes;
     268                 : 
     269           71527 :   holding_app_lock = 0;
     270           71527 :   ndp = NULL;
     271                 : 
     272           71527 :   allowed_flags = (NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED
     273                 :                    | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS);
     274                 : 
     275           71527 :   usraddr = (uintptr_t) start;
     276                 : 
     277           71527 :   if (0 != (flags & ~allowed_flags)) {
     278               0 :     NaClLog(2, "invalid mmap flags 0%o, ignoring extraneous bits\n", flags);
     279               0 :     flags &= allowed_flags;
     280               0 :   }
     281                 : 
     282           71527 :   if (0 != (flags & NACL_ABI_MAP_ANONYMOUS)) {
     283                 :     /*
     284                 :      * anonymous mmap, so backing store is just swap: no descriptor is
     285                 :      * involved, and no memory object will be created to represent the
     286                 :      * descriptor.
     287                 :      */
     288           71427 :     ndp = NULL;
     289           71427 :   } else {
     290             100 :     ndp = NaClAppGetDesc(nap, d);
     291             100 :     if (NULL == ndp) {
     292               1 :       map_result = -NACL_ABI_EBADF;
     293               1 :       goto cleanup;
     294                 :     }
     295                 :   }
     296                 : 
     297           71526 :   mapping_code = 0;
     298                 :   /*
     299                 :    * Check if application is trying to do dynamic code loading by
     300                 :    * mmaping a file.
     301                 :    */
     302           71562 :   if (0 != (NACL_ABI_PROT_EXEC & prot) &&
     303                 :       0 != (NACL_ABI_MAP_FIXED & flags) &&
     304                 :       NULL != ndp &&
     305              16 :       NaClSysCommonAddrRangeInAllowedDynamicCodeSpace(nap, usraddr, length)) {
     306              16 :     if (!nap->enable_dyncode_syscalls) {
     307               7 :       NaClLog(LOG_WARNING,
     308                 :               "NaClSysMmap: PROT_EXEC when dyncode syscalls are disabled.\n");
     309               7 :       map_result = -NACL_ABI_EINVAL;
     310               7 :       goto cleanup;
     311                 :     }
     312               9 :     if (0 != (NACL_ABI_PROT_WRITE & prot)) {
     313               1 :       NaClLog(3,
     314                 :               "NaClSysMmap: asked for writable and executable code pages?!?\n");
     315               1 :       map_result = -NACL_ABI_EINVAL;
     316               1 :       goto cleanup;
     317                 :     }
     318               8 :     mapping_code = 1;
     319           71518 :   } else if (0 != (prot & NACL_ABI_PROT_EXEC)) {
     320               4 :     map_result = -NACL_ABI_EINVAL;
     321               4 :     goto cleanup;
     322                 :   }
     323                 : 
     324                 :   /*
     325                 :    * Starting address must be aligned to worst-case allocation
     326                 :    * granularity.  (Windows.)
     327                 :    */
     328           71514 :   if (!NaClIsAllocPageMultiple(usraddr)) {
     329               4 :     NaClLog(2, "NaClSysMmap: address not allocation granularity aligned\n");
     330               4 :     map_result = -NACL_ABI_EINVAL;
     331               4 :     goto cleanup;
     332                 :   }
     333                 :   /*
     334                 :    * Offset should be non-negative (nacl_abi_off_t is signed).  This
     335                 :    * condition is caught when the file is stat'd and checked, and
     336                 :    * offset is ignored for anonymous mappings.
     337                 :    */
     338           71510 :   if (offset < 0) {
     339               0 :     NaClLog(1,  /* application bug */
     340                 :             "NaClSysMmap: negative file offset: %"NACL_PRIdNACL_OFF"\n",
     341                 :             offset);
     342               0 :     map_result = -NACL_ABI_EINVAL;
     343               0 :     goto cleanup;
     344                 :   }
     345                 :   /*
     346                 :    * And offset must be a multiple of the allocation unit.
     347                 :    */
     348           71510 :   if (!NaClIsAllocPageMultiple((uintptr_t) offset)) {
     349               1 :     NaClLog(1,
     350                 :             ("NaClSysMmap: file offset 0x%08"NACL_PRIxPTR" not multiple"
     351                 :              " of allocation size\n"),
     352                 :             (uintptr_t) offset);
     353               1 :     map_result = -NACL_ABI_EINVAL;
     354               1 :     goto cleanup;
     355                 :   }
     356                 : 
     357           71509 :   if (0 == length) {
     358               2 :     map_result = -NACL_ABI_EINVAL;
     359               2 :     goto cleanup;
     360                 :   }
     361           71507 :   alloc_rounded_length = NaClRoundAllocPage(length);
     362           71507 :   if (alloc_rounded_length != length) {
     363              80 :     if (mapping_code) {
     364               1 :       NaClLog(3, "NaClSysMmap: length not a multiple of allocation size\n");
     365               1 :       map_result = -NACL_ABI_EINVAL;
     366               1 :       goto cleanup;
     367                 :     }
     368              79 :     NaClLog(1,
     369                 :             "NaClSysMmap: rounded length to 0x%"NACL_PRIxS"\n",
     370                 :             alloc_rounded_length);
     371              79 :   }
     372                 : 
     373           71506 :   if (NULL == ndp) {
     374                 :     /*
     375                 :      * Note: sentinel values are bigger than the NaCl module addr space.
     376                 :      */
     377           71422 :     file_size                = kMaxUsableFileSize;
     378           71422 :     file_bytes               = kMaxUsableFileSize;
     379           71422 :     host_rounded_file_bytes  = kMaxUsableFileSize;
     380           71422 :     alloc_rounded_file_bytes = kMaxUsableFileSize;
     381           71422 :   } else {
     382                 :     /*
     383                 :      * We stat the file to figure out its actual size.
     384                 :      *
     385                 :      * This is necessary because the POSIXy interface we provide
     386                 :      * allows mapping beyond the extent of a file but Windows'
     387                 :      * interface does not.  We simulate the POSIX behaviour on
     388                 :      * Windows.
     389                 :      */
     390              84 :     map_result = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
     391                 :                   Fstat)(ndp, &stbuf);
     392              84 :     if (0 != map_result) {
     393               0 :       goto cleanup;
     394                 :     }
     395                 : 
     396                 :     /*
     397                 :      * Preemptively refuse to map anything that's not a regular file or
     398                 :      * shared memory segment.  Other types usually report st_size of zero,
     399                 :      * which the code below will handle by just doing a dummy PROT_NONE
     400                 :      * mapping for the requested size and never attempting the underlying
     401                 :      * NaClDesc Map operation.  So without this check, the host OS never
     402                 :      * gets the chance to refuse the mapping operation on an object that
     403                 :      * can't do it.
     404                 :      */
     405             123 :     if (!NACL_ABI_S_ISREG(stbuf.nacl_abi_st_mode) &&
     406                 :         !NACL_ABI_S_ISSHM(stbuf.nacl_abi_st_mode)) {
     407               1 :       map_result = -NACL_ABI_ENODEV;
     408               1 :       goto cleanup;
     409                 :     }
     410                 : 
     411                 :     /*
     412                 :      * BUG(bsy): there's a race between this fstat and the actual mmap
     413                 :      * below.  It's probably insoluble.  Even if we fstat again after
     414                 :      * mmap and compared, the mmap could have "seen" the file with a
     415                 :      * different size, after which the racing thread restored back to
     416                 :      * the same value before the 2nd fstat takes place.
     417                 :      */
     418              83 :     file_size = stbuf.nacl_abi_st_size;
     419                 : 
     420              83 :     if (file_size < offset) {
     421               0 :       map_result = -NACL_ABI_EINVAL;
     422               0 :       goto cleanup;
     423                 :     }
     424                 : 
     425              83 :     file_bytes = file_size - offset;
     426              83 :     NaClLog(4,
     427                 :             "NaClSysMmapIntern: file_bytes 0x%016"NACL_PRIxNACL_OFF"\n",
     428                 :             file_bytes);
     429              83 :     if ((nacl_off64_t) kMaxUsableFileSize < file_bytes) {
     430               0 :       host_rounded_file_bytes = kMaxUsableFileSize;
     431               0 :     } else {
     432              83 :       host_rounded_file_bytes = NaClRoundHostAllocPage((size_t) file_bytes);
     433                 :     }
     434                 : 
     435             249 :     ASSERT(host_rounded_file_bytes <= (nacl_off64_t) kMaxUsableFileSize);
     436                 :     /*
     437                 :      * We need to deal with NaClRoundHostAllocPage rounding up to zero
     438                 :      * from ~0u - n, where n < 4096 or 65536 (== 1 alloc page).
     439                 :      *
     440                 :      * Luckily, file_bytes is at most kMaxUsableFileSize which is
     441                 :      * smaller than SIZE_T_MAX, so it should never happen, but we
     442                 :      * leave the explicit check below as defensive programming.
     443                 :      */
     444                 :     alloc_rounded_file_bytes =
     445              83 :       NaClRoundAllocPage((size_t) host_rounded_file_bytes);
     446                 : 
     447              83 :     if (0 == alloc_rounded_file_bytes && 0 != host_rounded_file_bytes) {
     448               0 :       map_result = -NACL_ABI_ENOMEM;
     449               0 :       goto cleanup;
     450                 :     }
     451                 : 
     452                 :     /*
     453                 :      * NB: host_rounded_file_bytes and alloc_rounded_file_bytes can be
     454                 :      * zero.  Such an mmap just makes memory (offset relative to
     455                 :      * usraddr) in the range [0, alloc_rounded_length) inaccessible.
     456                 :      */
     457                 :   }
     458                 : 
     459                 :   /*
     460                 :    * host_rounded_file_bytes is how many bytes we can map from the
     461                 :    * file, given the user-supplied starting offset.  It is at least
     462                 :    * one page.  If it came from a real file, it is a multiple of
     463                 :    * host-OS allocation size.  it cannot be larger than
     464                 :    * kMaxUsableFileSize.
     465                 :    */
     466           71511 :   if (mapping_code && (size_t) file_bytes < alloc_rounded_length) {
     467               1 :     NaClLog(3,
     468                 :             "NaClSysMmap: disallowing partial allocation page extension for"
     469                 :             " short files\n");
     470               1 :     map_result = -NACL_ABI_EINVAL;
     471               1 :     goto cleanup;
     472                 :   }
     473           71504 :   length = size_min(alloc_rounded_length, (size_t) host_rounded_file_bytes);
     474                 : 
     475                 :   /*
     476                 :    * Lock the addr space.
     477                 :    */
     478           71504 :   NaClXMutexLock(&nap->mu);
     479                 : 
     480           71504 :   NaClVmHoleOpeningMu(nap);
     481                 : 
     482           71504 :   holding_app_lock = 1;
     483                 : 
     484           71504 :   if (0 == (flags & NACL_ABI_MAP_FIXED)) {
     485                 :     /*
     486                 :      * The user wants us to pick an address range.
     487                 :      */
     488           70458 :     if (0 == usraddr) {
     489                 :       /*
     490                 :        * Pick a hole in addr space of appropriate size, anywhere.
     491                 :        * We pick one that's best for the system.
     492                 :        */
     493           70444 :       usrpage = NaClVmmapFindMapSpace(&nap->mem_map,
     494                 :                                       alloc_rounded_length >> NACL_PAGESHIFT);
     495           70444 :       NaClLog(4, "NaClSysMmap: FindMapSpace: page 0x%05"NACL_PRIxPTR"\n",
     496                 :               usrpage);
     497           70444 :       if (0 == usrpage) {
     498               2 :         map_result = -NACL_ABI_ENOMEM;
     499               2 :         goto cleanup;
     500                 :       }
     501           70442 :       usraddr = usrpage << NACL_PAGESHIFT;
     502           70442 :       NaClLog(4, "NaClSysMmap: new starting addr: 0x%08"NACL_PRIxPTR
     503                 :               "\n", usraddr);
     504           70442 :     } else {
     505                 :       /*
     506                 :        * user supplied an addr, but it's to be treated as a hint; we
     507                 :        * find a hole of the right size in the app's address space,
     508                 :        * according to the usual mmap semantics.
     509                 :        */
     510              14 :       usrpage = NaClVmmapFindMapSpaceAboveHint(&nap->mem_map,
     511                 :                                                usraddr,
     512                 :                                                (alloc_rounded_length
     513                 :                                                 >> NACL_PAGESHIFT));
     514              14 :       NaClLog(4, "NaClSysMmap: FindSpaceAboveHint: page 0x%05"NACL_PRIxPTR"\n",
     515                 :               usrpage);
     516              14 :       if (0 == usrpage) {
     517               2 :         NaClLog(4, "NaClSysMmap: hint failed, doing generic allocation\n");
     518               2 :         usrpage = NaClVmmapFindMapSpace(&nap->mem_map,
     519                 :                                         alloc_rounded_length >> NACL_PAGESHIFT);
     520               2 :       }
     521              14 :       if (0 == usrpage) {
     522               0 :         map_result = -NACL_ABI_ENOMEM;
     523               0 :         goto cleanup;
     524                 :       }
     525              14 :       usraddr = usrpage << NACL_PAGESHIFT;
     526              14 :       NaClLog(4, "NaClSysMmap: new starting addr: 0x%08"NACL_PRIxPTR"\n",
     527                 :               usraddr);
     528                 :     }
     529           70456 :   }
     530                 : 
     531                 :   /*
     532                 :    * Validate [usraddr, endaddr) is okay.
     533                 :    */
     534           71502 :   if (usraddr >= ((uintptr_t) 1 << nap->addr_bits)) {
     535               0 :     NaClLog(2,
     536                 :             ("NaClSysMmap: start address (0x%08"NACL_PRIxPTR") outside address"
     537                 :              " space\n"),
     538                 :             usraddr);
     539               0 :     map_result = -NACL_ABI_EINVAL;
     540               0 :     goto cleanup;
     541                 :   }
     542           71502 :   endaddr = usraddr + alloc_rounded_length;
     543           71502 :   if (endaddr < usraddr) {
     544               0 :     NaClLog(0,
     545                 :             ("NaClSysMmap: integer overflow -- "
     546                 :              "NaClSysMmap(0x%08"NACL_PRIxPTR",0x%"NACL_PRIxS",0x%x,0x%x,%d,"
     547                 :              "0x%08"NACL_PRIxPTR"\n"),
     548                 :             usraddr, length, prot, flags, d, (uintptr_t) offset);
     549               0 :     map_result = -NACL_ABI_EINVAL;
     550               0 :     goto cleanup;
     551                 :   }
     552                 :   /*
     553                 :    * NB: we use > instead of >= here.
     554                 :    *
     555                 :    * endaddr is the address of the first byte beyond the target region
     556                 :    * and it can equal the address space limit.  (of course, normally
     557                 :    * the main thread's stack is there.)
     558                 :    */
     559           71502 :   if (endaddr > ((uintptr_t) 1 << nap->addr_bits)) {
     560               0 :     NaClLog(2,
     561                 :             ("NaClSysMmap: end address (0x%08"NACL_PRIxPTR") is beyond"
     562                 :              " the end of the address space\n"),
     563                 :             endaddr);
     564               0 :     map_result = -NACL_ABI_EINVAL;
     565               0 :     goto cleanup;
     566                 :   }
     567                 : 
     568           71502 :   if (mapping_code) {
     569               5 :     NaClLog(4,
     570                 :             "NaClSysMmap: PROT_EXEC requested, usraddr 0x%08"NACL_PRIxPTR
     571                 :             ", length %"NACL_PRIxS"\n",
     572                 :             usraddr, length);
     573              10 :     if (!NACL_FI("MMAP_BYPASS_DESCRIPTOR_SAFETY_CHECK",
     574                 :                  NaClDescIsSafeForMmap(ndp),
     575                 :                  1)) {
     576               1 :       NaClLog(4, "NaClSysMmap: descriptor not blessed\n");
     577               1 :       map_result = -NACL_ABI_EINVAL;
     578               1 :       goto cleanup;
     579                 :     }
     580               4 :     NaClLog(4, "NaClSysMmap: allowed\n");
     581           71501 :   } else if (NaClSysCommonAddrRangeContainsExecutablePages(nap,
     582                 :                                                            usraddr,
     583                 :                                                            length)) {
     584               3 :     NaClLog(2, "NaClSysMmap: region contains executable pages\n");
     585               3 :     map_result = -NACL_ABI_EINVAL;
     586               3 :     goto cleanup;
     587                 :   }
     588                 : 
     589           71496 :   NaClVmIoPendingCheck_mu(nap,
     590                 :                           (uint32_t) usraddr,
     591                 :                           (uint32_t) (usraddr + length - 1));
     592                 : 
     593                 :   /*
     594                 :    * Force NACL_ABI_MAP_FIXED, since we are specifying address in NaCl
     595                 :    * app address space.
     596                 :    */
     597           71496 :   flags |= NACL_ABI_MAP_FIXED;
     598                 : 
     599                 :   /*
     600                 :    * Turn off PROT_EXEC -- normal user mmapped pages should not be
     601                 :    * executable.  This is primarily for the service runtime's own
     602                 :    * bookkeeping -- prot is used in NaClVmmapAddWithOverwrite and will
     603                 :    * be needed for remapping data pages on Windows if page protection
     604                 :    * is set to PROT_NONE and back.
     605                 :    *
     606                 :    * NB: we've captured the notion of mapping executable memory for
     607                 :    * dynamic library loading etc in mapping_code, so when we do map
     608                 :    * text we will explicitly OR in NACL_ABI_PROT_EXEC as needed.
     609                 :    */
     610           71496 :   prot &= ~NACL_ABI_PROT_EXEC;
     611                 : 
     612                 :   /*
     613                 :    * Exactly one of NACL_ABI_MAP_SHARED and NACL_ABI_MAP_PRIVATE is set.
     614                 :    */
     615           71496 :   if ((0 == (flags & NACL_ABI_MAP_SHARED)) ==
     616                 :       (0 == (flags & NACL_ABI_MAP_PRIVATE))) {
     617               0 :     map_result = -NACL_ABI_EINVAL;
     618               0 :     goto cleanup;
     619                 :   }
     620                 : 
     621           71496 :   sysaddr = NaClUserToSys(nap, usraddr);
     622                 : 
     623                 :   /* [0, length) */
     624           71496 :   if (length > 0) {
     625           71496 :     if (NULL == ndp) {
     626           71417 :       NaClLog(4,
     627                 :               ("NaClSysMmap: NaClDescIoDescMap(,,0x%08"NACL_PRIxPTR","
     628                 :                "0x%08"NACL_PRIxS",0x%x,0x%x,0x%08"NACL_PRIxPTR")\n"),
     629                 :               sysaddr, length, prot, flags, (uintptr_t) offset);
     630           71417 :       map_result = NaClDescIoDescMapAnon(nap->effp,
     631                 :                                          (void *) sysaddr,
     632                 :                                          length,
     633                 :                                          prot,
     634                 :                                          flags,
     635                 :                                          (off_t) offset);
     636           71496 :     } else if (mapping_code) {
     637                 :       /*
     638                 :        * Map a read-only view in trusted memory, ask validator if
     639                 :        * valid without patching; if okay, then map in untrusted
     640                 :        * executable memory.  Fallback to using the dyncode_create
     641                 :        * interface otherwise.
     642                 :        *
     643                 :        * On Windows, threads are already stopped by the
     644                 :        * NaClVmHoleOpeningMu invocation above.
     645                 :        *
     646                 :        * For mmap, stopping threads on Windows is needed to ensure
     647                 :        * that nothing gets allocated into the temporary address space
     648                 :        * hole.  This would otherwise have been particularly dangerous,
     649                 :        * since the hole is in an executable region.  We must abort the
     650                 :        * program if some other trusted thread (or injected thread)
     651                 :        * allocates into this space.  We also need interprocessor
     652                 :        * interrupts to flush the icaches associated other cores, since
     653                 :        * they may contain stale data.  NB: mmap with PROT_EXEC should
     654                 :        * do this for us, since otherwise loading shared libraries in a
     655                 :        * multithreaded environment cannot work in a portable fashion.
     656                 :        * (Mutex locks only ensure dcache coherency.)
     657                 :        *
     658                 :        * For eventual munmap, stopping threads also involve looking at
     659                 :        * their registers to make sure their %rip/%eip/%ip are not
     660                 :        * inside the region being modified (impossible for initial
     661                 :        * insertion).  This is needed because mmap->munmap->mmap could
     662                 :        * cause problems due to scheduler races.
     663                 :        *
     664                 :        * Use NaClDynamicRegionCreate to mark region as allocated.
     665                 :        *
     666                 :        * See NaClElfFileMapSegment in elf_util.c for corresponding
     667                 :        * mmap-based main executable loading.
     668                 :        */
     669               4 :       uintptr_t image_sys_addr;
     670               4 :       NaClValidationStatus validator_status = NaClValidationFailed;
     671               4 :       struct NaClValidationMetadata metadata;
     672               4 :       int sys_ret;  /* syscall return convention */
     673               4 :       int ret;
     674                 : 
     675               4 :       NaClLog(4, "NaClSysMmap: checking descriptor type\n");
     676               4 :       if (NACL_VTBL(NaClDesc, ndp)->typeTag != NACL_DESC_HOST_IO) {
     677               0 :         NaClLog(4, "NaClSysMmap: not supported type, got %d\n",
     678                 :                 NACL_VTBL(NaClDesc, ndp)->typeTag);
     679               0 :         map_result = -NACL_ABI_EINVAL;
     680               0 :         goto cleanup;
     681                 :       }
     682                 : 
     683                 :       /*
     684                 :        * First, try to mmap.  Check if target address range is
     685                 :        * available.  It must be neither in use by NaClText interface,
     686                 :        * nor used by previous mmap'd code.  We record mmap'd code
     687                 :        * regions in the NaClText's data structures to avoid having to
     688                 :        * deal with looking in two data structures.
     689                 :        *
     690                 :        * This mapping is PROT_READ | PROT_WRITE, MAP_PRIVATE so that
     691                 :        * if validation fails in read-only mode, we can re-run the
     692                 :        * validator to patch in place.
     693                 :        */
     694                 : 
     695               8 :       image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)->
     696                 :                         Map)(ndp,
     697               4 :                              NaClDescEffectorTrustedMem(),
     698                 :                              (void *) NULL,
     699                 :                              length,
     700                 :                              NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
     701                 :                              NACL_ABI_MAP_PRIVATE,
     702                 :                              offset);
     703               4 :       if (NaClPtrIsNegErrno(&image_sys_addr)) {
     704               0 :         map_result = image_sys_addr;
     705               0 :         goto cleanup;
     706                 :       }
     707                 : 
     708                 :       /* Ask validator / validation cache */
     709               4 :       NaClMetadataFromNaClDescCtor(&metadata, ndp);
     710              12 :       validator_status = NACL_FI("MMAP_FORCE_MMAP_VALIDATION_FAIL",
     711                 :                                  (*nap->validator->
     712                 :                                   Validate)(usraddr,
     713                 :                                             (uint8_t *) image_sys_addr,
     714                 :                                             length,
     715                 :                                             0,  /* stubout_mode: no */
     716                 :                                             1,  /* readonly_text: yes */
     717                 :                                             nap->cpu_features,
     718                 :                                             &metadata,
     719                 :                                             nap->validation_cache),
     720                 :                                  NaClValidationFailed);
     721               4 :       NaClLog(3, "NaClSysMmap: prot_exec, validator_status %d\n",
     722                 :               validator_status);
     723               4 :       NaClMetadataDtor(&metadata);
     724                 : 
     725               4 :       if (NaClValidationSucceeded == validator_status) {
     726                 :         /*
     727                 :          * Check if target address range is actually available.  It
     728                 :          * must be neither in use by NaClText interface, nor used by
     729                 :          * previous mmap'd code.  We record mmap'd code regions in the
     730                 :          * NaClText's data structures to avoid lo both having to deal
     731                 :          * with looking in two data structures.  We could do this
     732                 :          * first since this is a cheaper check, but it shouldn't
     733                 :          * matter since application errors ought to be rare and we
     734                 :          * shouldn't optimize for error handling, and this makes the
     735                 :          * code simpler (releasing a created region is more code).
     736                 :          */
     737               3 :         NaClXMutexLock(&nap->dynamic_load_mutex);
     738               3 :         ret = NaClDynamicRegionCreate(nap, NaClUserToSys(nap, usraddr), length,
     739                 :                                       1);
     740               3 :         NaClXMutexUnlock(&nap->dynamic_load_mutex);
     741               3 :         if (!ret) {
     742               0 :           NaClLog(3, "NaClSysMmap: PROT_EXEC region"
     743                 :                   " overlaps other dynamic code\n");
     744               0 :           map_result = -NACL_ABI_EINVAL;
     745               0 :           goto cleanup;
     746                 :         }
     747                 :         /*
     748                 :          * Remove scratch mapping.
     749                 :          */
     750               3 :         NaClDescUnmapUnsafe(ndp, (void *) image_sys_addr, length);
     751                 :         /*
     752                 :          * We must succeed in mapping into the untrusted executable
     753                 :          * space, since otherwise it would mean that the temporary
     754                 :          * hole (for Windows) was filled by some other thread, and
     755                 :          * that's unrecoverable.  For Linux and OSX, this should never
     756                 :          * happen, since it's an atomic overmap.
     757                 :          */
     758               3 :         NaClLog(3, "NaClSysMmap: mapping into executable memory\n");
     759               3 :         image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)->
     760                 :                           Map)(ndp,
     761                 :                                nap->effp,
     762                 :                                (void *) sysaddr,
     763                 :                                length,
     764                 :                                NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC,
     765                 :                                NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_FIXED,
     766                 :                                offset);
     767               3 :         if (image_sys_addr != sysaddr) {
     768               0 :           NaClLog(LOG_FATAL,
     769                 :                   "NaClSysMmap: map into executable memory failed:"
     770                 :                   " got 0x%"NACL_PRIxPTR"\n", image_sys_addr);
     771               0 :         }
     772               3 :         map_result = (int32_t) usraddr;
     773               3 :         goto cleanup;
     774                 :       }
     775                 : 
     776               1 :       NaClLog(3,
     777                 :               "NaClSysMmap: did not validate in readonly_text mode;"
     778                 :               " attempting to use dyncode interface.\n");
     779                 : 
     780               1 :       if (holding_app_lock) {
     781               1 :         NaClVmHoleClosingMu(nap);
     782               1 :         NaClXMutexUnlock(&nap->mu);
     783               1 :       }
     784                 : 
     785               2 :       if (NACL_FI("MMAP_STUBOUT_EMULATION", 0, 1)) {
     786               1 :         NaClLog(3, "NaClSysMmap: emulating stubout mode by touching memory\n");
     787               1 :         *(volatile uint8_t *) image_sys_addr =
     788                 :             *(volatile uint8_t *) image_sys_addr;
     789               1 :       }
     790                 : 
     791                 :       /*
     792                 :        * Fallback implementation.  Use the mapped memory as source for
     793                 :        * the dynamic code insertion interface.
     794                 :        */
     795               1 :       sys_ret = NaClTextDyncodeCreate(nap,
     796                 :                                       (uint32_t) usraddr,
     797                 :                                       (uint8_t *) image_sys_addr,
     798                 :                                       (uint32_t) length,
     799                 :                                       NULL);
     800               1 :       if (sys_ret < 0) {
     801               0 :         map_result = sys_ret;
     802               0 :       } else {
     803               1 :         map_result = (int32_t) usraddr;
     804                 :       }
     805                 : 
     806                 : #if NACL_WINDOWS
     807                 :       sys_ret = (*NACL_VTBL(NaClDesc, ndp)->
     808                 :                  UnmapUnsafe)(ndp, (void *) image_sys_addr, length);
     809                 : #else
     810               1 :       sys_ret = munmap((void *) image_sys_addr, length);
     811                 : #endif
     812               1 :       if (0 != sys_ret) {
     813               0 :         NaClLog(LOG_FATAL,
     814                 :                 "NaClSysMmap: could not unmap text at 0x%"NACL_PRIxPTR","
     815                 :                 " length 0x%"NACL_PRIxS", NaCl errno %d\n",
     816                 :                 image_sys_addr, length, -sys_ret);
     817               0 :       }
     818               1 :       goto cleanup_no_locks;
     819                 :     } else {
     820                 :       /*
     821                 :        * This is a fix for Windows, where we cannot pass a size that
     822                 :        * goes beyond the non-page-rounded end of the file.
     823                 :        */
     824              75 :       size_t length_to_map = size_min(length, (size_t) file_bytes);
     825                 : 
     826              75 :       NaClLog(4,
     827                 :               ("NaClSysMmap: (*ndp->Map)(,,0x%08"NACL_PRIxPTR","
     828                 :                "0x%08"NACL_PRIxS",0x%x,0x%x,0x%08"NACL_PRIxPTR")\n"),
     829                 :               sysaddr, length, prot, flags, (uintptr_t) offset);
     830                 : 
     831              75 :       map_result = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
     832                 :                     Map)(ndp,
     833                 :                          nap->effp,
     834                 :                          (void *) sysaddr,
     835                 :                          length_to_map,
     836                 :                          prot,
     837                 :                          flags,
     838                 :                          (off_t) offset);
     839                 :     }
     840                 :     /*
     841                 :      * "Small" negative integers are errno values.  Larger ones are
     842                 :      * virtual addresses.
     843                 :      */
     844           71492 :     if (NaClPtrIsNegErrno(&map_result)) {
     845               2 :       if ((uintptr_t) -NACL_ABI_E_MOVE_ADDRESS_SPACE == map_result) {
     846               0 :         NaClLog(LOG_FATAL,
     847                 :                 ("NaClSysMmap: Map failed, but we"
     848                 :                  " cannot handle address space move, error %"NACL_PRIuS"\n"),
     849                 :                 (size_t) map_result);
     850               0 :       }
     851                 :       /*
     852                 :        * Propagate all other errors to user code.
     853                 :        */
     854               2 :       goto cleanup;
     855                 :     }
     856           71490 :     if (map_result != sysaddr) {
     857               0 :       NaClLog(LOG_FATAL, "system mmap did not honor NACL_ABI_MAP_FIXED\n");
     858               0 :     }
     859           71490 :   }
     860                 :   /*
     861                 :    * If we are mapping beyond the end of the file, we fill this space
     862                 :    * with PROT_NONE pages.
     863                 :    *
     864                 :    * Windows forces us to expose a mixture of 64k and 4k pages, and we
     865                 :    * expose the same mappings on other platforms.  For example,
     866                 :    * suppose untrusted code requests to map 0x40000 bytes from a file
     867                 :    * of extent 0x100.  We will create the following regions:
     868                 :    *
     869                 :    *       0-  0x100  A: Bytes from the file
     870                 :    *   0x100- 0x1000  B: The rest of the 4k page is accessible but undefined
     871                 :    *  0x1000-0x10000  C: The rest of the 64k page is inaccessible (PROT_NONE)
     872                 :    * 0x10000-0x40000  D: Further 64k pages are also inaccessible (PROT_NONE)
     873                 :    *
     874                 :    * On Windows, a single MapViewOfFileEx() call creates A, B and C.
     875                 :    * This call will not accept a size greater than 0x100, so we have
     876                 :    * to create D separately.  The hardware requires B to be accessible
     877                 :    * (whenever A is accessible), but Windows does not allow C to be
     878                 :    * mapped as accessible.  This is unfortunate because it interferes
     879                 :    * with how ELF dynamic linkers usually like to set up an ELF
     880                 :    * object's BSS.
     881                 :    */
     882                 :   /* inaccessible: [length, alloc_rounded_length) */
     883           71490 :   if (length < alloc_rounded_length) {
     884                 :     /*
     885                 :      * On Unix, this maps regions C and D as inaccessible.  On
     886                 :      * Windows, it just maps region D; region C has already been made
     887                 :      * inaccessible.
     888                 :      */
     889              11 :     size_t map_len = alloc_rounded_length - length;
     890              11 :     map_result = MunmapInternal(nap, sysaddr + length, map_len);
     891              11 :     if (map_result != 0) {
     892               0 :       goto cleanup;
     893                 :     }
     894              11 :   }
     895                 : 
     896           71490 :   if (alloc_rounded_length > 0) {
     897          142980 :     NaClVmmapAddWithOverwrite(&nap->mem_map,
     898           71490 :                               NaClSysToUser(nap, sysaddr) >> NACL_PAGESHIFT,
     899                 :                               alloc_rounded_length >> NACL_PAGESHIFT,
     900                 :                               prot,
     901                 :                               flags,
     902                 :                               ndp,
     903                 :                               offset,
     904                 :                               file_size);
     905           71490 :   }
     906                 : 
     907           71490 :   map_result = usraddr;
     908                 : 
     909                 :  cleanup:
     910           71524 :   if (holding_app_lock) {
     911           71501 :     NaClVmHoleClosingMu(nap);
     912           71501 :     NaClXMutexUnlock(&nap->mu);
     913          143025 :   }
     914                 :  cleanup_no_locks:
     915           71525 :   if (NULL != ndp) {
     916              97 :     NaClDescUnref(ndp);
     917              97 :   }
     918                 : 
     919                 :   /*
     920                 :    * Check to ensure that map_result will fit into a 32-bit value. This is
     921                 :    * a bit tricky because there are two valid ranges: one is the range from
     922                 :    * 0 to (almost) 2^32, the other is from -1 to -4096 (our error range).
     923                 :    * For a 32-bit value these ranges would overlap, but if the value is 64-bit
     924                 :    * they will be disjoint.
     925                 :    */
     926           71525 :   if (map_result > UINT32_MAX
     927              31 :       && !NaClPtrIsNegErrno(&map_result)) {
     928               0 :     NaClLog(LOG_FATAL, "Overflow in NaClSysMmap: return address is "
     929                 :                        "0x%"NACL_PRIxPTR"\n", map_result);
     930               0 :   }
     931           71525 :   NaClLog(3, "NaClSysMmap: returning 0x%08"NACL_PRIxPTR"\n", map_result);
     932                 : 
     933           71525 :   return (int32_t) map_result;
     934                 : }
     935                 : 
     936           71486 : int32_t NaClSysMmap(struct NaClAppThread  *natp,
     937           71486 :                     void                  *start,
     938           71486 :                     size_t                length,
     939           71486 :                     int                   prot,
     940           71486 :                     int                   flags,
     941           71486 :                     int                   d,
     942           71486 :                     nacl_abi_off_t        *offp) {
     943           71486 :   struct NaClApp  *nap = natp->nap;
     944           71486 :   int32_t         retval;
     945           71486 :   uintptr_t       sysaddr;
     946           71486 :   nacl_abi_off_t  offset;
     947                 : 
     948           71486 :   NaClLog(3,
     949                 :           "Entered NaClSysMmap(0x%08"NACL_PRIxPTR",0x%"NACL_PRIxS","
     950                 :           "0x%x,0x%x,%d,0x%08"NACL_PRIxPTR")\n",
     951                 :           (uintptr_t) start, length, prot, flags, d, (uintptr_t) offp);
     952                 : 
     953           71486 :   if ((nacl_abi_off_t *) 0 == offp) {
     954                 :     /*
     955                 :      * This warning is really targetted towards trusted code,
     956                 :      * especially tests that didn't notice the argument type change.
     957                 :      * Unfortunatey, zero is a common and legitimate offset value, and
     958                 :      * the compiler will not complain since an automatic type
     959                 :      * conversion works.
     960                 :      */
     961               0 :     NaClLog(LOG_WARNING,
     962                 :             "NaClSysMmap: NULL pointer used"
     963                 :             " for offset in/out argument\n");
     964               0 :     return -NACL_ABI_EINVAL;
     965                 :   }
     966                 : 
     967           71486 :   sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) offp, sizeof offset);
     968           71486 :   if (kNaClBadAddress == sysaddr) {
     969               0 :     NaClLog(3,
     970                 :             "NaClSysMmap: offset in a bad untrusted memory location\n");
     971               0 :     retval = -NACL_ABI_EFAULT;
     972               0 :     goto cleanup;
     973                 :   }
     974           71486 :   offset = *(nacl_abi_off_t volatile *) sysaddr;
     975                 : 
     976           71486 :   NaClLog(4, " offset = 0x%08"NACL_PRIxNACL_OFF"\n", offset);
     977                 : 
     978           71486 :   retval = NaClSysMmapIntern(nap, start, length, prot, flags, d, offset);
     979                 : cleanup:
     980           71484 :   return retval;
     981           71484 : }
     982                 : 
     983                 : #if NACL_WINDOWS
     984                 : static int32_t MunmapInternal(struct NaClApp *nap,
     985                 :                               uintptr_t sysaddr, size_t length) {
     986                 :   uintptr_t addr;
     987                 :   uintptr_t endaddr = sysaddr + length;
     988                 :   uintptr_t usraddr;
     989                 :   for (addr = sysaddr; addr < endaddr; addr += NACL_MAP_PAGESIZE) {
     990                 :     struct NaClVmmapEntry const *entry;
     991                 :     uintptr_t                   page_num;
     992                 :     uintptr_t                   offset;
     993                 : 
     994                 :     usraddr = NaClSysToUser(nap, addr);
     995                 : 
     996                 :     entry = NaClVmmapFindPage(&nap->mem_map, usraddr >> NACL_PAGESHIFT);
     997                 :     if (NULL == entry) {
     998                 :       continue;
     999                 :     }
    1000                 :     NaClLog(3,
    1001                 :             ("NaClSysMunmap: addr 0x%08x, desc 0x%08"NACL_PRIxPTR"\n"),
    1002                 :             addr, (uintptr_t) entry->desc);
    1003                 : 
    1004                 :     page_num = usraddr - (entry->page_num << NACL_PAGESHIFT);
    1005                 :     offset = (uintptr_t) entry->offset + page_num;
    1006                 : 
    1007                 :     if (NULL != entry->desc &&
    1008                 :         offset < (uintptr_t) entry->file_size) {
    1009                 :       if (!UnmapViewOfFile((void *) addr)) {
    1010                 :         NaClLog(LOG_FATAL,
    1011                 :                 ("MunmapInternal: UnmapViewOfFile failed to at addr"
    1012                 :                  " 0x%08"NACL_PRIxPTR", error %d\n"),
    1013                 :                 addr, GetLastError());
    1014                 :       }
    1015                 :       /*
    1016                 :       * Fill the address space hole that we opened
    1017                 :       * with UnmapViewOfFile().
    1018                 :       */
    1019                 :       if (!VirtualAlloc((void *) addr, NACL_MAP_PAGESIZE, MEM_RESERVE,
    1020                 :                         PAGE_READWRITE)) {
    1021                 :         NaClLog(LOG_FATAL, "MunmapInternal: "
    1022                 :                 "failed to fill hole with VirtualAlloc(), error %d\n",
    1023                 :                 GetLastError());
    1024                 :       }
    1025                 :     } else {
    1026                 :       /*
    1027                 :        * Anonymous memory; we just decommit it and thus
    1028                 :        * make it inaccessible.
    1029                 :        */
    1030                 :       if (!VirtualFree((void *) addr,
    1031                 :                        NACL_MAP_PAGESIZE,
    1032                 :                        MEM_DECOMMIT)) {
    1033                 :         int error = GetLastError();
    1034                 :         NaClLog(LOG_FATAL,
    1035                 :                 ("MunmapInternal: Could not VirtualFree MEM_DECOMMIT"
    1036                 :                  " addr 0x%08x, error %d (0x%x)\n"),
    1037                 :                 addr, error, error);
    1038                 :       }
    1039                 :     }
    1040                 :     NaClVmmapRemove(&nap->mem_map,
    1041                 :                     usraddr >> NACL_PAGESHIFT,
    1042                 :                     NACL_PAGES_PER_MAP);
    1043                 :   }
    1044                 :   return 0;
    1045                 : }
    1046                 : #else
    1047           68281 : static int32_t MunmapInternal(struct NaClApp *nap,
    1048           68281 :                               uintptr_t sysaddr, size_t length) {
    1049          136562 :   UNREFERENCED_PARAMETER(nap);
    1050           68281 :   NaClLog(3, "MunmapInternal(0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxS")\n",
    1051                 :           (uintptr_t) sysaddr, length);
    1052                 :   /*
    1053                 :    * Overwrite current mapping with inaccessible, anonymous
    1054                 :    * zero-filled pages, which should be copy-on-write and thus
    1055                 :    * relatively cheap.  Do not open up an address space hole.
    1056                 :    */
    1057           68281 :   if (MAP_FAILED == mmap((void *) sysaddr,
    1058                 :                          length,
    1059                 :                          PROT_NONE,
    1060                 :                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
    1061                 :                          -1,
    1062                 :                          (off_t) 0)) {
    1063               0 :     NaClLog(4, "mmap to put in anonymous memory failed, errno = %d\n", errno);
    1064               0 :     return -NaClXlateErrno(errno);
    1065                 :   }
    1066          136562 :   NaClVmmapRemove(&nap->mem_map,
    1067           68281 :                   NaClSysToUser(nap, (uintptr_t) sysaddr) >> NACL_PAGESHIFT,
    1068                 :                   length >> NACL_PAGESHIFT);
    1069           68281 :   return 0;
    1070           68281 : }
    1071                 : #endif
    1072                 : 
    1073           68277 : int32_t NaClSysMunmap(struct NaClAppThread  *natp,
    1074           68277 :                       void                  *start,
    1075           68277 :                       size_t                length) {
    1076           68277 :   struct NaClApp *nap = natp->nap;
    1077           68277 :   int32_t   retval = -NACL_ABI_EINVAL;
    1078           68277 :   uintptr_t sysaddr;
    1079           68277 :   int       holding_app_lock = 0;
    1080           68277 :   size_t    alloc_rounded_length;
    1081                 : 
    1082           68277 :   NaClLog(3, "Entered NaClSysMunmap(0x%08"NACL_PRIxPTR", "
    1083                 :           "0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxS")\n",
    1084                 :           (uintptr_t) natp, (uintptr_t) start, length);
    1085                 : 
    1086           68277 :   if (!NaClIsAllocPageMultiple((uintptr_t) start)) {
    1087               0 :     NaClLog(4, "start addr not allocation multiple\n");
    1088               0 :     retval = -NACL_ABI_EINVAL;
    1089               0 :     goto cleanup;
    1090                 :   }
    1091           68277 :   if (0 == length) {
    1092                 :     /*
    1093                 :      * Without this check we would get the following inconsistent
    1094                 :      * behaviour:
    1095                 :      *  * On Linux, an mmap() of zero length yields a failure.
    1096                 :      *  * On Mac OS X, an mmap() of zero length returns no error,
    1097                 :      *    which would lead to a NaClVmmapUpdate() of zero pages, which
    1098                 :      *    should not occur.
    1099                 :      *  * On Windows we would iterate through the 64k pages and do
    1100                 :      *    nothing, which would not yield a failure.
    1101                 :      */
    1102               1 :     retval = -NACL_ABI_EINVAL;
    1103               1 :     goto cleanup;
    1104                 :   }
    1105           68276 :   alloc_rounded_length = NaClRoundAllocPage(length);
    1106           68276 :   if (alloc_rounded_length != length) {
    1107              54 :     length = alloc_rounded_length;
    1108              54 :     NaClLog(2, "munmap: rounded length to 0x%"NACL_PRIxS"\n", length);
    1109              54 :   }
    1110           68276 :   sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) start, length);
    1111           68276 :   if (kNaClBadAddress == sysaddr) {
    1112               0 :     NaClLog(4, "munmap: region not user addresses\n");
    1113               0 :     retval = -NACL_ABI_EFAULT;
    1114               0 :     goto cleanup;
    1115                 :   }
    1116                 : 
    1117           68276 :   NaClXMutexLock(&nap->mu);
    1118                 : 
    1119           68276 :   NaClVmHoleOpeningMu(nap);
    1120                 : 
    1121           68276 :   holding_app_lock = 1;
    1122                 : 
    1123                 :   /*
    1124                 :    * User should be unable to unmap any executable pages.  We check here.
    1125                 :    */
    1126           68276 :   if (NaClSysCommonAddrRangeContainsExecutablePages(nap,
    1127                 :                                                     (uintptr_t) start,
    1128                 :                                                     length)) {
    1129               6 :     NaClLog(2, "NaClSysMunmap: region contains executable pages\n");
    1130               6 :     retval = -NACL_ABI_EINVAL;
    1131               6 :     goto cleanup;
    1132                 :   }
    1133                 : 
    1134           68270 :   NaClVmIoPendingCheck_mu(nap,
    1135                 :                           (uint32_t) (uintptr_t) start,
    1136                 :                           (uint32_t) ((uintptr_t) start + length - 1));
    1137                 : 
    1138           68270 :   retval = MunmapInternal(nap, sysaddr, length);
    1139                 : cleanup:
    1140           68277 :   if (holding_app_lock) {
    1141           68276 :     NaClVmHoleClosingMu(nap);
    1142           68276 :     NaClXMutexUnlock(&nap->mu);
    1143           68276 :   }
    1144           68277 :   return retval;
    1145                 : }
    1146                 : 
    1147                 : #if NACL_WINDOWS
    1148                 : static int32_t MprotectInternal(struct NaClApp *nap,
    1149                 :                                 uintptr_t sysaddr, size_t length, int prot) {
    1150                 :   uintptr_t addr;
    1151                 :   uintptr_t endaddr = sysaddr + length;
    1152                 :   uintptr_t usraddr;
    1153                 :   DWORD     flProtect;
    1154                 :   DWORD     flOldProtect;
    1155                 : 
    1156                 :   /*
    1157                 :    * VirtualProtect region cannot span allocations, all addresses must be
    1158                 :    * in one region of memory returned from VirtualAlloc or VirtualAllocEx.
    1159                 :    */
    1160                 :   for (addr = sysaddr; addr < endaddr; addr += NACL_MAP_PAGESIZE) {
    1161                 :     struct NaClVmmapEntry const *entry;
    1162                 :     uintptr_t                   page_num;
    1163                 :     uintptr_t                   offset;
    1164                 : 
    1165                 :     usraddr = NaClSysToUser(nap, addr);
    1166                 : 
    1167                 :     entry = NaClVmmapFindPage(&nap->mem_map, usraddr >> NACL_PAGESHIFT);
    1168                 :     if (NULL == entry) {
    1169                 :       continue;
    1170                 :     }
    1171                 :     NaClLog(3, "MprotectInternal: addr 0x%08x, desc 0x%08"NACL_PRIxPTR"\n",
    1172                 :             addr, (uintptr_t) entry->desc);
    1173                 : 
    1174                 :     page_num = usraddr - (entry->page_num << NACL_PAGESHIFT);
    1175                 :     offset = (uintptr_t) entry->offset + page_num;
    1176                 : 
    1177                 :     if (NULL == entry->desc) {
    1178                 :       flProtect = NaClflProtectMap(prot);
    1179                 : 
    1180                 :       /* Change the page protection */
    1181                 :       if (!VirtualProtect((void *) addr,
    1182                 :                           NACL_MAP_PAGESIZE,
    1183                 :                           flProtect,
    1184                 :                           &flOldProtect)) {
    1185                 :         int error = GetLastError();
    1186                 :         NaClLog(LOG_FATAL, "MprotectInternal: "
    1187                 :                 "failed to change the memory protection with VirtualProtect,"
    1188                 :                 " addr 0x%08x, error %d (0x%x)\n",
    1189                 :                 addr, error, error);
    1190                 :         return -NaClXlateSystemError(error);
    1191                 :       }
    1192                 :     } else if (offset < (uintptr_t) entry->file_size) {
    1193                 :       nacl_off64_t  file_bytes;
    1194                 :       size_t        chunk_size;
    1195                 :       size_t        rounded_chunk_size;
    1196                 :       int           desc_flags;
    1197                 :       char const    *err_msg;
    1198                 : 
    1199                 :       desc_flags = (*NACL_VTBL(NaClDesc, entry->desc)->GetFlags)(entry->desc);
    1200                 :       NaClflProtectAndDesiredAccessMap(prot,
    1201                 :                                        (entry->flags
    1202                 :                                         & NACL_ABI_MAP_PRIVATE) != 0,
    1203                 :                                        (desc_flags & NACL_ABI_O_ACCMODE),
    1204                 :                                        /* flMaximumProtect= */ NULL,
    1205                 :                                        &flProtect,
    1206                 :                                        /* dwDesiredAccess= */ NULL,
    1207                 :                                        &err_msg);
    1208                 :       if (0 == flProtect) {
    1209                 :         /*
    1210                 :          * This shouldn't really happen since we already checked the address
    1211                 :          * space using NaClVmmapCheckExistingMapping, but better be safe.
    1212                 :          */
    1213                 :         NaClLog(LOG_FATAL, "MprotectInternal: %s\n", err_msg);
    1214                 :       }
    1215                 : 
    1216                 :       file_bytes = entry->file_size - offset;
    1217                 :       chunk_size = size_min((size_t) file_bytes, NACL_MAP_PAGESIZE);
    1218                 :       rounded_chunk_size = NaClRoundPage(chunk_size);
    1219                 : 
    1220                 :       NaClLog(4, "VirtualProtect(0x%08x, 0x%"NACL_PRIxS", %x)\n",
    1221                 :               addr, rounded_chunk_size, flProtect);
    1222                 : 
    1223                 :       /* Change the page protection */
    1224                 :       if (!VirtualProtect((void *) addr,
    1225                 :                           rounded_chunk_size,
    1226                 :                           flProtect,
    1227                 :                           &flOldProtect)) {
    1228                 :         int error = GetLastError();
    1229                 :         NaClLog(LOG_FATAL, "MprotectInternal: "
    1230                 :                 "failed to change the memory protection with VirtualProtect()"
    1231                 :                 " addr 0x%08x, error %d (0x%x)\n",
    1232                 :                 addr, error, error);
    1233                 :         return -NaClXlateSystemError(error);
    1234                 :       }
    1235                 :     }
    1236                 :   }
    1237                 : 
    1238                 :   return 0;
    1239                 : }
    1240                 : #else
    1241              33 : static int32_t MprotectInternal(struct NaClApp *nap,
    1242              33 :                                 uintptr_t sysaddr, size_t length, int prot) {
    1243              33 :   uintptr_t               usraddr;
    1244              33 :   uintptr_t               last_page_num;
    1245              33 :   int                     host_prot;
    1246              33 :   struct NaClVmmapIter    iter;
    1247              33 :   struct NaClVmmapEntry   *entry;
    1248                 : 
    1249              33 :   host_prot = NaClProtMap(prot);
    1250                 : 
    1251              33 :   usraddr = NaClSysToUser(nap, sysaddr);
    1252              33 :   last_page_num = (usraddr + length) >> NACL_PAGESHIFT;
    1253                 : 
    1254              33 :   NaClVmmapFindPageIter(&nap->mem_map,
    1255                 :                         usraddr >> NACL_PAGESHIFT,
    1256                 :                         &iter);
    1257              33 :   entry = NaClVmmapIterStar(&iter);
    1258                 : 
    1259              99 :   CHECK(usraddr == (entry->page_num << NACL_PAGESHIFT));
    1260                 : 
    1261             103 :   for (; !NaClVmmapIterAtEnd(&iter) &&
    1262              70 :            (NaClVmmapIterStar(&iter))->page_num < last_page_num;
    1263              37 :        NaClVmmapIterIncr(&iter)) {
    1264              37 :     uintptr_t addr;
    1265              37 :     size_t    entry_len;
    1266                 : 
    1267              37 :     entry = NaClVmmapIterStar(&iter);
    1268                 : 
    1269              37 :     addr = NaClUserToSys(nap, entry->page_num << NACL_PAGESHIFT);
    1270              37 :     entry_len = entry->npages << NACL_PAGESHIFT;
    1271                 : 
    1272              37 :     NaClLog(3, "MprotectInternal: "
    1273                 :             "addr 0x%08"NACL_PRIxPTR", desc 0x%08"NACL_PRIxPTR"\n",
    1274                 :             addr, (uintptr_t) entry->desc);
    1275                 : 
    1276              37 :     if (NULL == entry->desc) {
    1277              14 :       if (0 != mprotect((void *) addr, entry_len, host_prot)) {
    1278               0 :         NaClLog(LOG_FATAL, "MprotectInternal: "
    1279               0 :                 "mprotect on anonymous memory failed, errno = %d\n", errno);
    1280               0 :         return -NaClXlateErrno(errno);
    1281                 :       }
    1282              37 :     } else if (entry->offset < entry->file_size) {
    1283              23 :       nacl_abi_off64_t  file_bytes;
    1284              23 :       size_t            rounded_file_bytes;
    1285              23 :       size_t            prot_len;
    1286                 : 
    1287              23 :       file_bytes = entry->file_size - entry->offset;
    1288              23 :       rounded_file_bytes = NaClRoundPage((size_t) file_bytes);
    1289              23 :       prot_len = size_min(rounded_file_bytes, entry_len);
    1290                 : 
    1291              23 :       if (0 != mprotect((void *) addr, prot_len, host_prot)) {
    1292               0 :         NaClLog(LOG_FATAL, "MprotectInternal: "
    1293               0 :                 "mprotect on file-backed memory failed, errno = %d\n", errno);
    1294               0 :         return -NaClXlateErrno(errno);
    1295                 :       }
    1296              23 :     }
    1297              37 :   }
    1298                 : 
    1299              99 :   CHECK((entry->page_num + entry->npages) == last_page_num);
    1300                 : 
    1301              33 :   return 0;
    1302              33 : }
    1303                 : #endif
    1304                 : 
    1305              38 : int32_t NaClSysMprotectInternal(struct NaClApp  *nap,
    1306              38 :                                 uint32_t        start,
    1307              38 :                                 size_t          length,
    1308              38 :                                 int             prot) {
    1309              38 :   int32_t     retval = -NACL_ABI_EINVAL;
    1310              38 :   uintptr_t   sysaddr;
    1311              38 :   int         holding_app_lock = 0;
    1312                 : 
    1313              38 :   if (!NaClIsAllocPageMultiple((uintptr_t) start)) {
    1314               0 :     NaClLog(4, "mprotect: start addr not allocation multiple\n");
    1315               0 :     retval = -NACL_ABI_EINVAL;
    1316               0 :     goto cleanup;
    1317                 :   }
    1318              38 :   length = NaClRoundAllocPage(length);
    1319              38 :   sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) start, length);
    1320              38 :   if (kNaClBadAddress == sysaddr) {
    1321               0 :     NaClLog(4, "mprotect: region not user addresses\n");
    1322               0 :     retval = -NACL_ABI_EFAULT;
    1323               0 :     goto cleanup;
    1324                 :   }
    1325              38 :   if (0 != (~(NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE) & prot)) {
    1326               0 :     NaClLog(4, "mprotect: prot has other bits than PROT_{READ|WRITE}\n");
    1327               0 :     retval = -NACL_ABI_EACCES;
    1328               0 :     goto cleanup;
    1329                 :   }
    1330                 : 
    1331              38 :   NaClXMutexLock(&nap->mu);
    1332                 : 
    1333              38 :   holding_app_lock = 1;
    1334                 : 
    1335                 :   /*
    1336                 :    * User should be unable to change protection of any executable pages.
    1337                 :    */
    1338              38 :   if (NaClSysCommonAddrRangeContainsExecutablePages(nap,
    1339                 :                                                     (uintptr_t) start,
    1340                 :                                                     length)) {
    1341               0 :     NaClLog(2, "mprotect: region contains executable pages\n");
    1342               0 :     retval = -NACL_ABI_EACCES;
    1343               0 :     goto cleanup;
    1344                 :   }
    1345                 : 
    1346              76 :   if (!NaClVmmapChangeProt(&nap->mem_map,
    1347              38 :                            NaClSysToUser(nap, sysaddr) >> NACL_PAGESHIFT,
    1348                 :                            length >> NACL_PAGESHIFT,
    1349                 :                            prot)) {
    1350               5 :     NaClLog(4, "mprotect: no such region\n");
    1351               5 :     retval = -NACL_ABI_EACCES;
    1352               5 :     goto cleanup;
    1353                 :   }
    1354                 : 
    1355              33 :   NaClVmIoPendingCheck_mu(nap,
    1356                 :                           (uint32_t) (uintptr_t) start,
    1357                 :                           (uint32_t) ((uintptr_t) start + length - 1));
    1358                 : 
    1359              33 :   retval = MprotectInternal(nap, sysaddr, length, prot);
    1360                 : cleanup:
    1361              38 :   if (holding_app_lock) {
    1362              38 :     NaClXMutexUnlock(&nap->mu);
    1363              38 :   }
    1364              38 :   return retval;
    1365                 : }
    1366                 : 
    1367              31 : int32_t NaClSysMprotect(struct NaClAppThread  *natp,
    1368              31 :                         uint32_t              start,
    1369              31 :                         size_t                length,
    1370              31 :                         int                   prot) {
    1371              31 :   struct NaClApp  *nap = natp->nap;
    1372                 : 
    1373              31 :   NaClLog(3, "Entered NaClSysMprotect(0x%08"NACL_PRIxPTR", "
    1374                 :           "0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxS", 0x%x)\n",
    1375                 :           (uintptr_t) natp, (uintptr_t) start, length, prot);
    1376                 : 
    1377              31 :   return NaClSysMprotectInternal(nap, start, length, prot);
    1378                 : }

Generated by: LCOV version 1.7