LCOV - code coverage report
Current view: directory - src/trusted/validator - ncfileutil.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 209 111 53.1 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : /*
       8                 :  * ncfileutil.c - open an executable file. FOR TESTING ONLY.
       9                 :  */
      10                 : 
      11                 : #include "native_client/src/trusted/validator/ncfileutil.h"
      12                 : 
      13                 : #include "native_client/src/include/portability.h"
      14                 : 
      15                 : #include <stdarg.h>
      16                 : #include <stdio.h>
      17                 : #include <stdlib.h>
      18                 : #include <string.h>
      19                 : #include <assert.h>
      20                 : #include <errno.h>
      21                 : #include <fcntl.h>
      22                 : #include <sys/types.h>
      23                 : 
      24                 : #include "native_client/src/include/portability_io.h"
      25                 : 
      26                 : /* This module is intended for testing use only, not for production use */
      27                 : /* in sel_ldr. To prevent unintended production usage, define a symbol  */
      28                 : /* that will cause a load-time error for sel_ldr.                       */
      29                 : int gNaClValidateImage_foo = 0;
      30               0 : void NaClValidateImage(void) { gNaClValidateImage_foo += 1; }
      31                 : 
      32                 : static void NcLoadFilePrintError(const char* format, ...)
      33                 :     ATTRIBUTE_FORMAT_PRINTF(1, 2);
      34                 : 
      35                 : /* Define the default print error function to use for this module. */
      36               0 : static void NcLoadFilePrintError(const char* format, ...) {
      37               0 :   va_list ap;
      38               0 :   va_start(ap, format);
      39               0 :   vfprintf(stderr, format, ap);
      40               0 :   va_end(ap);
      41               0 : }
      42                 : 
      43                 : /***********************************************************************/
      44                 : /* THIS ROUTINE IS FOR DEBUGGING/TESTING ONLY, NOT FOR SECURE RUNTIME  */
      45                 : /* ALL PAGES ARE LEFT WRITEABLE BY THIS LOADER.                        */
      46                 : /***********************************************************************/
      47                 : /* Loading a NC executable from a host file */
      48               4 : static off_t readat(ncfile* ncf, const int fd,
      49               4 :                     void *buf, const off_t sz, const off_t at) {
      50                 :   /* TODO(karl) fix types for off_t and size_t so that the work for 64-bits */
      51               4 :   int sofar = 0;
      52               4 :   int nread;
      53               4 :   char *cbuf = (char *) buf;
      54                 : 
      55               4 :   if (0 > lseek(fd, (long) at, SEEK_SET)) {
      56               0 :     ncf->error_fn("readat: lseek failed\n");
      57               0 :     return -1;
      58                 :   }
      59                 : 
      60                 :   /* TODO(robertm) Figure out if O_BINARY flag fixes this. */
      61                 :   /* Strangely this loop is needed on Windows. It seems the read()   */
      62                 :   /* implementation doesn't always return as many bytes as requested */
      63                 :   /* so you have to keep on trying.                                  */
      64               4 :   do {
      65               4 :     nread = read(fd, &cbuf[sofar], sz - sofar);
      66               4 :     if (nread <= 0) {
      67               0 :       ncf->error_fn("readat: read failed\n");
      68               0 :       return -1;
      69                 :     }
      70               4 :     sofar += nread;
      71               8 :   } while (sz != sofar);
      72               4 :   return nread;
      73               4 : }
      74                 : 
      75               0 : static const char* GetEiClassName(unsigned char c) {
      76               0 :   if (c == ELFCLASS32) {
      77               0 :     return "(32 bit executable)";
      78               0 :   } else if (c == ELFCLASS64) {
      79               0 :     return "(64 bit executable)";
      80                 :   } else {
      81               0 :     return "(invalid class)";
      82                 :   }
      83               0 : }
      84                 : 
      85               1 : static int nc_load(ncfile *ncf, int fd) {
      86                 :   union {
      87                 :     Elf32_Ehdr h32;
      88                 : #if NACL_TARGET_SUBARCH == 64
      89                 :     Elf64_Ehdr h64;
      90                 : #endif
      91               1 :   } h;
      92               1 :   Elf_Half phnum;
      93               1 :   Elf_Half shnum;
      94               1 :   Elf_Off phoff;
      95               1 :   Elf_Off shoff;
      96               1 :   ssize_t nread;
      97               1 :   Elf_Addr vmemlo, vmemhi;
      98               1 :   size_t shsize, phsize;
      99               1 :   int i;
     100                 : 
     101                 :   /* Read and check the ELF header */
     102               1 :   nread = readat(ncf, fd, &h, sizeof(h), 0);
     103               2 :   if (nread < 0 || (size_t) nread < sizeof(h)) {
     104               0 :     ncf->error_fn("nc_load(%s): could not read ELF header", ncf->fname);
     105               0 :     return -1;
     106                 :   }
     107                 : 
     108                 :   /* do a bunch of sanity checks */
     109               1 :   if (memcmp(h.h32.e_ident, ELFMAG, SELFMAG)) {
     110               0 :     ncf->error_fn("nc_load(%s): bad magic number", ncf->fname);
     111               0 :     return -1;
     112                 :   }
     113                 : 
     114                 : #if NACL_TARGET_SUBARCH == 64
     115               1 :   if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
     116               0 :     if (h.h64.e_phoff > 0xffffffffU) {
     117               0 :       ncf->error_fn("nc_load(%s): e_phoff overflows 32 bits\n", ncf->fname);
     118               0 :       return -1;
     119                 :     }
     120               0 :     if (h.h64.e_shoff > 0xffffffffU) {
     121               0 :       ncf->error_fn("nc_load(%s): e_shoff overflows 32 bits\n", ncf->fname);
     122               0 :       return -1;
     123                 :     }
     124               0 :     phoff = (Elf32_Off) h.h64.e_phoff;
     125               0 :     shoff = (Elf32_Off) h.h64.e_shoff;
     126               0 :     phnum = h.h64.e_phnum;
     127               0 :     shnum = h.h64.e_shnum;
     128               0 :   } else
     129                 : #endif
     130                 :   {
     131               1 :     if (h.h32.e_ident[EI_CLASS] == ELFCLASS32) {
     132               1 :       phoff = h.h32.e_phoff;
     133               1 :       shoff = h.h32.e_shoff;
     134               1 :       phnum = h.h32.e_phnum;
     135               1 :       shnum = h.h32.e_shnum;
     136               1 :     } else {
     137               0 :       ncf->error_fn("nc_load(%s): bad EI CLASS %d %s\n", ncf->fname,
     138                 :                     h.h32.e_ident[EI_CLASS],
     139               0 :                     GetEiClassName(h.h32.e_ident[EI_CLASS]));
     140               0 :       return -1;
     141                 :     }
     142                 :   }
     143                 : 
     144                 :   /* Read the program header table */
     145               2 :   if (phnum <= 0 || phnum > kMaxPhnum) {
     146               0 :     ncf->error_fn("nc_load(%s): e_phnum %d > kMaxPhnum %d\n",
     147                 :                   ncf->fname, phnum, kMaxPhnum);
     148               0 :     return -1;
     149                 :   }
     150               1 :   ncf->phnum = phnum;
     151               1 :   ncf->pheaders = (Elf_Phdr *)calloc(phnum, sizeof(Elf_Phdr));
     152               1 :   if (NULL == ncf->pheaders) {
     153               0 :     ncf->error_fn("nc_load(%s): calloc(%d, %"NACL_PRIdS") failed\n",
     154                 :                   ncf->fname, phnum, sizeof(Elf_Phdr));
     155               0 :     return -1;
     156                 :   }
     157               1 :   phsize = phnum * sizeof(*ncf->pheaders);
     158                 : #if NACL_TARGET_SUBARCH == 64
     159               1 :   if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
     160                 :     /*
     161                 :      * Read 64-bit program headers and convert them.
     162                 :      */
     163               0 :     Elf64_Phdr phdr64[kMaxPhnum];
     164               0 :     nread = readat(ncf, fd, phdr64, (off_t) (phnum * sizeof(phdr64[0])),
     165                 :                    (off_t) phoff);
     166               0 :     if (nread < 0 || (size_t) nread < phsize) return -1;
     167               0 :     for (i = 0; i < phnum; ++i) {
     168               0 :       if (phdr64[i].p_offset > 0xffffffffU ||
     169                 :           phdr64[i].p_vaddr > 0xffffffffU ||
     170                 :           phdr64[i].p_paddr > 0xffffffffU ||
     171                 :           phdr64[i].p_filesz > 0xffffffffU ||
     172                 :           phdr64[i].p_memsz > 0xffffffffU ||
     173                 :           phdr64[i].p_align > 0xffffffffU) {
     174               0 :         ncf->error_fn("nc_load(%s): phdr[%d] fields overflow 32 bits\n",
     175                 :                       ncf->fname, i);
     176               0 :         return -1;
     177                 :       }
     178               0 :       ncf->pheaders[i].p_type = phdr64[i].p_type;
     179               0 :       ncf->pheaders[i].p_flags = phdr64[i].p_flags;
     180               0 :       ncf->pheaders[i].p_offset = (Elf32_Off) phdr64[i].p_offset;
     181               0 :       ncf->pheaders[i].p_vaddr = (Elf32_Addr) phdr64[i].p_vaddr;
     182               0 :       ncf->pheaders[i].p_paddr = (Elf32_Addr) phdr64[i].p_paddr;
     183               0 :       ncf->pheaders[i].p_filesz = (Elf32_Word) phdr64[i].p_filesz;
     184               0 :       ncf->pheaders[i].p_memsz = (Elf32_Word) phdr64[i].p_memsz;
     185               0 :       ncf->pheaders[i].p_align = (Elf32_Word) phdr64[i].p_align;
     186               0 :     }
     187               0 :   } else
     188                 : #endif
     189                 :   {
     190                 :     /* TODO(karl) Remove the cast to size_t, or verify size. */
     191               1 :     nread = readat(ncf, fd, ncf->pheaders, (off_t) phsize, (off_t) phoff);
     192               2 :     if (nread < 0 || (size_t) nread < phsize) return -1;
     193                 :   }
     194                 : 
     195                 :   /* Iterate through the program headers to find the virtual */
     196                 :   /* size of loaded text.                                    */
     197               1 :   vmemlo = MAX_ELF_ADDR;
     198               1 :   vmemhi = MIN_ELF_ADDR;
     199               6 :   for (i = 0; i < phnum; i++) {
     200               2 :     if (ncf->pheaders[i].p_type != PT_LOAD) continue;
     201               3 :     if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
     202                 :     /* This is executable text. Check low and high addrs */
     203               2 :     if (vmemlo > ncf->pheaders[i].p_vaddr) vmemlo = ncf->pheaders[i].p_vaddr;
     204               1 :     if (vmemhi < ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz) {
     205               1 :       vmemhi = ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz;
     206               1 :     }
     207               1 :   }
     208               1 :   ncf->size = vmemhi - vmemlo;
     209               1 :   ncf->vbase = vmemlo;
     210                 :   /* TODO(karl) Remove the cast to size_t, or verify size. */
     211               1 :   ncf->data = (uint8_t *)calloc(1, (size_t) ncf->size);
     212               1 :   if (NULL == ncf->data) {
     213               0 :     ncf->error_fn("nc_load(%s): calloc(1, %d) failed\n",
     214                 :                   ncf->fname, (int)ncf->size);
     215               0 :     return -1;
     216                 :   }
     217                 : 
     218                 :   /* Load program text segments */
     219               6 :   for (i = 0; i < phnum; i++) {
     220               2 :     const Elf_Phdr *p = &ncf->pheaders[i];
     221               2 :     if (p->p_type != PT_LOAD) continue;
     222               3 :     if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
     223                 : 
     224                 :     /* TODO(karl) Remove the cast to off_t, or verify value in range. */
     225               1 :     nread = readat(ncf, fd, &(ncf->data[p->p_vaddr - ncf->vbase]),
     226                 :                    (off_t) p->p_filesz, (off_t) p->p_offset);
     227               2 :     if (nread < 0 || (size_t) nread < p->p_filesz) {
     228               0 :       ncf->error_fn(
     229                 :           "nc_load(%s): could not read segment %d (%d < %"
     230                 :           NACL_PRIuElf_Xword")\n",
     231                 :           ncf->fname, i, (int)nread, p->p_filesz);
     232               0 :       return -1;
     233                 :     }
     234               1 :   }
     235                 : 
     236                 :   /* load the section headers */
     237               1 :   ncf->shnum = shnum;
     238               1 :   shsize = ncf->shnum * sizeof(*ncf->sheaders);
     239               1 :   ncf->sheaders = (Elf_Shdr *)calloc(1, shsize);
     240               1 :   if (NULL == ncf->sheaders) {
     241               0 :     ncf->error_fn("nc_load(%s): calloc(1, %"NACL_PRIdS") failed\n",
     242                 :                   ncf->fname, shsize);
     243               0 :     return -1;
     244                 :   }
     245                 : #if NACL_TARGET_SUBARCH == 64
     246               1 :   if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
     247                 :     /*
     248                 :      * Read 64-bit section headers and convert them.
     249                 :      */
     250               0 :     Elf64_Shdr *shdr64 = (Elf64_Shdr *)calloc(shnum, sizeof(shdr64[0]));
     251               0 :     if (NULL == shdr64) {
     252               0 :       ncf->error_fn(
     253                 :           "nc_load(%s): calloc(%"NACL_PRIdS", %"NACL_PRIdS") failed\n",
     254                 :           ncf->fname, (size_t) shnum, sizeof(shdr64[0]));
     255               0 :       return -1;
     256                 :     }
     257               0 :     shsize = ncf->shnum * sizeof(shdr64[0]);
     258               0 :     nread = readat(ncf, fd, shdr64, (off_t) shsize, (off_t) shoff);
     259               0 :     if (nread < 0 || (size_t) nread < shsize) {
     260               0 :       ncf->error_fn("nc_load(%s): could not read section headers\n",
     261                 :                     ncf->fname);
     262               0 :       return -1;
     263                 :     }
     264               0 :     for (i = 0; i < shnum; ++i) {
     265               0 :       if (shdr64[i].sh_flags > 0xffffffffU ||
     266                 :           shdr64[i].sh_size > 0xffffffffU ||
     267                 :           shdr64[i].sh_addralign > 0xffffffffU ||
     268                 :           shdr64[i].sh_entsize > 0xffffffffU) {
     269               0 :         ncf->error_fn("nc_load(%s): shdr[%d] fields overflow 32 bits\n",
     270                 :                       ncf->fname, i);
     271               0 :         return -1;
     272                 :       }
     273               0 :       ncf->sheaders[i].sh_name = shdr64[i].sh_name;
     274               0 :       ncf->sheaders[i].sh_type = shdr64[i].sh_type;
     275               0 :       ncf->sheaders[i].sh_flags = (Elf32_Word) shdr64[i].sh_flags;
     276               0 :       ncf->sheaders[i].sh_addr = (Elf32_Addr) shdr64[i].sh_addr;
     277               0 :       ncf->sheaders[i].sh_offset = (Elf32_Off) shdr64[i].sh_offset;
     278               0 :       ncf->sheaders[i].sh_size = (Elf32_Word) shdr64[i].sh_size;
     279               0 :       ncf->sheaders[i].sh_link = shdr64[i].sh_link;
     280               0 :       ncf->sheaders[i].sh_info = shdr64[i].sh_info;
     281               0 :       ncf->sheaders[i].sh_addralign = (Elf32_Word) shdr64[i].sh_addralign;
     282               0 :       ncf->sheaders[i].sh_entsize = (Elf32_Word) shdr64[i].sh_entsize;
     283               0 :     }
     284               0 :     free(shdr64);
     285               0 :   } else
     286                 : #endif
     287                 :   {
     288                 :     /* TODO(karl) Remove the cast to size_t, or verify value in range. */
     289               1 :     nread = readat(ncf, fd, ncf->sheaders, (off_t) shsize, (off_t) shoff);
     290               2 :     if (nread < 0 || (size_t) nread < shsize) {
     291               0 :       ncf->error_fn("nc_load(%s): could not read section headers\n",
     292                 :                     ncf->fname);
     293               0 :       return -1;
     294                 :     }
     295                 :   }
     296                 : 
     297                 :   /* success! */
     298               1 :   return 0;
     299               1 : }
     300                 : 
     301               1 : ncfile *nc_loadfile_depending(const char *filename,
     302               1 :                               nc_loadfile_error_fn error_fn) {
     303               1 :   ncfile *ncf;
     304               1 :   int fd;
     305               1 :   int rdflags = O_RDONLY | _O_BINARY;
     306               1 :   fd = OPEN(filename, rdflags);
     307               1 :   if (fd < 0) return NULL;
     308                 : 
     309                 :   /* Allocate the ncfile structure */
     310               1 :   ncf = calloc(1, sizeof(ncfile));
     311               1 :   if (ncf == NULL) return NULL;
     312               1 :   ncf->size = 0;
     313               1 :   ncf->data = NULL;
     314               1 :   ncf->fname = filename;
     315               1 :   if (error_fn == NULL) {
     316               1 :     ncf->error_fn = NcLoadFilePrintError;
     317               1 :   } else {
     318               0 :     ncf->error_fn = error_fn;
     319                 :   }
     320                 : 
     321               1 :   if (nc_load(ncf, fd) < 0) {
     322               0 :     close(fd);
     323               0 :     free(ncf);
     324               0 :     return NULL;
     325                 :   }
     326               1 :   close(fd);
     327               1 :   return ncf;
     328               1 : }
     329                 : 
     330               1 : ncfile *nc_loadfile(const char *filename) {
     331               1 :   return nc_loadfile_depending(filename, NULL);
     332                 : }
     333                 : 
     334               0 : ncfile *nc_loadfile_with_error_fn(const char *filename,
     335               0 :                                   nc_loadfile_error_fn error_fn) {
     336               0 :   return nc_loadfile_depending(filename, error_fn);
     337                 : }
     338                 : 
     339                 : 
     340               1 : void nc_freefile(ncfile *ncf) {
     341               2 :   if (ncf->data != NULL) free(ncf->data);
     342               1 :   free(ncf);
     343               1 : }
     344                 : 
     345                 : /***********************************************************************/
     346                 : 
     347               1 : void GetVBaseAndLimit(ncfile *ncf, NaClPcAddress *vbase,
     348               1 :                       NaClPcAddress *vlimit) {
     349               1 :   int ii;
     350                 :   /* TODO(karl) - Define so constant applies to 64-bit pc address. */
     351               1 :   NaClPcAddress base = 0xffffffff;
     352               1 :   NaClPcAddress limit = 0;
     353                 : 
     354              14 :   for (ii = 0; ii < ncf->shnum; ii++) {
     355               6 :     if ((ncf->sheaders[ii].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR) {
     356               2 :       if (ncf->sheaders[ii].sh_addr < base) base = ncf->sheaders[ii].sh_addr;
     357               1 :       if (ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size > limit)
     358               1 :         limit = ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size;
     359               1 :     }
     360               6 :   }
     361               1 :   *vbase = base;
     362               1 :   *vlimit = limit;
     363               1 : }

Generated by: LCOV version 1.7