LCOV - code coverage report
Current view: directory - src/trusted/validator/driver - ncval.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 178 133 74.7 %
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                 : #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           82214 :   Jump(uint32_t from, uint32_t to) : from(from), to(to) {}
      43                 : };
      44                 : 
      45                 : 
      46            5284 : struct Error {
      47                 :   uint32_t offset;
      48                 :   string message;
      49            1584 :   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           10360 :   UserData(Segment segment,
      59           10360 :            vector<Error> *errors,
      60           10360 :            vector<Jump> *jumps,
      61           10360 :            set<uint32_t> *bad_jump_targets)
      62                 :       : segment(segment),
      63                 :         errors(errors),
      64                 :         jumps(jumps),
      65            5180 :         bad_jump_targets(bad_jump_targets) {
      66           10360 :   }
      67                 : };
      68                 : 
      69                 : 
      70                 : Bool ProcessInstruction(
      71          502535 :     const uint8_t *begin, const uint8_t *end,
      72          502535 :     uint32_t validation_info, void *user_data_ptr) {
      73                 : 
      74          502535 :   UserData &user_data = *reinterpret_cast<UserData *>(user_data_ptr);
      75          502535 :   vector<Error> &errors = *user_data.errors;
      76                 : 
      77          502535 :   uint32_t offset = user_data.segment.vaddr +
      78                 :                     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          502535 :   if ((validation_info & DIRECT_JUMP_OUT_OF_RANGE) == 0) {
      85          502514 :     uint32_t program_counter = user_data.segment.vaddr +
      86                 :                                static_cast<uint32_t>(end -
      87                 :                                                      user_data.segment.data);
      88          502514 :     if (validation_info & RELATIVE_8BIT) {
      89           33030 :       program_counter += reinterpret_cast<const int8_t *>(end)[-1];
      90           33030 :       user_data.jumps->push_back(Jump(offset, program_counter));
      91          502514 :     } else if (validation_info & RELATIVE_32BIT) {
      92            8077 :       program_counter += reinterpret_cast<const int32_t *>(end)[-1];
      93            8077 :       user_data.jumps->push_back(Jump(offset, program_counter));
      94            8077 :     }
      95          502514 :   }
      96                 : 
      97          502535 :   Bool result = (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET))
      98                 :                 ? FALSE
      99                 :                 : 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          502535 :   if (validation_info & UNRECOGNIZED_INSTRUCTION) {
     109             449 :     validation_info &= ~UNRECOGNIZED_INSTRUCTION;
     110            2694 :     errors.push_back(Error(offset, "unrecognized instruction"));
     111             449 :   }
     112                 : 
     113          502535 :   if (validation_info & DIRECT_JUMP_OUT_OF_RANGE) {
     114              21 :     validation_info &= ~DIRECT_JUMP_OUT_OF_RANGE;
     115             126 :     errors.push_back(Error(offset, "direct jump out of range"));
     116              21 :   }
     117                 : 
     118          502535 :   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               0 :   }
     122                 : 
     123          502535 :   if (validation_info & FORBIDDEN_BASE_REGISTER) {
     124              21 :     validation_info &= ~FORBIDDEN_BASE_REGISTER;
     125             126 :     errors.push_back(Error(offset, "improper memory address - bad base"));
     126              21 :   }
     127                 : 
     128          502535 :   if (validation_info & UNRESTRICTED_INDEX_REGISTER) {
     129              31 :     validation_info &= ~UNRESTRICTED_INDEX_REGISTER;
     130             186 :     errors.push_back(Error(offset, "improper memory address - bad index"));
     131              31 :   }
     132                 : 
     133          502535 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     134                 :       RESTRICTED_RBP_UNPROCESSED) {
     135              22 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     136             132 :     errors.push_back(Error(offset, "improper %rbp sandboxing"));
     137              22 :   }
     138                 : 
     139          502535 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     140                 :       UNRESTRICTED_RBP_PROCESSED) {
     141               6 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     142              36 :     errors.push_back(Error(offset, "improper %rbp sandboxing"));
     143               6 :   }
     144                 : 
     145          502535 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     146                 :       RESTRICTED_RSP_UNPROCESSED) {
     147               3 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     148              18 :     errors.push_back(Error(offset, "improper %rsp sandboxing"));
     149               3 :   }
     150                 : 
     151          502535 :   if ((validation_info & BAD_RSP_RBP_PROCESSING_MASK) ==
     152                 :       UNRESTRICTED_RSP_PROCESSED) {
     153              11 :     validation_info &= ~BAD_RSP_RBP_PROCESSING_MASK;
     154              66 :     errors.push_back(Error(offset, "improper %rsp sandboxing"));
     155              11 :   }
     156                 : 
     157          502535 :   if (validation_info & R15_MODIFIED) {
     158               8 :     validation_info &= ~R15_MODIFIED;
     159              48 :     errors.push_back(Error(offset, "error - %r15 is changed"));
     160               8 :   }
     161                 : 
     162          502535 :   if (validation_info & BP_MODIFIED) {
     163              31 :     validation_info &= ~BP_MODIFIED;
     164             186 :     errors.push_back(Error(offset, "error - %bpl or %bp is changed"));
     165              31 :   }
     166                 : 
     167          502535 :   if (validation_info & SP_MODIFIED) {
     168              24 :     validation_info &= ~SP_MODIFIED;
     169             144 :     errors.push_back(Error(offset, "error - %spl or %sp is changed"));
     170              24 :   }
     171                 : 
     172          502535 :   if (validation_info & BAD_CALL_ALIGNMENT) {
     173             106 :     validation_info &= ~BAD_CALL_ALIGNMENT;
     174             636 :     errors.push_back(Error(offset, "warning - bad call alignment"));
     175             106 :   }
     176                 : 
     177          502535 :   if (validation_info & BAD_JUMP_TARGET) {
     178            2268 :     validation_info &= ~BAD_JUMP_TARGET;
     179            2268 :     user_data.bad_jump_targets->insert(offset);
     180            2268 :   }
     181                 : 
     182          502535 :   if (validation_info & VALIDATION_ERRORS_MASK) {
     183               0 :     errors.push_back(Error(offset, "<some other error>"));
     184               0 :   }
     185                 : 
     186          502535 :   return result;
     187               0 : }
     188                 : 
     189                 : 
     190                 : Bool ProcessError(
     191            2879 :     const uint8_t *begin, const uint8_t *end,
     192            2879 :     uint32_t validation_info, void *user_data_ptr) {
     193            5758 :   UNREFERENCED_PARAMETER(begin);
     194            5758 :   UNREFERENCED_PARAMETER(end);
     195            5758 :   UNREFERENCED_PARAMETER(validation_info);
     196            5758 :   UNREFERENCED_PARAMETER(user_data_ptr);
     197            2879 :   return FALSE;
     198                 : }
     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            5180 :     const Segment &segment,
     211            5180 :     ValidateChunkFunc validate_chunk,
     212            5180 :     vector<Error> *errors) {
     213                 : 
     214            5180 :   errors->clear();
     215                 : 
     216            5180 :   if (segment.vaddr % kBundleSize != 0) {
     217               0 :     errors->push_back(Error(
     218                 :         segment.vaddr,
     219                 :         "Text segment offset in memory is not bundle-aligned."));
     220               0 :     return false;
     221                 :   }
     222                 : 
     223            5180 :   if (segment.size % kBundleSize != 0) {
     224               0 :     char buf[100];
     225               0 :     SNPRINTF(buf, sizeof buf,
     226                 :              "Text segment size (0x%" NACL_PRIx32 ") is not "
     227                 :              "multiple of bundle size.",
     228                 :              segment.size);
     229               0 :     errors->push_back(Error(segment.vaddr + segment.size, buf));
     230               0 :     return false;
     231                 :   }
     232                 : 
     233           10360 :   vector<Jump> jumps;
     234            5180 :   set<uint32_t> bad_jump_targets;
     235                 : 
     236            5180 :   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           10360 :   Bool result = validate_chunk(
     243                 :       segment.data, segment.size,
     244                 :       CALL_USER_CALLBACK_ON_EACH_INSTRUCTION, &kFullCPUIDFeatures,
     245                 :       ProcessInstruction, &user_data);
     246                 : 
     247                 :   // Report destinations of jumps that lead to bad targets.
     248          138861 :   for (size_t i = 0; i < jumps.size(); i++) {
     249           82214 :     const Jump &jump = jumps[i];
     250           82214 :     if (bad_jump_targets.count(jump.to) > 0)
     251             354 :       errors->push_back(Error(jump.from, "bad jump target"));
     252           41107 :   }
     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           25900 :   CHECK(result == validate_chunk(
     259                 :       segment.data, segment.size,
     260                 :       0, &kFullCPUIDFeatures,
     261                 :       ProcessError, NULL));
     262                 : 
     263            5180 :   return static_cast<bool>(result);
     264           15540 : }
     265                 : 
     266                 : 
     267               0 : class NcvalArmProblemReporter : public ProblemReporter {
     268                 :  public:
     269               0 :   explicit NcvalArmProblemReporter(vector<Error> *errors) : errors(errors) {}
     270                 : 
     271                 :  protected:
     272               0 :   void ReportProblemMessage(nacl_arm_dec::Violation violation,
     273               0 :                             uint32_t vaddr,
     274               0 :                             const char* message) {
     275               0 :     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               0 :       errors->push_back(
     280                 :           Error(vaddr, "Too may errors: supressing remaining errors."));
     281               0 :     }
     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               0 :   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               0 :   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               0 :       nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Tp()),
     312               0 :       nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Sp()),
     313                 :       &cpu_features);
     314                 : 
     315               0 :   NcvalArmProblemReporter reporter(errors);
     316               0 :   return validator.validate(segments, &reporter);
     317               0 : }
     318                 : 
     319                 : 
     320                 : void Usage() {
     321               0 :   printf("Usage:\n");
     322               0 :   printf("    ncval <ELF file>\n");
     323               0 :   exit(1);
     324               0 : }
     325                 : 
     326                 : 
     327                 : struct Options {
     328                 :   const char *input_file;
     329                 : };
     330                 : 
     331                 : 
     332            5180 : void ParseOptions(size_t argc, const char * const *argv, Options *options) {
     333            5180 :   if (argc != 2)
     334               0 :     Usage();
     335                 : 
     336            5180 :   options->input_file = argv[argc - 1];
     337            5180 : }
     338                 : 
     339                 : 
     340            5180 : int main(int argc, char **argv) {
     341            5180 :   Options options;
     342            5180 :   ParseOptions(argc, argv, &options);
     343                 : 
     344           10360 :   elf_load::Image image;
     345            5180 :   elf_load::ReadImage(options.input_file, &image);
     346                 : 
     347           10360 :   elf_load::Architecture architecture = elf_load::GetElfArch(image);
     348           10360 :   Segment segment = elf_load::GetElfTextSegment(image);
     349                 : 
     350           10360 :   vector<Error> errors;
     351            5180 :   bool result = false;
     352            5180 :   switch (architecture) {
     353                 :     case elf_load::X86_32:
     354            1252 :       result = ValidateX86(segment, ValidateChunkIA32, &errors);
     355             626 :       break;
     356                 :     case elf_load::X86_64:
     357            9108 :       result = ValidateX86(segment, ValidateChunkAMD64, &errors);
     358            4554 :       break;
     359                 :     case elf_load::ARM:
     360               0 :       result = ValidateArm(segment, &errors);
     361               0 :       break;
     362                 :     default:
     363               0 :       CHECK(false);
     364               0 :   }
     365                 : 
     366           17916 :   for (size_t i = 0; i < errors.size(); i++) {
     367            1584 :     const Error &e = errors[i];
     368            1584 :     printf("%8" NACL_PRIx32 ": %s\n", e.offset, e.message.c_str());
     369             792 :   }
     370                 : 
     371            5180 :   if (result) {
     372            2380 :     printf("Valid.\n");
     373            2380 :     return 0;
     374                 :   } else {
     375            2800 :     printf("Invalid.\n");
     376           13160 :     return 1;
     377                 :   }
     378            5180 : }

Generated by: LCOV version 1.7