LCOV - code coverage report
Current view: directory - src/trusted/validator/driver - ncval.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 138 106 76.8 %
Date: 2014-09-25 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                 : #include <stddef.h>
       8                 : #include <stdio.h>
       9                 : #include <stdlib.h>
      10                 : #include <string.h>
      11                 : 
      12                 : #include <set>
      13                 : #include <string>
      14                 : #include <vector>
      15                 : 
      16                 : #include "native_client/src/include/elf.h"
      17                 : #include "native_client/src/include/elf_constants.h"
      18                 : #include "native_client/src/include/nacl_macros.h"
      19                 : #include "native_client/src/include/portability_io.h"
      20                 : #include "native_client/src/shared/platform/nacl_check.h"
      21                 : #include "native_client/src/shared/utils/types.h"
      22                 : #include "native_client/src/trusted/cpu_features/arch/arm/cpu_arm.h"
      23                 : #include "native_client/src/trusted/validator/driver/elf_load.h"
      24                 : #include "native_client/src/trusted/validator_arm/problem_reporter.h"
      25                 : #include "native_client/src/trusted/validator_arm/validator.h"
      26                 : #include "native_client/src/trusted/validator_ragel/validator.h"
      27                 : 
      28                 : using std::set;
      29                 : using std::string;
      30                 : using std::vector;
      31                 : 
      32                 : using nacl_arm_val::CodeSegment;
      33                 : using nacl_arm_val::ProblemReporter;
      34                 : using nacl_arm_val::SfiValidator;
      35                 : 
      36                 : using elf_load::Segment;
      37                 : 
      38                 : 
      39                 : struct Jump {
      40                 :   uint32_t from;
      41                 :   uint32_t to;
      42               1 :   Jump(uint32_t from, uint32_t to) : from(from), to(to) {}
      43                 : };
      44                 : 
      45                 : 
      46                 : struct Error {
      47                 :   uint32_t offset;
      48                 :   string message;
      49               1 :   Error(uint32_t offset, string message) : offset(offset), message(message) {}
      50                 : };
      51                 : 
      52                 : 
      53                 : struct UserData {
      54                 :   Segment segment;
      55                 :   vector<Error> *errors;
      56                 :   vector<Jump> *jumps;
      57                 :   set<uint32_t> *bad_jump_targets;
      58                 :   UserData(Segment segment,
      59                 :            vector<Error> *errors,
      60                 :            vector<Jump> *jumps,
      61                 :            set<uint32_t> *bad_jump_targets)
      62                 :       : segment(segment),
      63                 :         errors(errors),
      64                 :         jumps(jumps),
      65               1 :         bad_jump_targets(bad_jump_targets) {
      66               1 :   }
      67                 : };
      68                 : 
      69                 : 
      70                 : Bool ProcessInstruction(
      71                 :     const uint8_t *begin, const uint8_t *end,
      72               1 :     uint32_t validation_info, void *user_data_ptr) {
      73                 : 
      74               1 :   UserData &user_data = *reinterpret_cast<UserData *>(user_data_ptr);
      75               1 :   vector<Error> &errors = *user_data.errors;
      76                 : 
      77                 :   uint32_t offset = user_data.segment.vaddr +
      78               1 :                     static_cast<uint32_t>(begin - user_data.segment.data);
      79                 : 
      80                 :   // Presence of RELATIVE_8BIT or RELATIVE_32BIT indicates that instruction is
      81                 :   // immediate jump or call.
      82                 :   // We only record jumps that are in range, so we don't have to worry
      83                 :   // about overflows.
      84               1 :   if ((validation_info & DIRECT_JUMP_OUT_OF_RANGE) == 0) {
      85                 :     uint32_t program_counter = user_data.segment.vaddr +
      86                 :                                static_cast<uint32_t>(end -
      87               1 :                                                      user_data.segment.data);
      88               1 :     if (validation_info & RELATIVE_8BIT) {
      89               1 :       program_counter += reinterpret_cast<const int8_t *>(end)[-1];
      90               1 :       user_data.jumps->push_back(Jump(offset, program_counter));
      91               1 :     } else if (validation_info & RELATIVE_32BIT) {
      92               1 :       program_counter += reinterpret_cast<const int32_t *>(end)[-1];
      93               1 :       user_data.jumps->push_back(Jump(offset, program_counter));
      94                 :     }
      95                 :   }
      96                 : 
      97                 :   Bool result = (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET))
      98                 :                 ? FALSE
      99               1 :                 : TRUE;
     100                 : 
     101                 :   // We clear bit for each processed error, and in the end if there are still
     102                 :   // errors we did not report, we produce generic error message.
     103                 :   // This way, if validator interface was changed (new error bits were
     104                 :   // introduced), we at least wouldn't ignore them completely.
     105                 : 
     106                 :   // TODO(shcherbina): deal with code duplication somehow.
     107                 : 
     108               1 :   if (validation_info & UNRECOGNIZED_INSTRUCTION) {
     109               1 :     validation_info &= ~UNRECOGNIZED_INSTRUCTION;
     110               1 :     errors.push_back(Error(offset, "unrecognized instruction"));
     111                 :   }
     112                 : 
     113               1 :   if (validation_info & DIRECT_JUMP_OUT_OF_RANGE) {
     114               1 :     validation_info &= ~DIRECT_JUMP_OUT_OF_RANGE;
     115               1 :     errors.push_back(Error(offset, "direct jump out of range"));
     116                 :   }
     117                 : 
     118               1 :   if (validation_info & CPUID_UNSUPPORTED_INSTRUCTION) {
     119               0 :     validation_info &= ~CPUID_UNSUPPORTED_INSTRUCTION;
     120               0 :     errors.push_back(Error(offset, "required CPU feature not found"));
     121                 :   }
     122                 : 
     123               1 :   if (validation_info & FORBIDDEN_BASE_REGISTER) {
     124               1 :     validation_info &= ~FORBIDDEN_BASE_REGISTER;
     125               1 :     errors.push_back(Error(offset, "improper memory address - bad base"));
     126                 :   }
     127                 : 
     128               1 :   if (validation_info & UNRESTRICTED_INDEX_REGISTER) {
     129               1 :     validation_info &= ~UNRESTRICTED_INDEX_REGISTER;
     130               1 :     errors.push_back(Error(offset, "improper memory address - bad index"));
     131                 :   }
     132                 : 
     133                 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     134               1 :       RESTRICTED_RBP_UNPROCESSED) {
     135               1 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     136               1 :     errors.push_back(Error(offset, "improper %rbp sandboxing"));
     137                 :   }
     138                 : 
     139                 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     140               1 :       UNRESTRICTED_RBP_PROCESSED) {
     141               1 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     142               1 :     errors.push_back(Error(offset, "improper %rbp sandboxing"));
     143                 :   }
     144                 : 
     145                 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     146               1 :       RESTRICTED_RSP_UNPROCESSED) {
     147               1 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     148               1 :     errors.push_back(Error(offset, "improper %rsp sandboxing"));
     149                 :   }
     150                 : 
     151                 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     152               1 :       UNRESTRICTED_RSP_PROCESSED) {
     153               1 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     154               1 :     errors.push_back(Error(offset, "improper %rsp sandboxing"));
     155                 :   }
     156                 : 
     157               1 :   if (validation_info & R15_MODIFIED) {
     158               1 :     validation_info &= ~R15_MODIFIED;
     159               1 :     errors.push_back(Error(offset, "error - %r15 is changed"));
     160                 :   }
     161                 : 
     162               1 :   if (validation_info & BP_MODIFIED) {
     163               1 :     validation_info &= ~BP_MODIFIED;
     164               1 :     errors.push_back(Error(offset, "error - %bpl or %bp is changed"));
     165                 :   }
     166                 : 
     167               1 :   if (validation_info & SP_MODIFIED) {
     168               1 :     validation_info &= ~SP_MODIFIED;
     169               1 :     errors.push_back(Error(offset, "error - %spl or %sp is changed"));
     170                 :   }
     171                 : 
     172               1 :   if (validation_info & BAD_CALL_ALIGNMENT) {
     173               1 :     validation_info &= ~BAD_CALL_ALIGNMENT;
     174               1 :     errors.push_back(Error(offset, "warning - bad call alignment"));
     175                 :   }
     176                 : 
     177               1 :   if (validation_info & BAD_JUMP_TARGET) {
     178               1 :     validation_info &= ~BAD_JUMP_TARGET;
     179               1 :     user_data.bad_jump_targets->insert(offset);
     180                 :   }
     181                 : 
     182               1 :   if (validation_info & VALIDATION_ERRORS_MASK) {
     183               0 :     errors.push_back(Error(offset, "<some other error>"));
     184                 :   }
     185                 : 
     186               1 :   return result;
     187               1 : }
     188                 : 
     189                 : 
     190                 : Bool ProcessError(
     191                 :     const uint8_t *begin, const uint8_t *end,
     192               1 :     uint32_t validation_info, void *user_data_ptr) {
     193                 :   UNREFERENCED_PARAMETER(begin);
     194                 :   UNREFERENCED_PARAMETER(end);
     195                 :   UNREFERENCED_PARAMETER(validation_info);
     196                 :   UNREFERENCED_PARAMETER(user_data_ptr);
     197               1 :   return FALSE;
     198               1 : }
     199                 : 
     200                 : 
     201                 : typedef Bool ValidateChunkFunc(
     202                 :     const uint8_t *data, size_t size,
     203                 :     uint32_t options,
     204                 :     const NaClCPUFeaturesX86 *cpu_features,
     205                 :     ValidationCallbackFunc user_callback,
     206                 :     void *callback_data);
     207                 : 
     208                 : 
     209                 : bool ValidateX86(
     210                 :     const Segment &segment,
     211                 :     ValidateChunkFunc validate_chunk,
     212               1 :     vector<Error> *errors) {
     213                 : 
     214               1 :   errors->clear();
     215                 : 
     216               1 :   if (segment.vaddr % kBundleSize != 0) {
     217                 :     errors->push_back(Error(
     218                 :         segment.vaddr,
     219               0 :         "Text segment offset in memory is not bundle-aligned."));
     220               0 :     return false;
     221                 :   }
     222                 : 
     223               1 :   if (segment.size % kBundleSize != 0) {
     224                 :     char buf[100];
     225                 :     SNPRINTF(buf, sizeof buf,
     226                 :              "Text segment size (0x%" NACL_PRIx32 ") is not "
     227                 :              "multiple of bundle size.",
     228               0 :              segment.size);
     229               0 :     errors->push_back(Error(segment.vaddr + segment.size, buf));
     230               0 :     return false;
     231                 :   }
     232                 : 
     233               1 :   vector<Jump> jumps;
     234               1 :   set<uint32_t> bad_jump_targets;
     235                 : 
     236               1 :   UserData user_data(segment, errors, &jumps, &bad_jump_targets);
     237                 : 
     238                 :   // TODO(shcherbina): customize from command line
     239                 : 
     240                 :   // We collect all errors except bad jump targets, and we separately
     241                 :   // memoize all direct jumps (or calls) and bad jump targets.
     242                 :   Bool result = validate_chunk(
     243                 :       segment.data, segment.size,
     244                 :       CALL_USER_CALLBACK_ON_EACH_INSTRUCTION, &kFullCPUIDFeatures,
     245               1 :       ProcessInstruction, &user_data);
     246                 : 
     247                 :   // Report destinations of jumps that lead to bad targets.
     248               1 :   for (size_t i = 0; i < jumps.size(); i++) {
     249               1 :     const Jump &jump = jumps[i];
     250               1 :     if (bad_jump_targets.count(jump.to) > 0)
     251               1 :       errors->push_back(Error(jump.from, "bad jump target"));
     252               1 :   }
     253                 : 
     254                 :   // Run validator as it is run in loader, without callback on each instruction,
     255                 :   // and ensure that results match. If ncval is guaranteed to return the same
     256                 :   // result as actual validator, it can be reliably used as validator wrapper
     257                 :   // for testing.
     258                 :   CHECK(result == validate_chunk(
     259                 :       segment.data, segment.size,
     260                 :       0, &kFullCPUIDFeatures,
     261               1 :       ProcessError, NULL));
     262                 : 
     263               1 :   return static_cast<bool>(result);
     264               1 : }
     265                 : 
     266                 : 
     267                 : class NcvalArmProblemReporter : public ProblemReporter {
     268                 :  public:
     269               0 :   explicit NcvalArmProblemReporter(vector<Error> *errors) : errors(errors) {}
     270                 : 
     271                 :  protected:
     272                 :   void ReportProblemMessage(nacl_arm_dec::Violation violation,
     273                 :                             uint32_t vaddr,
     274               0 :                             const char* message) {
     275                 :     UNREFERENCED_PARAMETER(violation);
     276               0 :     if (errors->size() < max_errors) {
     277               0 :       errors->push_back(Error(vaddr, message));
     278               0 :     } else if (errors->size() == max_errors) {
     279                 :       errors->push_back(
     280               0 :           Error(vaddr, "Too may errors: supressing remaining errors."));
     281                 :     }
     282               0 :   }
     283                 : 
     284                 :  private:
     285                 :   vector<Error> *errors;
     286                 :   // Maximum number of errors to generate.
     287                 :   static const size_t max_errors = 5000;
     288                 : };
     289                 : 
     290                 : 
     291               0 : bool ValidateArm(const Segment &segment, vector<Error> *errors) {
     292               0 :   errors->clear();
     293                 : 
     294               0 :   vector<CodeSegment> segments;
     295               0 :   segments.push_back(CodeSegment(segment.data, segment.vaddr, segment.size));
     296                 : 
     297                 :   NaClCPUFeaturesArm cpu_features;
     298               0 :   NaClClearCPUFeaturesArm(&cpu_features);
     299                 :   // TODO(shcherbina): add support for '--allow-conditional-memory-access' flag
     300                 :   // with the effect of
     301                 :   // NaClSetCPUFeatureArm(&cpu_features, NaClCPUFeatureArm_CanUseTstMem, 1);
     302                 :   // Alternatively, use approach described in
     303                 :   // http://code.google.com/p/nativeclient/issues/detail?id=3254
     304                 : 
     305                 :   SfiValidator validator(
     306                 :       16,  // bytes per bundle
     307                 :       // TODO(cbiffle): maybe check region sizes from ELF headers?
     308                 :       //                verify that instructions are in right region
     309                 :       1U << 30,  // code region size
     310                 :       1U << 30,  // data region size
     311                 :       nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Tp()),
     312                 :       nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Sp()),
     313               0 :       &cpu_features);
     314                 : 
     315               0 :   NcvalArmProblemReporter reporter(errors);
     316               0 :   return validator.validate(segments, &reporter);
     317               0 : }
     318                 : 
     319                 : 
     320               0 : void Usage() {
     321               0 :   printf("Usage:\n");
     322               0 :   printf("    ncval <ELF file>\n");
     323               0 :   exit(1);
     324                 : }
     325                 : 
     326                 : 
     327                 : struct Options {
     328                 :   const char *input_file;
     329                 : };
     330                 : 
     331                 : 
     332               1 : void ParseOptions(size_t argc, const char * const *argv, Options *options) {
     333               1 :   if (argc != 2)
     334               0 :     Usage();
     335                 : 
     336               1 :   options->input_file = argv[argc - 1];
     337               1 : }
     338                 : 
     339                 : 
     340               1 : int main(int argc, char **argv) {
     341                 :   Options options;
     342               1 :   ParseOptions(argc, argv, &options);
     343                 : 
     344               1 :   elf_load::Image image;
     345               1 :   elf_load::ReadImage(options.input_file, &image);
     346                 : 
     347               1 :   elf_load::Architecture architecture = elf_load::GetElfArch(image);
     348               1 :   Segment segment = elf_load::GetElfTextSegment(image);
     349                 : 
     350               1 :   vector<Error> errors;
     351               1 :   bool result = false;
     352               1 :   switch (architecture) {
     353                 :     case elf_load::X86_32:
     354               1 :       result = ValidateX86(segment, ValidateChunkIA32, &errors);
     355               1 :       break;
     356                 :     case elf_load::X86_64:
     357               1 :       result = ValidateX86(segment, ValidateChunkAMD64, &errors);
     358               1 :       break;
     359                 :     case elf_load::ARM:
     360               0 :       result = ValidateArm(segment, &errors);
     361               0 :       break;
     362                 :     default:
     363               0 :       CHECK(false);
     364                 :   }
     365                 : 
     366               1 :   for (size_t i = 0; i < errors.size(); i++) {
     367               1 :     const Error &e = errors[i];
     368               1 :     printf("%8" NACL_PRIx32 ": %s\n", e.offset, e.message.c_str());
     369               1 :   }
     370                 : 
     371               1 :   if (result) {
     372               1 :     printf("Valid.\n");
     373               1 :     return 0;
     374                 :   } else {
     375               1 :     printf("Invalid.\n");
     376               1 :     return 1;
     377                 :   }
     378               1 : }

Generated by: LCOV version 1.7