LCOV - code coverage report
Current view: directory - src/trusted/validator - ncfileutil.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 122 82 67.2 %
Date: 2012-02-16 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2011 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() { 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                 :   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                 : static off_t readat(ncfile* ncf, const int fd,
      49              24 :                     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              24 :   int sofar = 0;
      52                 :   int nread;
      53              24 :   char *cbuf = (char *) buf;
      54                 : 
      55              24 :   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                 :   do {
      65              24 :     nread = read(fd, &cbuf[sofar], sz - sofar);
      66              24 :     if (nread <= 0) {
      67               0 :       ncf->error_fn("readat: read failed\n");
      68               0 :       return -1;
      69                 :     }
      70              24 :     sofar += nread;
      71              24 :   } while (sz != sofar);
      72              24 :   return nread;
      73                 : }
      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                 : }
      84                 : 
      85               6 : 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                 :   } h;
      92                 :   Elf_Half phnum;
      93                 :   Elf_Half shnum;
      94                 :   Elf_Off phoff;
      95                 :   Elf_Off shoff;
      96                 :   ssize_t nread;
      97                 :   Elf_Addr vmemlo, vmemhi;
      98                 :   size_t shsize, phsize;
      99                 :   int i;
     100                 : 
     101                 :   /* Read and check the ELF header */
     102               6 :   nread = readat(ncf, fd, &h, sizeof(h), 0);
     103               6 :   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               6 :   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                 :   if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
     116                 :     if (h.h64.e_phoff > 0xffffffffU) {
     117                 :       ncf->error_fn("nc_load(%s): e_phoff overflows 32 bits\n", ncf->fname);
     118                 :       return -1;
     119                 :     }
     120                 :     if (h.h64.e_shoff > 0xffffffffU) {
     121                 :       ncf->error_fn("nc_load(%s): e_shoff overflows 32 bits\n", ncf->fname);
     122                 :       return -1;
     123                 :     }
     124                 :     phoff = (Elf32_Off) h.h64.e_phoff;
     125                 :     shoff = (Elf32_Off) h.h64.e_shoff;
     126                 :     phnum = h.h64.e_phnum;
     127                 :     shnum = h.h64.e_shnum;
     128                 :   } else
     129                 : #endif
     130                 :   {
     131               6 :     if (h.h32.e_ident[EI_CLASS] == ELFCLASS32) {
     132               6 :       phoff = h.h32.e_phoff;
     133               6 :       shoff = h.h32.e_shoff;
     134               6 :       phnum = h.h32.e_phnum;
     135               6 :       shnum = h.h32.e_shnum;
     136                 :     } 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                 :                     GetEiClassName(h.h32.e_ident[EI_CLASS]));
     140               0 :       return -1;
     141                 :     }
     142                 :   }
     143                 : 
     144                 :   /* We now support only 32-byte bundle alignment.  */
     145               6 :   ncf->ncalign = 32;
     146                 : 
     147                 :   /* Read the program header table */
     148              12 :   if (phnum <= 0 || phnum > kMaxPhnum) {
     149               0 :     ncf->error_fn("nc_load(%s): e_phnum %d > kMaxPhnum %d\n",
     150                 :                   ncf->fname, phnum, kMaxPhnum);
     151               0 :     return -1;
     152                 :   }
     153               6 :   ncf->phnum = phnum;
     154               6 :   ncf->pheaders = (Elf_Phdr *)calloc(phnum, sizeof(Elf_Phdr));
     155               6 :   if (NULL == ncf->pheaders) {
     156               0 :     ncf->error_fn("nc_load(%s): calloc(%d, %"NACL_PRIdS") failed\n",
     157                 :                   ncf->fname, phnum, sizeof(Elf_Phdr));
     158               0 :     return -1;
     159                 :   }
     160               6 :   phsize = phnum * sizeof(*ncf->pheaders);
     161                 : #if NACL_TARGET_SUBARCH == 64
     162                 :   if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
     163                 :     /*
     164                 :      * Read 64-bit program headers and convert them.
     165                 :      */
     166                 :     Elf64_Phdr phdr64[kMaxPhnum];
     167                 :     nread = readat(ncf, fd, phdr64, (off_t) (phnum * sizeof(phdr64[0])),
     168                 :                    (off_t) phoff);
     169                 :     if (nread < 0 || (size_t) nread < phsize) return -1;
     170                 :     for (i = 0; i < phnum; ++i) {
     171                 :       if (phdr64[i].p_offset > 0xffffffffU ||
     172                 :           phdr64[i].p_vaddr > 0xffffffffU ||
     173                 :           phdr64[i].p_paddr > 0xffffffffU ||
     174                 :           phdr64[i].p_filesz > 0xffffffffU ||
     175                 :           phdr64[i].p_memsz > 0xffffffffU ||
     176                 :           phdr64[i].p_align > 0xffffffffU) {
     177                 :         ncf->error_fn("nc_load(%s): phdr[%d] fields overflow 32 bits\n",
     178                 :                       ncf->fname, i);
     179                 :         return -1;
     180                 :       }
     181                 :       ncf->pheaders[i].p_type = phdr64[i].p_type;
     182                 :       ncf->pheaders[i].p_flags = phdr64[i].p_flags;
     183                 :       ncf->pheaders[i].p_offset = (Elf32_Off) phdr64[i].p_offset;
     184                 :       ncf->pheaders[i].p_vaddr = (Elf32_Addr) phdr64[i].p_vaddr;
     185                 :       ncf->pheaders[i].p_paddr = (Elf32_Addr) phdr64[i].p_paddr;
     186                 :       ncf->pheaders[i].p_filesz = (Elf32_Word) phdr64[i].p_filesz;
     187                 :       ncf->pheaders[i].p_memsz = (Elf32_Word) phdr64[i].p_memsz;
     188                 :       ncf->pheaders[i].p_align = (Elf32_Word) phdr64[i].p_align;
     189                 :     }
     190                 :   } else
     191                 : #endif
     192                 :   {
     193                 :     /* TODO(karl) Remove the cast to size_t, or verify size. */
     194               6 :     nread = readat(ncf, fd, ncf->pheaders, (off_t) phsize, (off_t) phoff);
     195               6 :     if (nread < 0 || (size_t) nread < phsize) return -1;
     196                 :   }
     197                 : 
     198                 :   /* Iterate through the program headers to find the virtual */
     199                 :   /* size of loaded text.                                    */
     200               6 :   vmemlo = MAX_ELF_ADDR;
     201               6 :   vmemhi = MIN_ELF_ADDR;
     202              30 :   for (i = 0; i < phnum; i++) {
     203              24 :     if (ncf->pheaders[i].p_type != PT_LOAD) continue;
     204              18 :     if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
     205                 :     /* This is executable text. Check low and high addrs */
     206               6 :     if (vmemlo > ncf->pheaders[i].p_vaddr) vmemlo = ncf->pheaders[i].p_vaddr;
     207               6 :     if (vmemhi < ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz) {
     208               6 :       vmemhi = ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz;
     209                 :     }
     210                 :   }
     211               6 :   ncf->size = vmemhi - vmemlo;
     212               6 :   ncf->vbase = vmemlo;
     213                 :   /* TODO(karl) Remove the cast to size_t, or verify size. */
     214               6 :   ncf->data = (uint8_t *)calloc(1, (size_t) ncf->size);
     215               6 :   if (NULL == ncf->data) {
     216               0 :     ncf->error_fn("nc_load(%s): calloc(1, %d) failed\n",
     217                 :                   ncf->fname, (int)ncf->size);
     218               0 :     return -1;
     219                 :   }
     220                 : 
     221                 :   /* Load program text segments */
     222              30 :   for (i = 0; i < phnum; i++) {
     223              24 :     const Elf_Phdr *p = &ncf->pheaders[i];
     224              24 :     if (p->p_type != PT_LOAD) continue;
     225              18 :     if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
     226                 : 
     227                 :     /* TODO(karl) Remove the cast to off_t, or verify value in range. */
     228               6 :     nread = readat(ncf, fd, &(ncf->data[p->p_vaddr - ncf->vbase]),
     229                 :                    (off_t) p->p_filesz, (off_t) p->p_offset);
     230               6 :     if (nread < 0 || (size_t) nread < p->p_filesz) {
     231               0 :       ncf->error_fn(
     232                 :           "nc_load(%s): could not read segment %d (%d < %"
     233                 :           NACL_PRIuElf_Xword")\n",
     234                 :           ncf->fname, i, (int)nread, p->p_filesz);
     235               0 :       return -1;
     236                 :     }
     237                 :   }
     238                 : 
     239                 :   /* load the section headers */
     240               6 :   ncf->shnum = shnum;
     241               6 :   shsize = ncf->shnum * sizeof(*ncf->sheaders);
     242               6 :   ncf->sheaders = (Elf_Shdr *)calloc(1, shsize);
     243               6 :   if (NULL == ncf->sheaders) {
     244               0 :     ncf->error_fn("nc_load(%s): calloc(1, %"NACL_PRIdS") failed\n",
     245                 :                   ncf->fname, shsize);
     246               0 :     return -1;
     247                 :   }
     248                 : #if NACL_TARGET_SUBARCH == 64
     249                 :   if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
     250                 :     /*
     251                 :      * Read 64-bit section headers and convert them.
     252                 :      */
     253                 :     Elf64_Shdr *shdr64 = (Elf64_Shdr *)calloc(shnum, sizeof(shdr64[0]));
     254                 :     if (NULL == shdr64) {
     255                 :       ncf->error_fn(
     256                 :           "nc_load(%s): calloc(%"NACL_PRIdS", %"NACL_PRIdS") failed\n",
     257                 :           ncf->fname, (size_t) shnum, sizeof(shdr64[0]));
     258                 :       return -1;
     259                 :     }
     260                 :     shsize = ncf->shnum * sizeof(shdr64[0]);
     261                 :     nread = readat(ncf, fd, shdr64, (off_t) shsize, (off_t) shoff);
     262                 :     if (nread < 0 || (size_t) nread < shsize) {
     263                 :       ncf->error_fn("nc_load(%s): could not read section headers\n",
     264                 :                     ncf->fname);
     265                 :       return -1;
     266                 :     }
     267                 :     for (i = 0; i < shnum; ++i) {
     268                 :       if (shdr64[i].sh_flags > 0xffffffffU ||
     269                 :           shdr64[i].sh_size > 0xffffffffU ||
     270                 :           shdr64[i].sh_addralign > 0xffffffffU ||
     271                 :           shdr64[i].sh_entsize > 0xffffffffU) {
     272                 :         ncf->error_fn("nc_load(%s): shdr[%d] fields overflow 32 bits\n",
     273                 :                       ncf->fname, i);
     274                 :         return -1;
     275                 :       }
     276                 :       ncf->sheaders[i].sh_name = shdr64[i].sh_name;
     277                 :       ncf->sheaders[i].sh_type = shdr64[i].sh_type;
     278                 :       ncf->sheaders[i].sh_flags = (Elf32_Word) shdr64[i].sh_flags;
     279                 :       ncf->sheaders[i].sh_addr = (Elf32_Addr) shdr64[i].sh_addr;
     280                 :       ncf->sheaders[i].sh_offset = (Elf32_Off) shdr64[i].sh_offset;
     281                 :       ncf->sheaders[i].sh_size = (Elf32_Word) shdr64[i].sh_size;
     282                 :       ncf->sheaders[i].sh_link = shdr64[i].sh_link;
     283                 :       ncf->sheaders[i].sh_info = shdr64[i].sh_info;
     284                 :       ncf->sheaders[i].sh_addralign = (Elf32_Word) shdr64[i].sh_addralign;
     285                 :       ncf->sheaders[i].sh_entsize = (Elf32_Word) shdr64[i].sh_entsize;
     286                 :     }
     287                 :     free(shdr64);
     288                 :   } else
     289                 : #endif
     290                 :   {
     291                 :     /* TODO(karl) Remove the cast to size_t, or verify value in range. */
     292               6 :     nread = readat(ncf, fd, ncf->sheaders, (off_t) shsize, (off_t) shoff);
     293               6 :     if (nread < 0 || (size_t) nread < shsize) {
     294               0 :       ncf->error_fn("nc_load(%s): could not read section headers\n",
     295                 :                     ncf->fname);
     296               0 :       return -1;
     297                 :     }
     298                 :   }
     299                 : 
     300                 :   /* success! */
     301               6 :   return 0;
     302                 : }
     303                 : 
     304                 : ncfile *nc_loadfile_depending(const char *filename,
     305               6 :                               nc_loadfile_error_fn error_fn) {
     306                 :   ncfile *ncf;
     307                 :   int fd;
     308               6 :   int rdflags = O_RDONLY | _O_BINARY;
     309               6 :   fd = OPEN(filename, rdflags);
     310               6 :   if (fd < 0) return NULL;
     311                 : 
     312                 :   /* Allocate the ncfile structure */
     313               6 :   ncf = calloc(1, sizeof(ncfile));
     314               6 :   if (ncf == NULL) return NULL;
     315               6 :   ncf->size = 0;
     316               6 :   ncf->data = NULL;
     317               6 :   ncf->fname = filename;
     318               6 :   if (error_fn == NULL) {
     319               6 :     ncf->error_fn = NcLoadFilePrintError;
     320                 :   } else {
     321               0 :     ncf->error_fn = error_fn;
     322                 :   }
     323                 : 
     324               6 :   if (nc_load(ncf, fd) < 0) {
     325               0 :     close(fd);
     326               0 :     free(ncf);
     327               0 :     return NULL;
     328                 :   }
     329               6 :   close(fd);
     330               6 :   return ncf;
     331                 : }
     332                 : 
     333               6 : ncfile *nc_loadfile(const char *filename) {
     334               6 :   return nc_loadfile_depending(filename, NULL);
     335                 : }
     336                 : 
     337                 : ncfile *nc_loadfile_with_error_fn(const char *filename,
     338               0 :                                   nc_loadfile_error_fn error_fn) {
     339               0 :   return nc_loadfile_depending(filename, error_fn);
     340                 : }
     341                 : 
     342                 : 
     343               6 : void nc_freefile(ncfile *ncf) {
     344               6 :   if (ncf->data != NULL) free(ncf->data);
     345               6 :   free(ncf);
     346               6 : }
     347                 : 
     348                 : /***********************************************************************/
     349                 : 
     350                 : void GetVBaseAndLimit(ncfile *ncf, NaClPcAddress *vbase,
     351               6 :                       NaClPcAddress *vlimit) {
     352                 :   int ii;
     353                 :   /* TODO(karl) - Define so constant applies to 64-bit pc address. */
     354               6 :   NaClPcAddress base = 0xffffffff;
     355               6 :   NaClPcAddress limit = 0;
     356                 : 
     357             145 :   for (ii = 0; ii < ncf->shnum; ii++) {
     358             139 :     if ((ncf->sheaders[ii].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR) {
     359              14 :       if (ncf->sheaders[ii].sh_addr < base) base = ncf->sheaders[ii].sh_addr;
     360              14 :       if (ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size > limit)
     361              14 :         limit = ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size;
     362                 :     }
     363                 :   }
     364               6 :   *vbase = base;
     365               6 :   *vlimit = limit;
     366               6 : }

Generated by: LCOV version 1.7