LCOV - code coverage report
Current view: directory - src/trusted/service_runtime/win - sel_memory.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 145 31 21.4 %
Date: 2014-09-25 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                 : /*
       8                 :  * NaCl Service Runtime memory allocation code
       9                 :  */
      10                 : #include "native_client/src/include/portability.h"
      11                 : #include "native_client/src/include/win/mman.h"
      12                 : 
      13                 : #include <errno.h>
      14                 : #include <windows.h>
      15                 : #include <string.h>
      16                 : 
      17                 : #include "native_client/src/shared/platform/nacl_check.h"
      18                 : #include "native_client/src/shared/platform/nacl_global_secure_random.h"
      19                 : #include "native_client/src/shared/platform/nacl_log.h"
      20                 : #include "native_client/src/shared/platform/win/xlate_system_error.h"
      21                 : 
      22                 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
      23                 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
      24                 : #include "native_client/src/trusted/service_runtime/sel_util.h"
      25                 : 
      26                 : /*
      27                 :  * NaClPageFree: free pages allocated with NaClPageAlloc.
      28                 :  * Must start at allocation granularity (NACL_MAP_PAGESIZE) and
      29                 :  * number of bytes must be a multiple of allocation granularity.
      30                 :  */
      31               1 : void NaClPageFree(void *p, size_t num_bytes) {
      32                 :   void  *end_addr;
      33                 : 
      34               1 :   end_addr = (void *) (((char *) p) + num_bytes);
      35               1 :   while (p < end_addr) {
      36               1 :     if (!VirtualFree(p, 0, MEM_RELEASE)) {
      37               0 :       DWORD err = GetLastError();
      38                 :       NaClLog(LOG_FATAL,
      39                 :               "NaClPageFree: VirtualFree(0x%016"NACL_PRIxPTR
      40                 :               ", 0, MEM_RELEASE) failed "
      41                 :               "with error 0x%X\n",
      42               0 :               (uintptr_t) p, err);
      43                 :     }
      44               1 :     p = (void *) (((char *) p) + NACL_MAP_PAGESIZE);
      45               1 :   }
      46               1 : }
      47                 : 
      48                 : 
      49               1 : int NaClPageAllocAtAddr(void **p, size_t num_bytes) {
      50                 :   SYSTEM_INFO sys_info;
      51                 : 
      52                 :   int         attempt_count;
      53                 : 
      54               1 :   void        *hint = *p;
      55                 :   void        *addr;
      56                 :   void        *end_addr;
      57                 :   void        *chunk;
      58                 :   void        *unroll;
      59                 : 
      60                 :   /*
      61                 :    * We have to allocate every 64KB -- the windows allocation
      62                 :    * granularity -- because VirtualFree will only accept an address
      63                 :    * that was returned by a call to VirtualAlloc.  NB: memory pages
      64                 :    * put into the address space via MapViewOfFile{,Ex} must be
      65                 :    * released by UnmapViewOfFile.  Thus, in order for us to open up a
      66                 :    * hole in the NaCl application's address space to map in a file, we
      67                 :    * must allocate the entire address space in 64KB chunks, so we can
      68                 :    * later pick an arbitrary range of addresses (in 64KB chunks) to
      69                 :    * free up and map in a file later.
      70                 :    *
      71                 :    * First, we verify via GetSystemInfo that the allocation
      72                 :    * granularity matches NACL_MAP_PAGESIZE.
      73                 :    *
      74                 :    * Next, we VirtualAlloc the entire chunk desired.  This essentially
      75                 :    * asks the kernel where there is space in the virtual address
      76                 :    * space.  Then, we free this back, and repeat the allocation
      77                 :    * starting at the returned address, but in 64KB chunks.  If any of
      78                 :    * these smaller allocations fail, we roll back and try again.
      79                 :    */
      80                 : 
      81               1 :   NaClLog(3, "NaClPageAlloc(*, 0x%"NACL_PRIxS")\n", num_bytes);
      82               1 :   GetSystemInfo(&sys_info);
      83               1 :   if (NACL_PAGESIZE != sys_info.dwPageSize) {
      84                 :     NaClLog(2, "page size is 0x%x; expected 0x%x\n",
      85                 :             sys_info.dwPageSize,
      86               0 :             NACL_PAGESIZE);
      87                 :   }
      88               1 :   if (NACL_MAP_PAGESIZE != sys_info.dwAllocationGranularity) {
      89                 :     NaClLog(LOG_ERROR, "allocation granularity is 0x%x; expected 0x%x\n",
      90                 :             sys_info.dwAllocationGranularity,
      91               0 :             NACL_MAP_PAGESIZE);
      92                 :   }
      93                 : 
      94                 :   /*
      95                 :    * Round allocation request up to next NACL_MAP_PAGESIZE.  This is
      96                 :    * assumed to have taken place in NaClPageFree.
      97                 :    */
      98               1 :   num_bytes = NaClRoundAllocPage(num_bytes);
      99                 : 
     100                 :   for (attempt_count = 0;
     101                 :        attempt_count < NACL_MEMORY_ALLOC_RETRY_MAX;
     102               1 :        ++attempt_count) {
     103                 : 
     104               1 :     addr = VirtualAlloc(hint, num_bytes, MEM_RESERVE, PAGE_NOACCESS);
     105               1 :     if (addr == NULL) {
     106                 :       NaClLog(LOG_ERROR,
     107                 :               "NaClPageAlloc: VirtualAlloc(*,0x%"NACL_PRIxS") failed\n",
     108               0 :               num_bytes);
     109               0 :       return -ENOMEM;
     110                 :     }
     111                 :     NaClLog(3,
     112                 :             ("NaClPageAlloc:"
     113                 :              "  VirtualAlloc(*,0x%"NACL_PRIxS")"
     114                 :              " succeeded, 0x%016"NACL_PRIxPTR","
     115                 :              " releasing and re-allocating in 64K chunks....\n"),
     116               1 :             num_bytes, (uintptr_t) addr);
     117               1 :     (void) VirtualFree(addr, 0, MEM_RELEASE);
     118                 :     /*
     119                 :      * We now know [addr, addr + num_bytes) is available in our addr space.
     120                 :      * Get that memory again, but in 64KB chunks.
     121                 :      */
     122               1 :     end_addr = (void *) (((char *) addr) + num_bytes);
     123                 :     for (chunk = addr;
     124                 :          chunk < end_addr;
     125               1 :          chunk = (void *) (((char *) chunk) + NACL_MAP_PAGESIZE)) {
     126                 :       if (NULL == VirtualAlloc(chunk,
     127                 :                                NACL_MAP_PAGESIZE,
     128                 :                                MEM_RESERVE,
     129               1 :                                PAGE_NOACCESS)) {
     130                 :         NaClLog(LOG_ERROR,
     131                 :                 ("NaClPageAlloc: re-allocation failed at "
     132                 :                  "0x%016"NACL_PRIxPTR","
     133                 :                  " error %d\n"),
     134               0 :                 (uintptr_t) chunk, GetLastError());
     135                 :         for (unroll = addr;
     136                 :              unroll < chunk;
     137               0 :              unroll = (void *) (((char *) unroll) + NACL_MAP_PAGESIZE)) {
     138               0 :           (void) VirtualFree(unroll, 0, MEM_RELEASE);
     139               0 :         }
     140               0 :         goto retry;
     141                 :         /* double break */
     142                 :       }
     143               1 :     }
     144                 :     NaClLog(2,
     145                 :             ("NaClPageAlloc: *p = 0x%016"NACL_PRIxPTR","
     146                 :              " returning success.\n"),
     147               1 :             (uintptr_t) addr);
     148               1 :     *p = addr;
     149               1 :     return 0;
     150                 :  retry:
     151               0 :     NaClLog(2, "NaClPageAllocAtAddr: retrying w/o hint\n");
     152               0 :     hint = NULL;
     153               0 :   }
     154                 : 
     155               0 :   return -ENOMEM;
     156               1 : }
     157                 : 
     158               1 : int NaClPageAlloc(void **p, size_t num_bytes) {
     159               1 :   *p = NULL;
     160               1 :   return NaClPageAllocAtAddr(p, num_bytes);
     161               1 : }
     162                 : 
     163                 : /*
     164                 :  * Pick a "hint" address that is random.
     165                 :  */
     166               0 : int NaClPageAllocRandomized(void **p, size_t num_bytes) {
     167                 :   uintptr_t addr;
     168               0 :   int       neg_errno = -ENOMEM;  /* in case we change kNumTries to 0 */
     169                 :   int       tries;
     170               0 :   const int kNumTries = 4;
     171                 : 
     172               0 :   for (tries = 0; tries < kNumTries; ++tries) {
     173                 : #if NACL_HOST_WORDSIZE == 32
     174               0 :     addr = NaClGlobalSecureRngUint32();
     175               0 :     NaClLog(2, "NaClPageAllocRandomized: 0x%"NACL_PRIxPTR"\n", addr);
     176                 :     /*
     177                 :      * Windows permits 2-3 GB of user address space, depending on
     178                 :      * 4-gigabyte tuning (4GT) parameter.  We ask for somewhere in the
     179                 :      * lower 2G.
     180                 :      */
     181                 :     *p = (void *) (addr & ~((uintptr_t) NACL_MAP_PAGESIZE - 1)
     182               0 :                    & ((~(uintptr_t) 0) >> 1));
     183                 : #elif NACL_HOST_WORDSIZE == 64
     184                 :     addr = NaClGlobalSecureRngUint32();
     185                 :     NaClLog(2, "NaClPageAllocRandomized: 0x%"NACL_PRIxPTR"\n", addr);
     186                 :     /*
     187                 :      * 64-bit windows permits 8 TB of user address space, and we keep
     188                 :      * the low 16 bits free (64K alignment), so we just have 43-16=27
     189                 :      * bits of entropy.
     190                 :      */
     191                 :     *p = (void *) ((addr << NACL_MAP_PAGESHIFT)  /* bits [47:16] are random */
     192                 :                    & ((((uintptr_t) 1) << 43) - 1));  /* now bits [42:16] */
     193                 : #else
     194                 : # error "where am i?"
     195                 : #endif
     196                 : 
     197                 :     NaClLog(2, "NaClPageAllocRandomized: hint 0x%"NACL_PRIxPTR"\n",
     198               0 :             (uintptr_t) *p);
     199               0 :     neg_errno = NaClPageAllocAtAddr(p, num_bytes);
     200               0 :     if (0 == neg_errno) {
     201               0 :       break;
     202                 :     }
     203               0 :   }
     204               0 :   if (0 != neg_errno) {
     205                 :     NaClLog(LOG_INFO,
     206                 :             "NaClPageAllocRandomized: failed (%d), dropping hints\n",
     207               0 :             -neg_errno);
     208               0 :     *p = 0;
     209               0 :     neg_errno = NaClPageAllocAtAddr(p, num_bytes);
     210                 :   }
     211               0 :   return neg_errno;
     212               0 : }
     213                 : 
     214                 : static uintptr_t NaClProtectChunkSize(uintptr_t start,
     215               0 :                                       uintptr_t end) {
     216                 :   uintptr_t chunk_end;
     217                 : 
     218               0 :   chunk_end = NaClRoundAllocPage(start + 1);
     219               0 :   if (chunk_end > end) {
     220               0 :     chunk_end = end;
     221                 :   }
     222               0 :   return chunk_end - start;
     223               0 : }
     224                 : 
     225                 : 
     226                 : /*
     227                 :  * Change memory protection.
     228                 :  *
     229                 :  * This is critical to make the text region non-writable, and the data
     230                 :  * region read/write but no exec.  Of course, some kernels do not
     231                 :  * respect the lack of PROT_EXEC.
     232                 :  *
     233                 :  * NB: this function signature is likely to need to change.  In
     234                 :  * particular, when on Windows, if the memory originated from a memory
     235                 :  * mapping, whether the file was MAP_SHARED or MAP_PRIVATE and thus
     236                 :  * whether the dwDesiredAccess used in the MapViewOfFileEx contained
     237                 :  * FILE_MAP_WRITE or FILE_MAP_COPY determines whether the right
     238                 :  * newProtection below should be PAGE_READWRITE or PAGE_WRITECOPY.
     239                 :  * So, we need to have either extend prot to include a PROT_CoW, or
     240                 :  * otherwise some way to look up whether the region had what level of
     241                 :  * access when MapViewOfFileEx was invoked.
     242                 :  *
     243                 :  * For now, we implement a "fallback" mechanism on windows, where if
     244                 :  * PAGE_READWRITE fails, we retry with PAGE_WRITECOPY.  Fortunately,
     245                 :  * this is only needed when setting up memory protection for the BSS
     246                 :  * segment during program loading, when the executable was deemed to
     247                 :  * be safe-for-mapping.
     248                 :  */
     249               0 : int NaClMprotect(void *addr, size_t len, int prot) {
     250                 :   uintptr_t start_addr;
     251                 :   uintptr_t end_addr;
     252                 :   uintptr_t cur_addr;
     253                 :   uintptr_t cur_chunk_size;
     254                 :   DWORD     newProtection, oldProtection;
     255                 :   BOOL      res;
     256                 :   int       xlated_res;
     257                 : 
     258                 :   NaClLog(2, "NaClMprotect(0x%016"NACL_PRIxPTR", 0x%"NACL_PRIxS", 0x%x)\n",
     259               0 :           (uintptr_t) addr, len, prot);
     260                 : 
     261               0 :   if (len == 0) {
     262               0 :     return 0;
     263                 :   }
     264                 : 
     265               0 :   start_addr = (uintptr_t) addr;
     266               0 :   if (!NaClIsPageMultiple(start_addr)) {
     267               0 :     NaClLog(2, "NaClMprotect: start address not a multiple of page size\n");
     268               0 :     return -EINVAL;
     269                 :   }
     270               0 :   if (!NaClIsPageMultiple(len)) {
     271               0 :     NaClLog(2, "NaClMprotect: length not a multiple of page size\n");
     272               0 :     return -EINVAL;
     273                 :   }
     274               0 :   end_addr = start_addr + len;
     275                 : 
     276                 : #define M(P)                                                      \
     277                 :   do {                                                            \
     278                 :     NaClLog(2, "NaClMprotect: newProtection %s, 0x%x\n", #P, P);  \
     279                 :     newProtection = P;                                            \
     280                 :   } while (0)
     281                 : 
     282               0 :   switch (prot) {
     283                 :     case PROT_EXEC: {
     284               0 :       M(PAGE_EXECUTE);
     285               0 :       break;
     286                 :     }
     287                 :     case PROT_EXEC | PROT_READ: {
     288               0 :       M(PAGE_EXECUTE_READ);
     289               0 :       break;
     290                 :     }
     291                 :     case PROT_EXEC | PROT_READ | PROT_WRITE: {
     292               0 :       M(PAGE_EXECUTE_READWRITE);
     293               0 :       break;
     294                 :     }
     295                 :     case PROT_READ: {
     296               0 :       M(PAGE_READONLY);
     297               0 :       break;
     298                 :     }
     299                 :     case PROT_READ | PROT_WRITE: {
     300               0 :       M(PAGE_READWRITE);
     301               0 :       break;
     302                 :     }
     303                 :     case PROT_NONE: {
     304               0 :       M(PAGE_NOACCESS);
     305               0 :       break;
     306                 :     }
     307                 :     default: {
     308               0 :       NaClLog(2, "NaClMprotect: invalid protection mode\n");
     309               0 :       return -EINVAL;
     310                 :     }
     311                 :   }
     312                 : #undef M
     313                 :   /*
     314                 :    * VirtualProtect region cannot span allocations: all addresses from
     315                 :    * [lpAddress, lpAddress+dwSize) must be in one region of memory
     316                 :    * returned from VirtualAlloc or VirtualAllocEx
     317                 :    */
     318                 :   for (cur_addr = start_addr,
     319                 :            cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr);
     320                 :        cur_addr < end_addr;
     321                 :        cur_addr += cur_chunk_size,
     322               0 :            cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr)) {
     323                 :     NaClLog(7,
     324                 :             "NaClMprotect: VirtualProtect(0x%016"NACL_PRIxPTR","
     325                 :             " 0x%"NACL_PRIxPTR", 0x%x, *)\n",
     326               0 :             cur_addr, cur_chunk_size, newProtection);
     327                 : 
     328               0 :     if (newProtection != PAGE_NOACCESS) {
     329                 :       res = VirtualProtect((void *) cur_addr,
     330                 :                            cur_chunk_size,
     331                 :                            newProtection,
     332               0 :                            &oldProtection);
     333                 : 
     334               0 :       if (!res && newProtection == PAGE_READWRITE) {
     335                 :         NaClLog(4,
     336                 :                 "NaClMprotect: VirtualProtect(0x%016"NACL_PRIxPTR","
     337                 :                 " 0x%"NACL_PRIxPTR", 0x%x, *) failed,"
     338                 :                 " trying CoW fallback\n",
     339               0 :                 cur_addr, cur_chunk_size, newProtection);
     340                 :         res = VirtualProtect((void *) cur_addr,
     341                 :                              cur_chunk_size,
     342                 :                              PAGE_WRITECOPY,
     343               0 :                              &oldProtection);
     344               0 :         if (!res) {
     345                 :           NaClLog(4,
     346                 :                   "NaClMprotect: VirtualProtect with PAGE_WRITECOPY failed,"
     347               0 :                   " trying VirtualAlloc\n");
     348                 :         }
     349                 :       }
     350                 : 
     351               0 :       if (!res) {
     352                 :         void *p;
     353                 :         p = VirtualAlloc((void*) cur_addr,
     354                 :                          cur_chunk_size,
     355                 :                          MEM_COMMIT,
     356               0 :                          newProtection);
     357               0 :         if (p != (void*) cur_addr) {
     358                 :           NaClLog(2,
     359                 :                   "NaClMprotect: VirtualAlloc(0x%016"NACL_PRIxPTR","
     360                 :                   " 0x%"NACL_PRIxPTR", MEM_COMMIT, 0x%x) failed\n",
     361               0 :                   cur_addr, cur_chunk_size, newProtection);
     362               0 :           return -NaClXlateSystemError(GetLastError());
     363                 :         }
     364                 :       }
     365               0 :     } else {
     366                 :       /*
     367                 :        * decommit the memory--this has the same effect as setting the protection
     368                 :        * level to PAGE_NOACCESS, with the added benefit of not taking up any
     369                 :        * swap space.
     370                 :        */
     371               0 :       if (!VirtualFree((void *) cur_addr, cur_chunk_size, MEM_DECOMMIT)) {
     372               0 :         xlated_res = NaClXlateSystemError(GetLastError());
     373               0 :         NaClLog(2, "NaClMprotect: VirtualFree failed: 0x%x\n", xlated_res);
     374               0 :         return -xlated_res;
     375                 :       }
     376                 :     }
     377               0 :   }
     378               0 :   NaClLog(2, "NaClMprotect: done\n");
     379               0 :   return 0;
     380               0 : }
     381                 : 
     382               0 : int NaClMadvise(void *start, size_t length, int advice) {
     383                 :   int       err;
     384                 :   uintptr_t start_addr;
     385                 :   uintptr_t end_addr;
     386                 :   uintptr_t cur_addr;
     387                 :   uintptr_t cur_chunk_size;
     388                 : 
     389                 :   /*
     390                 :    * MADV_DONTNEED and MADV_NORMAL are needed
     391                 :    */
     392                 :   NaClLog(5, "NaClMadvise(0x%016"NACL_PRIxPTR", 0x%"NACL_PRIxS", 0x%x)\n",
     393               0 :           (uintptr_t) start, length, advice);
     394               0 :   switch (advice) {
     395                 :     case MADV_DONTNEED:
     396               0 :       start_addr = (uintptr_t) start;
     397               0 :       if (!NaClIsPageMultiple(start_addr)) {
     398                 :         NaClLog(2,
     399               0 :                 "NaClMadvise: start address not a multiple of page size\n");
     400               0 :         return -EINVAL;
     401                 :       }
     402               0 :       if (!NaClIsPageMultiple(length)) {
     403               0 :         NaClLog(2, "NaClMadvise: length not a multiple of page size\n");
     404               0 :         return -EINVAL;
     405                 :       }
     406               0 :       end_addr = start_addr + length;
     407                 :       for (cur_addr = start_addr,
     408                 :                cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr);
     409                 :            cur_addr < end_addr;
     410                 :            cur_addr += cur_chunk_size,
     411               0 :                cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr)) {
     412                 :         NaClLog(7,
     413                 :                 ("NaClMadvise: MADV_DONTNEED"
     414                 :                  " -> VirtualAlloc(0x%016"NACL_PRIxPTR","
     415                 :                  " 0x%"NACL_PRIxPTR", MEM_RESET, PAGE_NOACCESS)\n"),
     416               0 :                 cur_addr, cur_chunk_size);
     417                 : 
     418                 :         /*
     419                 :          * Decommit (but do not release) the page. This allows the kernel to
     420                 :          * release backing store, but does not release the VA space. Should be
     421                 :          * fairly close to the behavior we'd get from the Linux madvise()
     422                 :          * function.
     423                 :          */
     424                 :         if (NULL == VirtualAlloc((void *) cur_addr,
     425               0 :                                  cur_chunk_size, MEM_RESET, PAGE_READONLY)) {
     426               0 :           err = NaClXlateSystemError(GetLastError());
     427               0 :           NaClLog(2, "NaClMadvise: VirtualFree failed: 0x%x\n", err);
     428               0 :           return -err;
     429                 :         }
     430               0 :       }
     431               0 :       break;
     432                 :     case MADV_NORMAL:
     433               0 :       memset(start, 0, length);
     434               0 :       break;
     435                 :     default:
     436               0 :       return -EINVAL;
     437                 :   }
     438               0 :   NaClLog(5, "NaClMadvise: done\n");
     439               0 :   return 0;
     440               0 : }

Generated by: LCOV version 1.7