LCOV - code coverage report
Current view: directory - src/shared/platform/posix - nacl_find_addrsp.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 44 31 70.5 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2010 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 <stdio.h>
       8                 : #include <stdint.h>
       9                 : #include <sys/mman.h>
      10                 : #include <sys/types.h>
      11                 : 
      12                 : #include "native_client/src/shared/platform/nacl_find_addrsp.h"
      13                 : 
      14                 : #include "native_client/src/include/nacl_base.h"
      15                 : #include "native_client/src/include/nacl_platform.h"
      16                 : #include "native_client/src/include/portability.h"
      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                 : 
      21                 : 
      22                 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
      23                 : /*
      24                 :  * The x86-64 (currently) permits only a 48 bit virtual address
      25                 :  * space (and requires that the upper 64-48=16 bits is sign extended
      26                 :  * from bit 47).  Additionally, the linux kernel disallows negative
      27                 :  * addresses for user-space code.  So instead of 48, we get a
      28                 :  * maximum of 47 usable bits of virtual address.
      29                 :  */
      30                 : # define NUM_USER_ADDRESS_BITS 47
      31                 : /*
      32                 :  * Don't assume __LP64__ is defined, even though we're POSIX and thus
      33                 :  * we are compiling with gcc or gcc-compatible compilers.
      34                 :  */
      35                 : # define NACL_POINTER_SIZE 64
      36                 : 
      37                 : #elif (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32) || \
      38                 :       NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
      39                 : /*
      40                 :  * The x86-32 / ARM architectures use a 32-bit address space, but it is common
      41                 :  * for the kernel to reserve the high 1 or 2 GB. In order to allow the start
      42                 :  * address to be in the 2GB-3GB range, we choose from all possible 32-bit
      43                 :  * addresses.
      44                 :  */
      45                 : # define NUM_USER_ADDRESS_BITS 32
      46                 : # define NACL_POINTER_SIZE 32
      47                 : 
      48                 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
      49                 : /*
      50                 :  * The MIPS architecture uses a 32-bit address space, but it is common for the
      51                 :  * kernel to reserve the high 1 or 2 GB. The current MIPS platforms we target
      52                 :  * will not be making use of the address space in the 2GB-4GB range.
      53                 :  */
      54                 : # define NUM_USER_ADDRESS_BITS 31
      55                 : # define NACL_POINTER_SIZE 32
      56                 : 
      57                 : #else
      58                 : # error "Is this a 32-bit or 64-bit architecture?"
      59                 : #endif
      60                 : 
      61                 : #define NACL_VALGRIND_MAP_FAILED_COUNT  10000
      62                 : 
      63                 : /* bool */
      64             380 : int NaClFindAddressSpaceRandomized(uintptr_t *addr, size_t memory_size,
      65             380 :                                    int max_tries) {
      66             380 :   void *map_addr;
      67             380 :   int tries_remaining;
      68                 :   /*
      69                 :    * Mask for keeping the low order NUM_USER_ADDRESS_BITS of a randomly
      70                 :    * generated address.
      71                 :    */
      72             380 :   uintptr_t addr_mask;
      73             380 :   uintptr_t suggested_addr;
      74             380 :   int mmap_type;
      75                 : 
      76             380 :   size_t map_failed_count_left = NACL_VALGRIND_MAP_FAILED_COUNT;
      77                 : 
      78            1140 :   CHECK(max_tries >= 0);
      79             380 :   NaClLog(4,
      80                 :           "NaClFindAddressSpaceRandomized: looking for %"NACL_PRIxS" bytes\n",
      81                 :           memory_size);
      82             380 :   NaClLog(4, "NaClFindAddressSpaceRandomized: max %d tries\n", max_tries);
      83                 : 
      84                 :   if (NACL_ARCH(NACL_BUILD_ARCH) != NACL_mips) {
      85                 :     /* 4kB pages */
      86             380 :     addr_mask = ~(((uintptr_t) 1 << 12) - 1);
      87             380 :     mmap_type = MAP_PRIVATE;
      88                 :   } else {
      89                 :     /*
      90                 :      * 8kB pages in userspace memory region.
      91                 :      * Apart from system page size, MIPS shared memory mask in kernel can depend
      92                 :      * on dcache attributes. Ideally, we could use kernel constant SHMLBA, but
      93                 :      * it is too large. Common case is that 8kB is sufficient.
      94                 :      * As shared memory mask alignment is more rigid on MIPS, we need to pass
      95                 :      * MAP_SHARED type to mmap, so it can return value applicable both for
      96                 :      * private and shared mapping.
      97                 :      */
      98                 :     addr_mask = ~(((uintptr_t) 1 << 13) - 1);
      99                 :     mmap_type = MAP_SHARED;
     100                 :   }
     101                 :   /*
     102                 :    * We cannot just do
     103                 :    *
     104                 :    *   if (NUM_USER_ADDRESS_BITS < sizeof(uintptr_t) * 8) {
     105                 :    *     addr_mask &= (((uintptr_t) 1 << NUM_USER_ADDRESS_BITS) - 1);
     106                 :    *   }
     107                 :    *
     108                 :    * since when NUM_USER_ADDRESS_BITS is defined to be 32, for
     109                 :    * example, and sizeof(uintptr_t) is 4, then even though the
     110                 :    * constant expression NUM_USER_ADDRESS_BITS < sizeof(uintptr_t) * 8
     111                 :    * is false, the compiler still parses the block of code controlled
     112                 :    * by the conditional.  And the warning for shifting by too many
     113                 :    * bits would be produced because we'd venture into
     114                 :    * undefined-behavior territory:
     115                 :    *
     116                 :    *   6.5.7.3: "The integer promotions are performed on each of the
     117                 :    *   operands. The type of the result is that of the promoted left
     118                 :    *   operand. If the value of the right operand is negative or is
     119                 :    *   greater than or equal to the width of the promoted left
     120                 :    *   operand, the behavior is undefined."
     121                 :    *
     122                 :    * Since we compile with -Wall and -Werror, this would lead to a
     123                 :    * build failure.
     124                 :    */
     125                 : #if NACL_POINTER_SIZE > NUM_USER_ADDRESS_BITS
     126             380 :   addr_mask &= (((uintptr_t) 1 << NUM_USER_ADDRESS_BITS) - 1);
     127                 : #endif
     128                 : 
     129             380 :   tries_remaining = max_tries;
     130             380 :   for (;;) {
     131                 : #if NACL_POINTER_SIZE > 32
     132             380 :     suggested_addr = (((uintptr_t) NaClGlobalSecureRngUint32() << 32) |
     133             380 :                       (uintptr_t) NaClGlobalSecureRngUint32());
     134                 : #else
     135                 :     suggested_addr = ((uintptr_t) NaClGlobalSecureRngUint32());
     136                 : #endif
     137             380 :     suggested_addr &= addr_mask;
     138                 : 
     139             380 :     NaClLog(4,
     140                 :             ("NaClFindAddressSpaceRandomized: non-MAP_FAILED tries"
     141                 :              " remaining %d, hint addr %"NACL_PRIxPTR"\n"),
     142                 :             tries_remaining,
     143                 :             suggested_addr);
     144             380 :     map_addr = mmap((void *) suggested_addr, memory_size,
     145                 :                     PROT_NONE,
     146                 :                     MAP_ANONYMOUS | MAP_NORESERVE | mmap_type,
     147                 :                     -1,
     148                 :                     0);
     149                 :     /*
     150                 :      * On most POSIX systems, the mmap syscall, without MAP_FIXED,
     151                 :      * will use the first parameter (actual argument suggested_addr)
     152                 :      * as a hint for where to find a hole in the address space for the
     153                 :      * new memory -- often as a starting point for a search.
     154                 :      *
     155                 :      * However, when Valgrind is used (3.7.0 to 3.8.1 at least), the
     156                 :      * mmap syscall is replaced with wrappers which do not behave
     157                 :      * correctly: the Valgrind-provided mmap replacement will return
     158                 :      * MAP_FAILED instead of ignoring the hint, until the "Warning:
     159                 :      * set address range perms: large range [0x...., 0x....]
     160                 :      * (noaccess)" message to warn about large allocations shows up.
     161                 :      * So in order for this code to not fail when run under Valgrind,
     162                 :      * we have to ignore MAP_FAILED and not count these attempts
     163                 :      * against randomization failures.
     164                 :      */
     165             380 :     if (MAP_FAILED == map_addr) {
     166               0 :       if (--map_failed_count_left != 0) {
     167               0 :         NaClLog(LOG_INFO, "NaClFindAddressSpaceRandomized: MAP_FAILED\n");
     168               0 :       } else {
     169               0 :         NaClLog(LOG_ERROR,
     170                 :                 "NaClFindAddressSpaceRandomized: too many MAP_FAILED\n");
     171               0 :         return 0;
     172                 :       }
     173               0 :     } else {
     174                 :       /*
     175                 :        * mmap will use a system-dependent algorithm to find a starting
     176                 :        * address if the hint location cannot work, e.g., if there is
     177                 :        * already memory mapped there.  We do not trust that algorithm
     178                 :        * to provide any randomness in the high-order bits, so we only
     179                 :        * accept allocations that match the requested high-order bits
     180                 :        * exactly.  If the algorithm is to scan the address space
     181                 :        * starting near the hint address, then it would be acceptable;
     182                 :        * if it is to use a default algorithm that is independent of
     183                 :        * the supplied hint, then it would not be.
     184                 :        */
     185             380 :       if ((addr_mask & ((uintptr_t) map_addr)) == suggested_addr) {
     186             380 :         NaClLog(5,
     187                 :                 "NaClFindAddressSpaceRandomized: high order bits matched.\n");
     188                 :         /* success */
     189             380 :         break;
     190                 :       }
     191                 : 
     192               0 :       if (0 == tries_remaining--) {
     193                 :         /* give up on retrying, and just use what was returned */
     194               0 :         NaClLog(5, "NaClFindAddressSpaceRandomized: last try, taking as is.\n");
     195               0 :         break;
     196                 :       }
     197                 :       /*
     198                 :        * Remove undesirable mapping location before trying again.
     199                 :        */
     200               0 :       if (-1 == munmap(map_addr, memory_size)) {
     201               0 :         NaClLog(LOG_FATAL,
     202                 :                 "NaClFindAddressSpaceRandomized: could not unmap non-random"
     203                 :                 " memory\n");
     204               0 :       }
     205                 :     }
     206               0 :   }
     207             380 :   NaClLog(4,
     208                 :           "NaClFindAddressSpaceRandomized: got addr %"NACL_PRIxPTR"\n",
     209                 :           (uintptr_t) map_addr);
     210             380 :   *addr = (uintptr_t) map_addr;
     211             380 :   return 1;
     212             380 : }
     213                 : 
     214             103 : int NaClFindAddressSpace(uintptr_t *addr, size_t memory_size) {
     215             103 :   return NaClFindAddressSpaceRandomized(addr, memory_size, 0);
     216                 : }

Generated by: LCOV version 1.7