LCOV - code coverage report
Current view: directory - src/trusted/validator_mips - validator.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 187 136 72.7 %
Date: 2014-07-02 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 <assert.h>
       8                 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
       9                 : #include "native_client/src/trusted/validator_mips/validator.h"
      10                 : #include "native_client/src/include/nacl_macros.h"
      11                 : 
      12                 : 
      13                 : using nacl_mips_dec::Instruction;
      14                 : using nacl_mips_dec::ClassDecoder;
      15                 : using nacl_mips_dec::Register;
      16                 : using nacl_mips_dec::RegisterList;
      17                 : 
      18                 : using nacl_mips_dec::kInstrSize;
      19                 : using nacl_mips_dec::kInstrAlign;
      20                 : 
      21                 : using std::vector;
      22                 : 
      23                 : namespace nacl_mips_val {
      24                 : 
      25                 : /*
      26                 :  * TODO(petarj): MIPS validator and runtime port need an external security
      27                 :  * review before they are delivered in products.
      28                 :  */
      29                 : 
      30                 : /*********************************************************
      31                 :  * Implementations of patterns used in the first pass.
      32                 :  *
      33                 :  * N.B. IF YOU ADD A PATTERN HERE, REGISTER IT BELOW.
      34                 :  * See the list in apply_patterns.
      35                 :  *********************************************************/
      36                 : 
      37                 : // A possible result from a validator pattern.
      38                 : enum PatternMatch {
      39                 :   // The pattern does not apply to the instructions it was given.
      40                 :   NO_MATCH,
      41                 :   // The pattern matches, and is safe; do not allow jumps to split it.
      42                 :   PATTERN_SAFE,
      43                 :   // The pattern matches, and has detected a problem.
      44                 :   PATTERN_UNSAFE
      45                 : };
      46                 : 
      47                 : 
      48                 : /*********************************************************
      49                 :  * One instruction patterns.
      50                 :  *********************************************************/
      51                 : 
      52                 : /*
      53                 :  * Checks for instructions that are not allowed.
      54                 :  */
      55             118 : static PatternMatch CheckSafety(const SfiValidator &sfi,
      56                 :                                 const DecodedInstruction &inst,
      57                 :                                 ProblemSink *out) {
      58                 :   UNREFERENCED_PARAMETER(sfi);
      59             118 :   if (nacl_mips_dec::MAY_BE_SAFE != inst.safety()) {
      60               0 :     out->ReportProblem(inst.addr(), inst.safety(), kProblemUnsafe);
      61               0 :     return PATTERN_UNSAFE;
      62                 :   }
      63             118 :   return PATTERN_SAFE;
      64                 : }
      65                 : 
      66                 : /*
      67                 :  * Checks for instructions that alter read-only registers.
      68                 :  */
      69             118 : static PatternMatch CheckReadOnly(const SfiValidator &sfi,
      70                 :                                   const DecodedInstruction &inst,
      71                 :                                   ProblemSink *out) {
      72             118 :   if (inst.IsDestGprReg(sfi.read_only_registers())) {
      73               0 :     out->ReportProblem(inst.addr(), inst.safety(), kProblemReadOnlyRegister);
      74               0 :     return PATTERN_UNSAFE;
      75                 :   }
      76             118 :   return NO_MATCH;
      77                 : }
      78                 : 
      79                 : /*
      80                 :  * Checks the location of linking branches -- in order to be correct, the
      81                 :  * branches must be in the slot before the last slot.
      82                 :  *
      83                 :  */
      84             118 : static PatternMatch CheckCallPosition(const SfiValidator &sfi,
      85                 :                                       const DecodedInstruction &inst,
      86                 :                                       ProblemSink *out) {
      87             118 :   if (inst.IsJal()) {
      88               2 :     uint32_t end_addr = sfi.BundleForAddress(inst.addr()).EndAddr();
      89               2 :     uint32_t branch_slot = end_addr - (2 * kInstrSize);
      90               2 :     if (inst.addr() != branch_slot) {
      91               1 :       out->ReportProblem(inst.addr(), inst.safety(), kProblemMisalignedCall);
      92               1 :       return PATTERN_UNSAFE;
      93                 :     }
      94               1 :     return PATTERN_SAFE;
      95                 :   }
      96             116 :   return NO_MATCH;
      97                 : }
      98                 : 
      99                 : /*
     100                 :  * Checks for jumps to unsafe area.
     101                 :  */
     102             118 : static PatternMatch CheckJumpDestAddr(const SfiValidator &sfi,
     103                 :                                       const DecodedInstruction &instr,
     104                 :                                       ProblemSink *out) {
     105             118 :   if (instr.IsDirectJump()) {
     106               0 :     uint32_t dest_addr = instr.DestAddr();
     107               0 :     if (dest_addr < sfi.trampoline_region_start()) {
     108                 :       //  Safe guard region, allow jumps if somebody is suicidal.
     109               0 :       return PATTERN_SAFE;
     110               0 :     } else if (dest_addr < sfi.code_region_start()) {
     111                 :       //  Trampoline region, allow only 0mod16.
     112               0 :       if ((dest_addr & (sfi.bytes_per_bundle() - 1)) != 0) {
     113                 :         out->ReportProblem(instr.addr(), instr.safety(),
     114               0 :                            kProblemUnalignedJumpToTrampoline);
     115               0 :         return PATTERN_UNSAFE;
     116                 :       }
     117               0 :       return PATTERN_SAFE;
     118                 :     } else {
     119                 :       // Jumps outside of unsafe region are not allowed.
     120               0 :       if ((dest_addr & ~(sfi.code_address_mask())) != 0) {
     121                 :         out->ReportProblem(instr.addr(), instr.safety(),
     122               0 :                            kProblemBranchInvalidDest);
     123               0 :         return PATTERN_UNSAFE;
     124                 :       }
     125                 :       //  Another pattern is responsible for checking if it lands inside of a
     126                 :       //  pseudo-instruction.
     127               0 :       return PATTERN_SAFE;
     128                 :     }
     129                 :   }
     130             118 :   return NO_MATCH;
     131                 : }
     132                 : 
     133                 : /*********************************************************
     134                 :  * Two instruction patterns.
     135                 :  *********************************************************/
     136                 : 
     137                 : /*
     138                 :  * Checks if indirect jumps are guarded properly.
     139                 :  */
     140             148 : static PatternMatch CheckJmpReg(const SfiValidator &sfi,
     141                 :                                 const DecodedInstruction &first,
     142                 :                                 const DecodedInstruction &second,
     143                 :                                 ProblemSink *out) {
     144                 :   UNREFERENCED_PARAMETER(sfi);
     145             148 :   if (second.IsJmpReg()) {
     146               4 :     if (first.IsMask(second.TargetReg(), Register::JumpMask())) {
     147               2 :       return PATTERN_SAFE;
     148                 :     }
     149                 :     out->ReportProblem(second.addr(), second.safety(),
     150               2 :                        kProblemUnsafeJumpRegister);
     151               2 :     return PATTERN_UNSAFE;
     152                 :   }
     153             144 :   return NO_MATCH;
     154                 : }
     155                 : 
     156                 : 
     157                 : /*
     158                 :  * Checks if change of the data register ($sp) is followed by load/store mask.
     159                 :  */
     160             148 : static PatternMatch CheckDataRegisterUpdate(const SfiValidator &sfi,
     161                 :                                             const DecodedInstruction &first,
     162                 :                                             const DecodedInstruction &second,
     163                 :                                             ProblemSink *out) {
     164                 :   UNREFERENCED_PARAMETER(sfi);
     165             174 :   if (first.DestGprReg().Equals(Register::Sp())
     166              26 :       && !first.IsMask(first.DestGprReg(), Register::LoadStoreMask())) {
     167              15 :     if (second.IsMask(first.DestGprReg(), Register::LoadStoreMask())) {
     168               9 :       return PATTERN_SAFE;
     169                 :     }
     170               6 :     out->ReportProblem(first.addr(), first.safety(), kProblemUnsafeDataWrite);
     171               6 :     return PATTERN_UNSAFE;
     172                 :   }
     173             133 :   return NO_MATCH;
     174                 : }
     175                 : 
     176                 : /*
     177                 :  * Checks if data register ($sp) change is in the delay slot.
     178                 :  */
     179             148 : static PatternMatch CheckDataRegisterDslot(const SfiValidator &sfi,
     180                 :                                            const DecodedInstruction &first,
     181                 :                                            const DecodedInstruction &second,
     182                 :                                            ProblemSink *out) {
     183                 :   UNREFERENCED_PARAMETER(sfi);
     184             174 :   if (second.DestGprReg().Equals(Register::Sp())
     185              26 :       && !second.IsMask(second.DestGprReg(), Register::LoadStoreMask())) {
     186              15 :     if (first.HasDelaySlot()) {
     187                 :       out->ReportProblem(second.addr(), second.safety(),
     188               0 :                          kProblemDataRegInDelaySlot);
     189               0 :       return PATTERN_UNSAFE;
     190                 :     }
     191                 :   }
     192             148 :   return NO_MATCH;
     193                 : }
     194                 : 
     195                 : /*
     196                 :  * Checks if load and store instructions are preceded by load/store mask.
     197                 :  */
     198             148 : static PatternMatch CheckLoadStore(const SfiValidator &sfi,
     199                 :                                    const DecodedInstruction &first,
     200                 :                                    const DecodedInstruction &second,
     201                 :                                    ProblemSink *out) {
     202             148 :   if (second.IsLoadStore()) {
     203              16 :     Register base_addr_reg = second.BaseAddressRegister();
     204              16 :     if (!sfi.data_address_registers().
     205              16 :            ContainsAll(RegisterList(base_addr_reg))) {
     206              10 :       if (first.IsMask(base_addr_reg, Register::LoadStoreMask())) {
     207               5 :         return PATTERN_SAFE;
     208                 :       }
     209                 :       out->ReportProblem(second.addr(), second.safety(),
     210               5 :                          kProblemUnsafeLoadStore);
     211               5 :       return PATTERN_UNSAFE;
     212                 :     }
     213                 :   }
     214             138 :   return NO_MATCH;
     215                 : }
     216                 : 
     217                 : 
     218                 : /*
     219                 :  * Checks if there is jump/branch in the delay slot.
     220                 :  */
     221             148 : static PatternMatch CheckBranchInDelaySlot(const SfiValidator &sfi,
     222                 :                                    const DecodedInstruction &first,
     223                 :                                    const DecodedInstruction &second,
     224                 :                                    ProblemSink *out) {
     225                 :   UNREFERENCED_PARAMETER(sfi);
     226             148 :   if (first.HasDelaySlot() && second.HasDelaySlot()) {
     227                 :     out->ReportProblem(second.addr(), second.safety(),
     228               0 :                        kProblemBranchInDelaySlot);
     229               0 :     return PATTERN_UNSAFE;
     230                 :   }
     231             148 :   return NO_MATCH;
     232                 : }
     233                 : 
     234                 : 
     235                 : /*********************************************************
     236                 :  * Pseudo-instruction patterns.
     237                 :  *********************************************************/
     238                 : 
     239                 : /*
     240                 :  * Checks if a pseudo-instruction that starts with instr will cross bundle
     241                 :  * border (i.e. if it starts in one and ends in second).
     242                 :  * The exception to this rule are pseudo-instructions altering the data register
     243                 :  * value (because mask is the second instruction).
     244                 :  */
     245               6 : static PatternMatch CheckBundleCross(const SfiValidator &sfi,
     246                 :                                      const DecodedInstruction instr,
     247                 :                                      ProblemSink *out) {
     248               6 :   uint32_t begin_addr = sfi.BundleForAddress(instr.addr()).BeginAddr();
     249               6 :   if ((instr.addr() == begin_addr) && !instr.IsDataRegMask()) {
     250                 :     out->ReportProblem(instr.addr(), instr.safety(),
     251               0 :                        kProblemPatternCrossesBundle);
     252               0 :     return PATTERN_UNSAFE;
     253                 :   }
     254               6 :   return PATTERN_SAFE;
     255                 : }
     256                 : 
     257                 : /*
     258                 :  * Checks if branch instruction will jump in the middle of pseudo-instruction.
     259                 :  */
     260               6 : static PatternMatch CheckJumpToPseudo(const SfiValidator &sfi,
     261                 :                                       const std::vector<CodeSegment> &segms,
     262                 :                                       const DecodedInstruction pseudoinstr,
     263                 :                                       const AddressSet &branches,
     264                 :                                       const AddressSet &branch_targets,
     265                 :                                       ProblemSink *out) {
     266               6 :   uint32_t target_va = pseudoinstr.addr();
     267               6 :   if (branch_targets.Contains(target_va)) {
     268               0 :     std::vector<DecodedInstruction> instrs;
     269               0 :     if (sfi.FindBranch(segms, branches, target_va, &instrs)) {
     270               0 :       for (uint32_t i = 0; i < instrs.size(); i++) {
     271               0 :         out->ReportProblem(instrs[i].addr(), instrs[i].safety(),
     272               0 :                            kProblemBranchSplitsPattern);
     273                 :       }
     274               0 :       return PATTERN_UNSAFE;
     275                 :     } else {
     276               0 :       assert(0);
     277               0 :     }
     278                 :   }
     279               6 :   return PATTERN_SAFE;
     280                 : }
     281                 : 
     282                 : 
     283                 : /*********************************************************
     284                 :  *
     285                 :  * Implementation of SfiValidator itself.
     286                 :  *
     287                 :  *********************************************************/
     288              10 : SfiValidator::SfiValidator(uint32_t bytes_per_bundle,
     289                 :                            uint32_t code_region_bytes,
     290                 :                            uint32_t data_region_bytes,
     291                 :                            RegisterList read_only_registers,
     292                 :                            RegisterList data_address_registers)
     293                 :     : bytes_per_bundle_(bytes_per_bundle),
     294                 :       code_region_bytes_(code_region_bytes),
     295                 :       data_address_mask_(~(data_region_bytes - 1)),
     296                 :       code_address_mask_((code_region_bytes - 1) & kInstrAlign),  // 0x0FFFFFFC
     297                 :       read_only_registers_(read_only_registers),
     298                 :       data_address_registers_(data_address_registers),
     299              10 :       decode_state_(nacl_mips_dec::init_decode()),
     300              20 :       is_position_independent_(false) {}
     301                 : 
     302              30 : bool SfiValidator::Validate(const vector<CodeSegment> &segments,
     303                 :                             ProblemSink *out) {
     304              30 :   uint32_t base = segments[0].BeginAddr();
     305              30 :   uint32_t size = segments.back().EndAddr() - base;
     306              30 :   AddressSet branches(base, size);
     307              30 :   AddressSet branch_targets(base, size);
     308              30 :   AddressSet critical(base, size);
     309                 : 
     310              30 :   bool complete_success = true;
     311                 : 
     312             120 :   for (vector<CodeSegment>::const_iterator it = segments.begin();
     313              60 :       it != segments.end(); ++it) {
     314              30 :     complete_success &= ValidateFallthrough(*it, out, &branches,
     315              60 :                                             &branch_targets, &critical);
     316                 : 
     317              30 :     if (!out->ShouldContinue()) {
     318               0 :       return false;
     319                 :     }
     320                 :   }
     321                 : 
     322              30 :   if (segments.size() == 1) {
     323              30 :     bool external_target_addr = false;
     324              60 :     for (AddressSet::Iterator it = branch_targets.Begin();
     325              30 :          !it.Equals(branch_targets.End()); it.Next()) {
     326               0 :         if (!segments[0].ContainsAddress(it.GetAddress())) {
     327               0 :           external_target_addr = true;
     328               0 :           break;
     329                 :         }
     330                 :     }
     331              30 :     is_position_independent_ = !external_target_addr;
     332                 :   }
     333                 : 
     334                 :   complete_success &= ValidatePseudos(*this, segments,
     335              30 :                                       branches, branch_targets, critical, out);
     336              30 :   return complete_success;
     337                 : }
     338                 : 
     339              30 : bool SfiValidator::ValidateFallthrough(const CodeSegment &segment,
     340                 :                                        ProblemSink *out,
     341                 :                                        AddressSet *branches,
     342                 :                                        AddressSet *branch_targets,
     343                 :                                        AddressSet *critical) {
     344              30 :   bool complete_success = true;
     345                 : 
     346              30 :   nacl_mips_dec::Forbidden initial_decoder;
     347                 :   // Initialize the previous instruction to a syscall, so patterns all fail.
     348                 :   DecodedInstruction prev(
     349                 :       0,         // Virtual address 0, which will be in a different bundle.
     350                 :       Instruction(0x0000000c),  // syscall.
     351              30 :       initial_decoder);         // and ensure that it decodes as Forbidden.
     352                 : 
     353             148 :   for (uint32_t va = segment.BeginAddr(); va != segment.EndAddr();
     354                 :        va += kInstrSize) {
     355                 :     DecodedInstruction inst(va, segment[va],
     356             118 :                             nacl_mips_dec::decode(segment[va], decode_state_));
     357                 : 
     358             118 :     complete_success &= ApplyPatterns(inst, out);
     359             118 :     if (!out->ShouldContinue()) return false;
     360                 : 
     361             118 :     complete_success &= ApplyPatterns(prev, inst, critical, out);
     362             118 :     if (!out->ShouldContinue()) return false;
     363                 : 
     364             118 :     if (inst.IsDirectJump()) {
     365               0 :       branches->Add(inst.addr());
     366               0 :       branch_targets->Add(inst.DestAddr());
     367                 :     }
     368                 : 
     369             118 :     prev = inst;
     370                 :   }
     371                 : 
     372                 :   // Validate the last instruction, paired with a nop.
     373              30 :   const Instruction nop(nacl_mips_dec::kNop);
     374                 :   DecodedInstruction one_past_end(segment.EndAddr(), nop,
     375              30 :                                   nacl_mips_dec::decode(nop, decode_state_));
     376              30 :   complete_success &= ApplyPatterns(prev, one_past_end, critical, out);
     377                 : 
     378              30 :   return complete_success;
     379                 : }
     380                 : 
     381              30 : bool SfiValidator::ValidatePseudos(const SfiValidator &sfi,
     382                 :                                    const std::vector<CodeSegment> &segments,
     383                 :                                    const AddressSet &branches,
     384                 :                                    const AddressSet &branch_targets,
     385                 :                                    const AddressSet &critical,
     386                 :                                    ProblemSink* out) {
     387              30 :   bool complete_success = true;
     388              30 :   vector<CodeSegment>::const_iterator seg_it = segments.begin();
     389                 : 
     390              36 :   for (AddressSet::Iterator it = critical.Begin(); !it.Equals(critical.End());
     391                 :        it.Next()) {
     392               6 :     uint32_t va = it.GetAddress();
     393                 : 
     394              12 :     while (!seg_it->ContainsAddress(va)) {
     395               0 :       ++seg_it;
     396                 :     }
     397                 : 
     398               6 :     const CodeSegment &segment = *seg_it;
     399                 :     DecodedInstruction inst_p(va,
     400                 :                               segment[va],
     401                 :                               nacl_mips_dec::decode(segment[va],
     402               6 :                               decode_state_));
     403                 : 
     404                 :     //  Check if the pseudo-instruction will cross bundle borders.
     405               6 :     complete_success &= CheckBundleCross(sfi, inst_p, out);
     406                 : 
     407                 :     //  Check if direct jumps destination is inside of a pseudo-instruction.
     408                 :     complete_success &= CheckJumpToPseudo(sfi, segments, inst_p, branches,
     409               6 :                                           branch_targets, out);
     410                 :   }
     411                 : 
     412              30 :   return complete_success;
     413                 : }
     414                 : 
     415                 : 
     416             118 : bool SfiValidator::ApplyPatterns(const DecodedInstruction &inst,
     417                 :     ProblemSink *out) {
     418                 :   //  Single-instruction patterns.
     419                 :   typedef PatternMatch (*OneInstPattern)(const SfiValidator &,
     420                 :                                          const DecodedInstruction &,
     421                 :                                          ProblemSink *out);
     422                 :   static const OneInstPattern one_inst_patterns[] = {
     423                 :     &CheckSafety,
     424                 :     &CheckReadOnly,
     425                 :     &CheckCallPosition,
     426                 :     &CheckJumpDestAddr
     427                 :   };
     428                 : 
     429             118 :   bool complete_success = true;
     430                 : 
     431             590 :   for (uint32_t i = 0; i < NACL_ARRAY_SIZE(one_inst_patterns); i++) {
     432             472 :     PatternMatch r = one_inst_patterns[i](*this, inst, out);
     433             472 :     switch (r) {
     434                 :       case PATTERN_SAFE:
     435                 :       case NO_MATCH:
     436             471 :         break;
     437                 : 
     438                 :       case PATTERN_UNSAFE:
     439               1 :         complete_success = false;
     440               1 :         break;
     441                 :     }
     442                 :   }
     443             118 :   return complete_success;
     444                 : }
     445                 : 
     446             148 : bool SfiValidator::ApplyPatterns(const DecodedInstruction &first,
     447                 :     const DecodedInstruction &second, AddressSet *critical, ProblemSink *out) {
     448                 :   //  Type for two-instruction pattern functions.
     449                 :   typedef PatternMatch (*TwoInstPattern)(const SfiValidator &,
     450                 :                                          const DecodedInstruction &first,
     451                 :                                          const DecodedInstruction &second,
     452                 :                                          ProblemSink *out);
     453                 :   //  The list of patterns -- defined in static functions up top.
     454                 :   static const TwoInstPattern two_inst_patterns[] = {
     455                 :     &CheckJmpReg,
     456                 :     &CheckDataRegisterUpdate,
     457                 :     &CheckDataRegisterDslot,
     458                 :     &CheckLoadStore,
     459                 :     &CheckBranchInDelaySlot
     460                 :   };
     461                 : 
     462             148 :   bool complete_success = true;
     463                 : 
     464             888 :   for (uint32_t i = 0; i < NACL_ARRAY_SIZE(two_inst_patterns); i++) {
     465             740 :     PatternMatch r = two_inst_patterns[i](*this, first, second, out);
     466             740 :     switch (r) {
     467                 :       case NO_MATCH:
     468             711 :          break;
     469                 :       case PATTERN_UNSAFE:
     470                 :         // Pattern is in charge of reporting specific issue.
     471              13 :         complete_success = false;
     472              13 :         break;
     473                 :       case PATTERN_SAFE:
     474              16 :         critical->Add(second.addr());
     475              16 :         break;
     476                 :     }
     477                 :   }
     478             148 :   return complete_success;
     479                 : }
     480                 : 
     481              31 : bool SfiValidator::IsDataAddressRegister(Register reg) const {
     482              31 :   return data_address_registers_[reg];
     483                 : }
     484                 : 
     485               0 : bool SfiValidator::IsBundleHead(uint32_t address) const {
     486               0 :   return (address % bytes_per_bundle_) == 0;
     487                 : }
     488                 : 
     489              25 : const Bundle SfiValidator::BundleForAddress(uint32_t address) const {
     490              25 :   uint32_t base = address - (address % bytes_per_bundle_);
     491              25 :   return Bundle(base, bytes_per_bundle_);
     492                 : }
     493                 : 
     494               0 : bool SfiValidator::FindBranch(const std::vector<CodeSegment> &segments,
     495                 :                               const AddressSet &branches,
     496                 :                               uint32_t dest_address,
     497                 :                               std::vector<DecodedInstruction> *instrs) const {
     498               0 :   vector<CodeSegment>::const_iterator seg_it = segments.begin();
     499                 : 
     500               0 :   for (AddressSet::Iterator it = branches.Begin(); !it.Equals(branches.End());
     501                 :        it.Next()) {
     502               0 :     uint32_t va = it.GetAddress();
     503                 : 
     504               0 :     while (!seg_it->ContainsAddress(va)) {
     505               0 :       ++seg_it;
     506                 :     }
     507                 : 
     508               0 :     const CodeSegment &segment = *seg_it;
     509                 :     DecodedInstruction instr = DecodedInstruction(va, segment[va],
     510               0 :                              nacl_mips_dec::decode(segment[va], decode_state_));
     511               0 :     if (instr.DestAddr() == dest_address) {
     512               0 :       instrs->push_back(instr);
     513                 :     }
     514                 :   }
     515               0 :   if (!instrs->empty()) return true;
     516               0 :   return false;
     517                 : }
     518                 : /*
     519                 :  * DecodedInstruction.
     520                 :  */
     521             184 : DecodedInstruction::DecodedInstruction(uint32_t vaddr,
     522                 :                                        Instruction inst,
     523                 :                                        const ClassDecoder &decoder)
     524                 :     : vaddr_(vaddr),
     525                 :       inst_(inst),
     526                 :       decoder_(&decoder),
     527             184 :       safety_(decoder.safety(inst_))
     528             184 : {}
     529                 : 
     530                 : }  // namespace nacl_mips_val

Generated by: LCOV version 1.7