github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/stage1/diagnostic/diagnostic-util.c (about)

     1  // Copyright 2014-2016 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #define _GNU_SOURCE
    16  #include <errno.h>
    17  #include <fcntl.h>
    18  #include <grp.h>
    19  #include <inttypes.h>
    20  #include <limits.h>
    21  #include <stdint.h>
    22  #include <sys/mman.h>
    23  #include <sys/stat.h>
    24  #include <sys/types.h>
    25  #include <unistd.h>
    26  
    27  #include "diagnostic-util.h"
    28  #include "elf.h"
    29  
    30  
    31  static void map_file(const char *path, int prot, int flags, struct stat *st, void **map)
    32  {
    33      int fd;
    34  
    35      pexit_if((fd = open(path, O_RDONLY)) == -1,
    36               "Unable to open \"%s\"", path);
    37      pexit_if(fstat(fd, st) == -1,
    38               "Cannot stat \"%s\"", path);
    39      exit_if(!S_ISREG(st->st_mode), "\"%s\" is not a regular file", path);
    40      pexit_if(!(*map = mmap(NULL, st->st_size, prot, flags, fd, 0)),
    41               "Mmap of \"%s\" failed", path);
    42      pexit_if(close(fd) == -1,
    43               "Close of %i [%s] failed", fd, path);
    44  }
    45  
    46  void diag(const char *exe)
    47  {
    48      static const uint8_t	elf[] = {0x7f, 'E', 'L', 'F'};
    49      static const uint8_t	shebang[] = {'#','!'};
    50      static int		        diag_depth;
    51      struct stat		        st;
    52      const uint8_t	        *mm;
    53      const char		        *itrp = NULL;
    54  
    55      map_file(exe, PROT_READ, MAP_SHARED, &st, (void **)&mm);
    56      exit_if(!((S_IXUSR|S_IXGRP|S_IXOTH) & st.st_mode),
    57              "\"%s\" is not executable", exe)
    58  
    59      if(st.st_size >= sizeof(shebang) &&
    60         !memcmp(mm, shebang, sizeof(shebang))) {
    61          const uint8_t	*nl;
    62          int		maxlen = MIN(PATH_MAX, st.st_size - sizeof(shebang));
    63          /* TODO(vc): EOF-terminated shebang lines are technically possible */
    64          exit_if(!(nl = memchr(&mm[sizeof(shebang)], '\n', maxlen)),
    65                  "Shebang line too long");
    66          pexit_if(!(itrp = strndup((char *)&mm[sizeof(shebang)], (nl - mm) - 2)),
    67                   "Failed to dup interpreter path");
    68      } else if(st.st_size >= sizeof(elf) &&
    69                !memcmp(mm, elf, sizeof(elf))) {
    70          uint64_t	(*lget)(const uint8_t *) = NULL;
    71          uint32_t	(*iget)(const uint8_t *) = NULL;
    72          uint16_t	(*sget)(const uint8_t *) = NULL;
    73          const void	*phoff = NULL, *phesz = NULL, *phecnt = NULL;
    74          const uint8_t	*ph = NULL;
    75          int		i, phreloff, phrelsz;
    76  
    77          exit_if(mm[ELF_VERSION] != 1,
    78                  "Unsupported ELF version: %hhx", mm[ELF_VERSION]);
    79  
    80          /* determine which accessors to use and where */
    81          if(mm[ELF_BITS] == ELF_BITS_32) {
    82              if(mm[ELF_ENDIAN] == ELF_ENDIAN_LITL) {
    83                  lget = le32_lget;
    84                  sget = le_sget;
    85                  iget = le_iget;
    86              } else if(mm[ELF_ENDIAN] == ELF_ENDIAN_BIG) {
    87                  lget = be32_lget;
    88                  sget = be_sget;
    89                  iget = be_iget;
    90              }
    91              phoff = &mm[ELF32_PHT_OFF];
    92              phesz = &mm[ELF32_PHTE_SIZE];
    93              phecnt = &mm[ELF32_PHTE_CNT];
    94              phreloff = ELF32_PHE_OFF;
    95              phrelsz = ELF32_PHE_SIZE;
    96          } else if(mm[ELF_BITS] == ELF_BITS_64) {
    97              if(mm[ELF_ENDIAN] == ELF_ENDIAN_LITL) {
    98                  lget = le64_lget;
    99                  sget = le_sget;
   100                  iget = le_iget;
   101              } else if(mm[ELF_ENDIAN] == ELF_ENDIAN_BIG) {
   102                  lget = be64_lget;
   103                  sget = be_sget;
   104                  iget = be_iget;
   105              }
   106              phoff = &mm[ELF64_PHT_OFF];
   107              phesz = &mm[ELF64_PHTE_SIZE];
   108              phecnt = &mm[ELF64_PHTE_CNT];
   109              phreloff = ELF64_PHE_OFF;
   110              phrelsz = ELF64_PHE_SIZE;
   111          }
   112  
   113          exit_if(!lget, "Unsupported ELF format");
   114  
   115          if(!phoff) /* program header may be absent, don't make it an error */
   116              return;
   117  
   118          /* TODO(vc): sanity checks on values before using them */
   119          for(ph = &mm[lget(phoff)], i = 0; i < sget(phecnt); i++, ph += sget(phesz)) {
   120              if(iget(ph) == ELF_PT_INTERP) {
   121                  itrp = strndup((char *)&mm[lget(&ph[phreloff])], lget(&ph[phrelsz]));
   122                  break;
   123              }
   124          }
   125      } else {
   126          exit_if(1, "Unsupported file type");
   127      }
   128  
   129      exit_if(!itrp, "Unable to determine interpreter for \"%s\"", exe);
   130      exit_if(*itrp != '/', "Path must be absolute: \"%s\"", itrp);
   131      exit_if(++diag_depth > MAX_DIAG_DEPTH,
   132              "Excessive interpreter recursion, giving up");
   133      diag(itrp);
   134  }