LCOV - code coverage report
Current view: directory - src/trusted/service_runtime/linux/x86 - nacl_ldt.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 95 60 63.2 %
Date: 2014-07-02 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 local descriptor table manipulation support.
       9                 :  */
      10                 : 
      11                 : #include <asm/ldt.h>
      12                 : #include <stdio.h>
      13                 : #if NACL_ANDROID
      14                 : #include <sys/syscall.h>
      15                 : #endif
      16                 : 
      17                 : #include "native_client/src/shared/platform/nacl_sync.h"
      18                 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
      19                 : #include "native_client/src/trusted/service_runtime/arch/x86/nacl_ldt_x86.h"
      20                 : #include "native_client/src/trusted/service_runtime/arch/x86/sel_ldr_x86.h"
      21                 : 
      22                 : #if NACL_ANDROID
      23                 : /* Android doesn't have standard modify_ldt() function. */
      24                 : int modify_ldt(int func, void *ptr, unsigned long bytecount) {
      25                 :   return syscall(__NR_modify_ldt, func, ptr, bytecount);
      26                 : }
      27                 : #endif
      28                 : 
      29                 : /*
      30                 :  * struct LdtEntry is a structure that is laid out exactly as the segment
      31                 :  * descriptors described in the Intel reference manuals.  This is needed
      32                 :  * because Mac and Windows use this representation in the methods used to
      33                 :  * set and get entries in the local descriptor table (LDT), but use different
      34                 :  * names for the members.  Linux uses a different representation to set, but
      35                 :  * also reads entries back in this format.  It needs to be laid out packed,
      36                 :  * bitwise, little endian.
      37                 :  */
      38                 : struct LdtEntry {
      39                 :   uint16_t limit_00to15;
      40                 :   uint16_t base_00to15;
      41                 : 
      42                 :   unsigned int base_16to23 : 8;
      43                 : 
      44                 :   unsigned int type : 5;
      45                 :   unsigned int descriptor_privilege : 2;
      46                 :   unsigned int present : 1;
      47                 : 
      48                 :   unsigned int limit_16to19 : 4;
      49                 :   unsigned int available : 1;
      50                 :   unsigned int code_64_bit : 1;
      51                 :   unsigned int op_size_32 : 1;
      52                 :   unsigned int granularity : 1;
      53                 : 
      54                 :   unsigned int base_24to31 : 8;
      55                 : };
      56                 : 
      57                 : /*
      58                 :  * The module initializer and finalizer set up a mutex used to guard LDT
      59                 :  * manipulation.
      60                 :  */
      61                 : static struct NaClMutex nacl_ldt_mutex;
      62                 : 
      63             294 : int NaClLdtInitPlatformSpecific(void) {
      64             294 :   if (!NaClMutexCtor(&nacl_ldt_mutex)) {
      65               0 :     return 0;
      66                 :   }
      67                 : 
      68                 :   /*
      69                 :    * Allocate the last LDT entry to force the LDT to grow to its maximum size.
      70                 :    */
      71             294 :   return NaClLdtAllocateSelector(LDT_ENTRIES - 1, 0,
      72                 :                                  NACL_LDT_DESCRIPTOR_DATA, 0, 0, 0);
      73                 : }
      74                 : 
      75              11 : void NaClLdtFiniPlatformSpecific(void) {
      76              11 :   NaClMutexDtor(&nacl_ldt_mutex);
      77              11 : }
      78                 : 
      79                 : /*
      80                 :  * Find a free selector.  Always invoked while holding nacl_ldt_mutex.
      81                 :  */
      82           29978 : static int NaClFindUnusedEntryNumber(void) {
      83           29978 :   int size = sizeof(struct LdtEntry) * LDT_ENTRIES;
      84           29978 :   struct LdtEntry *entries = malloc(size);
      85                 :   int i;
      86                 : 
      87           29978 :   int retval = modify_ldt(0, entries, size);
      88                 : 
      89           29978 :   if (-1 != retval) {
      90           29978 :     retval = -1;  /* In case we don't find any free entry */
      91        33587801 :     for (i = 0; i < LDT_ENTRIES; ++i) {
      92        33587801 :       if (!entries[i].present) {
      93           29978 :         retval = i;
      94           29978 :         break;
      95                 :       }
      96                 :     }
      97                 :   }
      98                 : 
      99           29978 :   free(entries);
     100           29978 :   return retval;
     101                 : }
     102                 : 
     103                 : /*
     104                 :  * Find and allocate an available selector, inserting an LDT entry with the
     105                 :  * appropriate permissions.
     106                 :  */
     107           30272 : uint16_t NaClLdtAllocateSelector(int entry_number,
     108                 :                                  int size_is_in_pages,
     109                 :                                  NaClLdtDescriptorType type,
     110                 :                                  int read_exec_only,
     111                 :                                  void* base_addr,
     112                 :                                  uint32_t size_minus_one) {
     113                 :   struct user_desc ud;
     114                 :   int retval;
     115                 : 
     116           30272 :   NaClXMutexLock(&nacl_ldt_mutex);
     117                 : 
     118           30272 :   if (-1 == entry_number) {
     119                 :     /* -1 means caller did not specify -- allocate */
     120           29978 :     entry_number = NaClFindUnusedEntryNumber();
     121                 : 
     122           29978 :     if (-1 == entry_number) {
     123                 :       /*
     124                 :        * No free entries were available.
     125                 :        */
     126               0 :       goto alloc_error;
     127                 :     }
     128                 :   }
     129           30272 :   ud.entry_number = entry_number;
     130                 : 
     131           30272 :   switch (type) {
     132                 :     case NACL_LDT_DESCRIPTOR_DATA:
     133           29993 :       ud.contents = MODIFY_LDT_CONTENTS_DATA;
     134           29993 :       break;
     135                 :     case NACL_LDT_DESCRIPTOR_CODE:
     136             279 :       ud.contents = MODIFY_LDT_CONTENTS_CODE;
     137             279 :       break;
     138                 :     default:
     139               0 :       goto alloc_error;
     140                 :   }
     141           30272 :   ud.read_exec_only = read_exec_only;
     142           30272 :   ud.seg_32bit = 1;
     143           30272 :   ud.seg_not_present = 0;
     144           30272 :   ud.useable = 1;
     145                 : 
     146           30272 :   if (size_is_in_pages && ((unsigned long) base_addr & 0xfff)) {
     147                 :     /*
     148                 :      * The base address needs to be page aligned.
     149                 :      */
     150               0 :     goto alloc_error;
     151                 :   };
     152           30272 :   ud.base_addr = (unsigned long) base_addr;
     153                 : 
     154           30272 :   if (size_minus_one > 0xfffff) {
     155                 :     /*
     156                 :      * If size is in pages, no more than 2**20 pages can be protected.
     157                 :      * If size is in bytes, no more than 2**20 bytes can be protected.
     158                 :      */
     159               0 :     goto alloc_error;
     160                 :   }
     161           30272 :   ud.limit = size_minus_one;
     162           30272 :   ud.limit_in_pages = size_is_in_pages;
     163                 : 
     164                 :   /*
     165                 :    * Install the LDT entry.
     166                 :    */
     167           30272 :   retval = modify_ldt(1, &ud, sizeof ud);
     168           30272 :   if (-1 == retval) {
     169               0 :     goto alloc_error;
     170                 :   }
     171                 : 
     172                 :   /*
     173                 :    * Return an LDT selector with a requested privilege level of 3.
     174                 :    */
     175           30272 :   NaClXMutexUnlock(&nacl_ldt_mutex);
     176           30272 :   return (ud.entry_number << 3) | 0x7;
     177                 : 
     178                 :   /*
     179                 :    * All error returns go through this epilog.
     180                 :    */
     181                 :  alloc_error:
     182               0 :   NaClXMutexUnlock(&nacl_ldt_mutex);
     183               0 :   return 0;
     184                 : }
     185                 : 
     186                 : /*
     187                 :  * Allocates a selector whose size is specified in pages.
     188                 :  * Page granular protection requires that the start address be page-aligned.
     189                 :  */
     190             558 : uint16_t NaClLdtAllocatePageSelector(NaClLdtDescriptorType type,
     191                 :                                      int read_exec_only,
     192                 :                                      void* base_addr,
     193                 :                                      uint32_t size_in_pages) {
     194             558 :   return NaClLdtAllocateSelector(-1, 1, type, read_exec_only, base_addr,
     195                 :                                  size_in_pages - 1);
     196                 : }
     197                 : 
     198                 : /*
     199                 :  * Allocates a selector whose size is specified in bytes.
     200                 :  */
     201           29420 : uint16_t NaClLdtAllocateByteSelector(NaClLdtDescriptorType type,
     202                 :                                      int read_exec_only,
     203                 :                                      void* base_addr,
     204                 :                                      uint32_t size_in_bytes) {
     205           29420 :   return NaClLdtAllocateSelector(-1, 0, type, read_exec_only, base_addr,
     206                 :                                  size_in_bytes - 1);
     207                 : }
     208                 : 
     209                 : /*
     210                 :  * Change a selector whose size is specified in pages.
     211                 :  * Page granular protection requires that the start address be page-aligned.
     212                 :  */
     213               0 : uint16_t NaClLdtChangePageSelector(int32_t entry_number,
     214                 :                                    NaClLdtDescriptorType type,
     215                 :                                    int read_exec_only,
     216                 :                                    void* base_addr,
     217                 :                                    uint32_t size_in_pages) {
     218               0 :   if ((uint32_t) entry_number >= LDT_ENTRIES) {
     219               0 :     return 0;
     220                 :   }
     221               0 :   return NaClLdtAllocateSelector(entry_number, 1, type, read_exec_only,
     222                 :                                  base_addr, size_in_pages - 1);
     223                 : }
     224                 : 
     225                 : /*
     226                 :  * Change a selector whose size is specified in bytes.
     227                 :  */
     228               0 : uint16_t NaClLdtChangeByteSelector(int32_t entry_number,
     229                 :                                    NaClLdtDescriptorType type,
     230                 :                                    int read_exec_only,
     231                 :                                    void* base_addr,
     232                 :                                    uint32_t size_in_bytes) {
     233               0 :   if ((uint32_t) entry_number >= LDT_ENTRIES) {
     234               0 :     return 0;
     235                 :   }
     236               0 :   return NaClLdtAllocateSelector(entry_number, 0, type, read_exec_only,
     237                 :                                  base_addr, size_in_bytes - 1);
     238                 : }
     239                 : 
     240                 : 
     241               0 : void NaClLdtPrintSelector(uint16_t selector) {
     242                 :   /* type_name converts the segment type into print name */
     243                 :   static const char* type_name[] = {
     244                 :     "data read only",
     245                 :     "data read_only accessed",
     246                 :     "data read write",
     247                 :     "data read write accessed",
     248                 :     "data read expand",
     249                 :     "data read expand accessed",
     250                 :     "data read write expand",
     251                 :     "data read write expand accessed",
     252                 :     "code execute",
     253                 :     "code execute accessed",
     254                 :     "code execute read",
     255                 :     "code execute read accessed",
     256                 :     "code execute conforming",
     257                 :     "code execute conforming accessed",
     258                 :     "code execute read conforming",
     259                 :     "code execute read conforming accessed"
     260                 :   };
     261                 : 
     262                 :   struct LdtEntry entries[LDT_ENTRIES];
     263                 :   struct LdtEntry entry;
     264               0 :   int retval = modify_ldt(0, entries, sizeof(entries));
     265               0 :   if (-1 == retval) {
     266               0 :     return;
     267                 :   }
     268               0 :   entry = entries[selector >> 3];
     269               0 :   printf("DESCRIPTOR for selector %04x\n", selector);
     270                 :   /* create functions to do base, limit, and type */
     271               0 :   printf("  base        %08x\n", (entry.base_24to31 << 24)
     272               0 :          | (entry.base_16to23 << 16) | entry.base_00to15);
     273               0 :   printf("  limit       %08x%s\n",
     274               0 :          (((entry.limit_16to19 << 16) | entry.limit_00to15)
     275               0 :           << (entry.granularity ? 12 : 0)),
     276               0 :          (entry.granularity ? " (page granularity)" : ""));
     277               0 :   printf("  type        %s, %s\n", ((entry.type & 0x10) ? "user" : "system"),
     278               0 :          type_name[entry.type & 0xf]);
     279               0 :   printf("  privilege   %d\n", entry.descriptor_privilege);
     280               0 :   printf("  present %s\n", (entry.present ? "yes" : "no"));
     281               0 :   printf("  available %s\n", (entry.available ? "yes" : "no"));
     282               0 :   printf("  64-bit code %s\n", (entry.code_64_bit ? "yes" : "no"));
     283               0 :   printf("  op size %s\n", (entry.op_size_32 ? "32" : "16"));
     284                 : }
     285                 : 
     286                 : /*
     287                 :  * Mark a selector as available for future reuse.
     288                 :  */
     289           29365 : void NaClLdtDeleteSelector(uint16_t selector) {
     290                 :   struct user_desc ud;
     291           29365 :   ud.entry_number = selector >> 3;
     292           29365 :   ud.seg_not_present = 1;
     293           29365 :   ud.base_addr = 0;
     294           29365 :   ud.limit = 0;
     295           29365 :   ud.limit_in_pages = 0;
     296           29365 :   ud.read_exec_only = 0;
     297           29365 :   ud.seg_32bit = 0;
     298           29365 :   ud.useable = 0;
     299           29365 :   ud.contents = MODIFY_LDT_CONTENTS_DATA;
     300           29365 :   NaClXMutexLock(&nacl_ldt_mutex);
     301           29365 :   modify_ldt(1, &ud, sizeof ud);
     302           29365 :   NaClXMutexUnlock(&nacl_ldt_mutex);
     303           29360 : }

Generated by: LCOV version 1.7