LCOV - code coverage report
Current view: directory - src/trusted/validator/x86/ncval_seg_sfi - ncvalidate.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 589 3 0.5 %
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                 : /*
       8                 :  * ncvalidate.c
       9                 :  * Validate x86 instructions for Native Client
      10                 :  *
      11                 :  */
      12                 : 
      13                 : #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.h"
      14                 : 
      15                 : #include <stdio.h>
      16                 : #include <stdlib.h>
      17                 : #include <errno.h>
      18                 : #include <string.h>
      19                 : #include <assert.h>
      20                 : 
      21                 : #include "native_client/src/include/portability.h"
      22                 : #include "native_client/src/shared/platform/nacl_check.h"
      23                 : #include "native_client/src/trusted/validator/x86/halt_trim.h"
      24                 : #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncdecode.h"
      25                 : #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate_internaltypes.h"
      26                 : 
      27                 : #include "native_client/src/trusted/validator/x86/ncinstbuffer_inl.c"
      28                 : #include "native_client/src/trusted/validator/x86/x86_insts_inl.c"
      29                 : 
      30                 : #if NACL_TARGET_SUBARCH == 64
      31                 : #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixmask_64.h"
      32                 : #else
      33                 : #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixmask_32.h"
      34                 : #endif
      35                 : 
      36                 : /* debugging stuff */
      37                 : #define DEBUGGING 0
      38                 : #if DEBUGGING
      39                 : #define dprint(args)        do { printf args; } while (0)
      40                 : #else
      41                 : #define dprint(args)        do { if (0) { printf args; } } while (0)
      42                 : /* allows DCE but compiler can still do format string checks */
      43                 : #endif  /* DEBUGGING */
      44                 : 
      45                 : /* TODO(bradchen) verbosity needs to be controllable via commandline flags */
      46                 : #define VERBOSE 1
      47                 : #if VERBOSE
      48                 : #define vprint(vstate, args) \
      49                 :   do { \
      50                 :     NaClErrorReporter* reporter = vstate->dstate.error_reporter; \
      51                 :     (*reporter->printf) args;                                    \
      52                 :   } while (0)
      53                 : #else
      54                 : #define vprint(vstate, args) \
      55                 :   do { \
      56                 :     if (0) { \
      57                 :       NaClErrorReporter* reporter = vstate->dstate.error_reporter; \
      58                 :       (*reporter->printf) args;                                    \
      59                 :     } \
      60                 :   } while (0)
      61                 : /* allows DCE but compiler can still do format string checks */
      62                 : #endif  /* VERBOSE */
      63                 : 
      64                 : static const uint8_t kNaClFullStop = 0xf4;   /* x86 HALT opcode */
      65                 : 
      66                 : /* Define how many diagnostic error messages are printed by the validator.
      67                 :  * A value of zero generates no diagnostics.
      68                 :  * A value >0 allows up to that many diagnostic error messages.
      69                 :  * A negative value prints all diagnostic error messages.
      70                 :  */
      71                 : static int kMaxDiagnostics = 0;
      72                 : 
      73                 : /* This flag controls a mode for testing only in which inter-instruction
      74                 :  * checks are disabled.
      75                 :  */
      76                 : static Bool NACL_FLAG_unsafe_single_inst32_mode = FALSE;
      77                 : 
      78                 : int NCValidatorGetMaxDiagnostics(void) {
      79               0 :   return kMaxDiagnostics;
      80                 : }
      81                 : 
      82             569 : void NCValidatorSetMaxDiagnostics(int new_value) {
      83             569 :   kMaxDiagnostics = new_value;
      84             569 : }
      85                 : 
      86               0 : int NCValidatorDidStubOut(struct NCValidatorState *vstate) {
      87               0 :   return vstate->stats.didstubout;
      88                 : }
      89                 : 
      90                 : /* This function is intended to only be called by ValidatePrintInstructionError
      91                 :  * and ValidatePrintOffsetError.
      92                 :  */
      93               0 : static void ValidatePrintError(const NaClPcAddress addr,
      94               0 :                                const char *msg,
      95               0 :                                struct NCValidatorState *vstate) {
      96               0 :   if (vstate->num_diagnostics != 0) {
      97               0 :     NaClErrorReporter* reporter = vstate->dstate.error_reporter;
      98               0 :     (*reporter->printf)(
      99                 :         reporter,
     100                 :         "VALIDATOR: %"NACL_PRIxNaClPcAddress": %s\n", addr, msg);
     101               0 :     --(vstate->num_diagnostics);
     102               0 :     if (vstate->num_diagnostics == 0) {
     103               0 :       (*reporter->printf)(
     104                 :           reporter,
     105                 :           "VALIDATOR: Error limit reached, turning off diagnostics!\n");
     106               0 :     }
     107               0 :   }
     108               0 : }
     109                 : 
     110               0 : static void ValidatePrintInstructionError(const struct NCDecoderInst *dinst,
     111               0 :                                           const char *msg,
     112               0 :                                           struct NCValidatorState *vstate) {
     113               0 :   ValidatePrintError(NCPrintableInstructionAddress(dinst), msg, vstate);
     114               0 : }
     115                 : 
     116               0 : static void ValidatePrintOffsetError(const NaClPcAddress addr,
     117               0 :                                      const char *msg,
     118               0 :                                      struct NCValidatorState *vstate) {
     119               0 :   ValidatePrintError(vstate->iadrbase + addr, msg, vstate);
     120               0 : }
     121                 : 
     122                 : /* The low-level implementation for stubbing out an instruction. Always use
     123                 :  * this function to (ultimately) stub out instructions. This makes it possible
     124                 :  * to detect when the validator modifies the code.
     125                 :  */
     126               0 : static void NCStubOutMem(struct NCValidatorState *vstate, void *ptr,
     127               0 :                          size_t num) {
     128               0 :   vstate->stats.didstubout = 1;
     129               0 :   memset(ptr, kNaClFullStop, num);
     130               0 : }
     131                 : 
     132               0 : void NCBadInstructionError(const struct NCDecoderInst *dinst,
     133               0 :                            const char *msg) {
     134               0 :   NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     135               0 :   ValidatePrintInstructionError(dinst, msg, vstate);
     136               0 :   if (vstate->do_stub_out) {
     137               0 :     NCStubOutMem(vstate, dinst->dstate->memory.mpc,
     138                 :                  dinst->dstate->memory.read_length);
     139               0 :   }
     140               0 : }
     141                 : 
     142                 : /* opcode histogram */
     143                 : #if VERBOSE == 1
     144               0 : void OpcodeHisto(const uint8_t byte1, struct NCValidatorState *vstate) {
     145               0 :   vstate->opcodehisto[byte1] += 1;
     146               0 : }
     147                 : 
     148               0 : static void InitOpcodeHisto(struct NCValidatorState *vstate) {
     149               0 :   int i;
     150               0 :   for (i = 0; i < 256; i += 1) vstate->opcodehisto[i] = 0;
     151               0 : }
     152                 : 
     153               0 : static void PrintOpcodeHisto(struct NCValidatorState *vstate) {
     154               0 :   int i;
     155               0 :   int printed_in_this_row = 0;
     156               0 :   NaClErrorReporter* reporter = vstate->dstate.error_reporter;
     157                 :   if (!VERBOSE) return;
     158               0 :   (*reporter->printf)(reporter, "\nOpcode Histogram;\n");
     159               0 :   for (i = 0; i < 256; ++i) {
     160               0 :     if (0 != vstate->opcodehisto[i]) {
     161               0 :       (*reporter->printf)(reporter, "%d\t0x%02x\t", vstate->opcodehisto[i], i);
     162               0 :       ++printed_in_this_row;
     163               0 :       if (printed_in_this_row > 3) {
     164               0 :         printed_in_this_row = 0;
     165               0 :         (*reporter->printf)(reporter, "\n");
     166               0 :       }
     167               0 :     }
     168               0 :   }
     169               0 :   if (0 != printed_in_this_row) {
     170               0 :     (*reporter->printf)(reporter, "\n");
     171               0 :   }
     172               0 : }
     173                 : #else
     174                 : #define OpcodeHisto(b, v)
     175                 : #define InitOpcodeHisto(v)
     176                 : #define PrintOpcodeHisto(f, v)
     177                 : #endif /* VERBOSE == 1 */
     178                 : 
     179                 : /* statistics code */
     180               0 : static void NCStatsInst(struct NCValidatorState *vstate) {
     181               0 :   vstate->stats.instructions += 1;
     182               0 : }
     183                 : 
     184               0 : static void NCStatsCheckTarget(struct NCValidatorState *vstate) {
     185               0 :   vstate->stats.checktarget += 1;
     186               0 : }
     187                 : 
     188               0 : static void NCStatsTargetIndirect(struct NCValidatorState *vstate) {
     189               0 :   vstate->stats.targetindirect += 1;
     190               0 : }
     191                 : 
     192               0 : static void NCStatsSawFailure(struct NCValidatorState *vstate) {
     193               0 :   vstate->stats.sawfailure = 1;
     194               0 : }
     195                 : 
     196               0 : void NCStatsInternalError(struct NCValidatorState *vstate) {
     197               0 :   vstate->stats.internalerrors += 1;
     198               0 :   NCStatsSawFailure(vstate);
     199               0 : }
     200                 : 
     201               0 : void NCStatsBadAlignment(struct NCValidatorState *vstate) {
     202               0 :   vstate->stats.badalignment += 1;
     203               0 :   NCStatsSawFailure(vstate);
     204               0 : }
     205                 : 
     206               0 : static void NCStatsSegFault(struct NCValidatorState *vstate) {
     207               0 :   vstate->stats.segfaults += 1;
     208               0 :   NCStatsSawFailure(vstate);
     209               0 : }
     210                 : 
     211               0 : static void NCStatsNewSegment(struct NCValidatorState *vstate) {
     212               0 :   vstate->stats.segments += 1;
     213               0 :   if (vstate->stats.segments > 1) {
     214               0 :     vprint(vstate, (reporter, "error: multiple segments\n"));
     215               0 :     NCStatsSawFailure(vstate);
     216               0 :   }
     217               0 : }
     218                 : 
     219               0 : void NCStatsBadTarget(struct NCValidatorState *vstate) {
     220               0 :   vstate->stats.badtarget += 1;
     221               0 :   NCStatsSawFailure(vstate);
     222               0 : }
     223                 : 
     224               0 : static void NCStatsUnsafeIndirect(struct NCValidatorState *vstate) {
     225               0 :   vstate->stats.unsafeindirect += 1;
     226               0 :   NCStatsSawFailure(vstate);
     227               0 : }
     228                 : 
     229               0 : static void NCStatsReturn(struct NCValidatorState *vstate) {
     230               0 :   vstate->stats.returns += 1;
     231               0 :   NCStatsUnsafeIndirect(vstate);
     232               0 :   NCStatsSawFailure(vstate);
     233               0 : }
     234                 : 
     235               0 : static void NCStatsIllegalInst(struct NCValidatorState *vstate) {
     236               0 :   vstate->stats.illegalinst += 1;
     237               0 :   NCStatsSawFailure(vstate);
     238               0 : }
     239                 : 
     240               0 : static void NCStatsBadPrefix(struct NCValidatorState *vstate) {
     241               0 :   vstate->stats.badprefix += 1;
     242               0 :   vstate->stats.illegalinst += 1; /* a bad prefix is also an invalid inst */
     243               0 :   NCStatsSawFailure(vstate);
     244               0 : }
     245                 : 
     246               0 : static void NCStatsBadInstLength(struct NCValidatorState *vstate) {
     247               0 :   vstate->stats.badinstlength += 1;
     248               0 :   NCStatsSawFailure(vstate);
     249               0 : }
     250                 : 
     251               0 : static void NCStatsInit(struct NCValidatorState *vstate) {
     252               0 :   vstate->stats.instructions = 0;
     253               0 :   vstate->stats.segments = 0;
     254               0 :   vstate->stats.checktarget = 0;
     255               0 :   vstate->stats.targetindirect = 0;
     256               0 :   vstate->stats.badtarget = 0;
     257               0 :   vstate->stats.unsafeindirect = 0;
     258               0 :   vstate->stats.returns = 0;
     259               0 :   vstate->stats.illegalinst = 0;
     260               0 :   vstate->stats.badalignment = 0;
     261               0 :   vstate->stats.internalerrors = 0;
     262               0 :   vstate->stats.badinstlength = 0;
     263               0 :   vstate->stats.badprefix = 0;
     264               0 :   vstate->stats.didstubout = 0;
     265               0 :   vstate->stats.sawfailure = 0;
     266               0 :   InitOpcodeHisto(vstate);
     267               0 : }
     268                 : 
     269               0 : void NCStatsPrint(struct NCValidatorState *vstate) {
     270               0 :   NaClErrorReporter* reporter;
     271               0 :   if (!VERBOSE || (vstate == NULL)) return;
     272               0 :   reporter = vstate->dstate.error_reporter;
     273               0 :   PrintOpcodeHisto(vstate);
     274               0 :   (*reporter->printf)(reporter, "Analysis Summary:\n");
     275               0 :   (*reporter->printf)(reporter, "%d Checked instructions\n",
     276                 :                       vstate->stats.instructions);
     277               0 :   (*reporter->printf)(reporter, "%d checked jump targets\n",
     278                 :                       vstate->stats.checktarget);
     279               0 :   (*reporter->printf)(
     280                 :       reporter, "%d calls/jumps need dynamic checking (%0.2f%%)\n",
     281                 :       vstate->stats.targetindirect,
     282                 :       vstate->stats.instructions ?
     283                 :       100.0 * vstate->stats.targetindirect/vstate->stats.instructions : 0);
     284               0 :   if (vstate->stats.didstubout) {
     285               0 :     (*reporter->printf)(reporter, "Some instructions were replaced with HLTs");
     286               0 :   }
     287               0 :   (*reporter->printf)(reporter, "\nProblems:\n");
     288               0 :   (*reporter->printf)(reporter, "%d illegal instructions\n",
     289                 :                    vstate->stats.illegalinst);
     290               0 :   (*reporter->printf)(reporter,
     291                 :                       "%d bad jump targets\n", vstate->stats.badtarget);
     292               0 :   (*reporter->printf)(
     293                 :       reporter, "%d illegal unprotected indirect jumps (including ret)\n",
     294                 :       vstate->stats.unsafeindirect);
     295               0 :   (*reporter->printf)(reporter, "%d instruction alignment defects\n",
     296                 :                       vstate->stats.badalignment);
     297               0 :   (*reporter->printf)(reporter, "%d segmentation errors\n",
     298                 :                       vstate->stats.segfaults);
     299               0 :   (*reporter->printf)(reporter, "%d bad prefix\n",
     300                 :                       vstate->stats.badprefix);
     301               0 :   (*reporter->printf)(reporter, "%d bad instruction length\n",
     302                 :                       vstate->stats.badinstlength);
     303               0 :   (*reporter->printf)(reporter, "%d internal errors\n",
     304                 :                       vstate->stats.internalerrors);
     305               0 : }
     306                 : 
     307                 : /***********************************************************************/
     308                 : /* jump target table                                                   */
     309                 : const uint8_t nc_iadrmasks[8] = {0x01, 0x02, 0x04, 0x08,
     310                 :                                  0x10, 0x20, 0x40, 0x80};
     311                 : 
     312                 : /* forward declarations, needed for registration */
     313                 : static Bool ValidateInst(const NCDecoderInst *dinst);
     314                 : static Bool ValidateInstReplacement(NCDecoderStatePair* tthis,
     315                 :                                     NCDecoderInst *dinst_old,
     316                 :                                     NCDecoderInst *dinst_new);
     317                 : static void NCJumpSummarize(struct NCValidatorState* vstate);
     318                 : 
     319               0 : struct NCValidatorState *NCValidateInit(const NaClPcAddress vbase,
     320               0 :                                         const NaClPcAddress codesize,
     321               0 :                                         const int readonly_text,
     322               0 :                                         const NaClCPUFeaturesX86 *features) {
     323               0 :   struct NCValidatorState *vstate = NULL;
     324               0 :   const int bundle_size = 32;
     325                 : 
     326               0 :   dprint(("NCValidateInit(%"NACL_PRIxNaClPcAddressAll
     327                 :           ", %"NACL_PRIxNaClMemorySizeAll", %08x)\n", vbase, codesize,
     328                 :           bundle_size));
     329               0 :   do {
     330               0 :     if (features == NULL)
     331               0 :       break;
     332               0 :     if ((vbase & (bundle_size - 1)) != 0)
     333               0 :       break;
     334               0 :     dprint(("ncv_init(%"NACL_PRIxNaClPcAddress", %"NACL_PRIxNaClMemorySize
     335                 :             ")\n", vbase, codesize));
     336               0 :     vstate = (struct NCValidatorState *)calloc(1, sizeof(*vstate));
     337               0 :     if (vstate == NULL)
     338               0 :       break;
     339                 :     /* Record default error reporter here, since we don't construct
     340                 :      * the decoder state until the call to NCValidateSegment. This allows
     341                 :      * us to update the error reporter in the decoder state properly.
     342                 :      */
     343               0 :     vstate->dstate.error_reporter = &kNCNullErrorReporter;
     344               0 :     vstate->num_diagnostics = kMaxDiagnostics;
     345               0 :     vstate->iadrbase = vbase;
     346               0 :     vstate->codesize = codesize;
     347               0 :     vstate->bundle_size = bundle_size;
     348               0 :     vstate->bundle_mask = bundle_size - 1;
     349               0 :     vstate->vttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1);
     350               0 :     vstate->kttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1);
     351               0 :     vstate->pattern_nonfirst_insts_table = NULL;
     352               0 :     vstate->summarize_fn = NCJumpSummarize;
     353               0 :     vstate->do_stub_out = 0;
     354               0 :     vstate->readonly_text = readonly_text;
     355               0 :     if (vstate->vttable == NULL || vstate->kttable == NULL)
     356               0 :       break;
     357               0 :     dprint(("  allocated tables\n"));
     358               0 :     NCStatsInit(vstate);
     359               0 :     NaClCopyCPUFeaturesX86(&vstate->cpufeatures, features);
     360               0 :     return vstate;
     361                 :   } while (0);
     362                 :   /* Failure. Clean up memory before returning. */
     363               0 :   if (NULL != vstate) {
     364               0 :     if (NULL != vstate->kttable)
     365               0 :       free(vstate->kttable);
     366               0 :     if (NULL != vstate->vttable)
     367               0 :       free(vstate->vttable);
     368               0 :     free(vstate);
     369               0 :   }
     370               0 :   return NULL;
     371               0 : }
     372                 : 
     373               0 : void NCValidateSetStubOutMode(struct NCValidatorState *vstate,
     374               0 :                               int do_stub_out) {
     375               0 :   vstate->do_stub_out = do_stub_out;
     376                 :   /* We also turn off error diagnostics, under the assumption
     377                 :    * you don't want them. (Note: if the user wants them,
     378                 :    * you can run ncval to get them)/
     379                 :    */
     380               0 :   if (do_stub_out) {
     381               0 :     NCValidateSetNumDiagnostics(vstate, 0);
     382               0 :   }
     383               0 : }
     384                 : 
     385               0 : void NCValidateSetNumDiagnostics(struct NCValidatorState* vstate,
     386               0 :                                  int num_diagnostics) {
     387               0 :   vstate->num_diagnostics = num_diagnostics;
     388               0 : }
     389                 : 
     390               0 : void NCValidateSetErrorReporter(struct NCValidatorState* state,
     391               0 :                                 NaClErrorReporter* error_reporter) {
     392               0 :   NCDecoderStateSetErrorReporter(&state->dstate, error_reporter);
     393               0 : }
     394                 : 
     395               0 : static INLINE void RememberInstructionBoundary(const NCDecoderInst *dinst,
     396               0 :                                               struct NCValidatorState *vstate) {
     397                 :   /* The decoder should never pass us an out-of-bounds instruction. */
     398               0 :   CHECK(dinst->inst_addr < vstate->codesize);
     399               0 :   if (NCGetAdrTable(dinst->inst_addr, vstate->vttable)) {
     400               0 :     vprint(vstate, (reporter,
     401                 :                     "RememberIP: Saw inst at %"NACL_PRIxNaClPcAddressAll
     402                 :                     " twice\n", NCPrintableInstructionAddress(dinst)));
     403               0 :     NCStatsInternalError(vstate);
     404               0 :     return;
     405                 :   }
     406               0 :   NCStatsInst(vstate);
     407               0 :   NCSetAdrTable(dinst->inst_addr, vstate->vttable);
     408               0 : }
     409                 : 
     410               0 : static void RememberJumpTarget(const NCDecoderInst *dinst, int32_t jump_offset,
     411               0 :                                struct NCValidatorState *vstate) {
     412               0 :   NaClPcAddress target = (dinst->inst_addr + dinst->inst.bytes.length
     413                 :                           + jump_offset);
     414                 : 
     415                 :   /* For testing only, this mode disables inter-instruction checks. */
     416               0 :   if (NACL_FLAG_unsafe_single_inst32_mode) return;
     417                 : 
     418               0 :   if (target < vstate->codesize) {
     419               0 :     NCSetAdrTable(target, vstate->kttable);
     420               0 :   } else if ((target & vstate->bundle_mask) == 0) {
     421                 :     /* Allow bundle-aligned jumps. */
     422               0 :   } else if (dinst->unchanged) {
     423                 :     /* If we are replacing this instruction during dynamic code modification
     424                 :      * and it has not changed, the jump target must be valid because the
     425                 :      * instruction has been previously validated.  However, we may be only
     426                 :      * replacing a subsection of the code segment and therefore may not have
     427                 :      * information about instruction boundaries outside of the code being
     428                 :      * replaced. Therefore, we allow unaligned direct jumps outside of the code
     429                 :      * being validated if and only if the instruction is unchanged.
     430                 :      * If dynamic code replacement is not being performed, inst->unchanged
     431                 :      * should always be false.
     432                 :      */
     433               0 :   } else {
     434               0 :     ValidatePrintInstructionError(dinst, "JUMP TARGET out of range", vstate);
     435               0 :     NCStatsBadTarget(vstate);
     436                 :   }
     437               0 : }
     438                 : 
     439               0 : static void ForgetInstructionBoundary(const NCDecoderInst *dinst,
     440               0 :                                      struct NCValidatorState *vstate) {
     441                 :   /* The decoder should never pass us an out-of-bounds instruction. */
     442               0 :   CHECK(dinst->inst_addr < vstate->codesize);
     443               0 :   NCClearAdrTable(dinst->inst_addr, vstate->vttable);
     444               0 :   if (NULL != vstate->pattern_nonfirst_insts_table) {
     445               0 :     NCSetAdrTable(dinst->inst_addr, vstate->pattern_nonfirst_insts_table);
     446               0 :   }
     447               0 : }
     448                 : 
     449               0 : int NCValidateFinish(struct NCValidatorState *vstate) {
     450               0 :   if (vstate == NULL) {
     451               0 :     dprint(("validator not initialized. Did you call ncvalidate_init()?\n"));
     452                 :     /* non-zero indicates failure */
     453               0 :     return 1;
     454                 :   }
     455                 : 
     456                 :   /* If we are stubbing out code, the following checks don't provide any
     457                 :    * usefull information, so quit early.
     458                 :    */
     459               0 :   if (vstate->do_stub_out) return vstate->stats.sawfailure;
     460                 : 
     461                 :   /* Double check that the base address matches the alignment constraint. */
     462               0 :   if (vstate->iadrbase & vstate->bundle_mask) {
     463                 :     /* This should never happen because the alignment of iadrbase is */
     464                 :     /* checked in NCValidateInit(). */
     465               0 :     ValidatePrintOffsetError(0, "Bad base address alignment", vstate);
     466               0 :     NCStatsBadAlignment(vstate);
     467               0 :   }
     468                 : 
     469                 :   /* Apply summary analysis to collected data during pass over
     470                 :    * instructions.
     471                 :    */
     472               0 :   (*(vstate->summarize_fn))(vstate);
     473                 : 
     474                 :   /* Now that all the work is done, generate return code. */
     475                 :   /* Return zero if there are no problems.                */
     476               0 :   return (vstate->stats.sawfailure);
     477               0 : }
     478                 : 
     479               0 : void NCValidateFreeState(struct NCValidatorState **vstate) {
     480               0 :   CHECK(*vstate != NULL);
     481               0 :   free((*vstate)->vttable);
     482               0 :   free((*vstate)->kttable);
     483               0 :   free((*vstate)->pattern_nonfirst_insts_table);
     484               0 :   free(*vstate);
     485               0 :   *vstate = NULL;
     486               0 : }
     487                 : 
     488                 : /* ValidateSFenceClFlush is called for the sfence/clflush opcode 0f ae /7 */
     489                 : /* It returns 0 if the current instruction is implemented, and 1 if not.  */
     490               0 : static int ValidateSFenceClFlush(const NCDecoderInst *dinst) {
     491               0 :   NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     492               0 :   uint8_t mrm = NCInstBytesByteInline(&dinst->inst_bytes, 2);
     493                 : 
     494               0 :   if (modrm_modInline(mrm) == 3) {
     495                 :     /* this is an sfence */
     496               0 :     if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_FXSR))
     497               0 :       return 0;
     498               0 :     return 1;
     499                 :   } else {
     500                 :     /* this is an clflush */
     501               0 :     if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_CLFLUSH))
     502               0 :       return 0;
     503               0 :     return 1;
     504                 :   }
     505               0 : }
     506                 : 
     507               0 : static void ValidateCallAlignment(const NCDecoderInst *dinst) {
     508               0 :   NaClPcAddress fallthru = dinst->inst_addr + dinst->inst.bytes.length;
     509               0 :   struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     510               0 :   if (fallthru & vstate->bundle_mask) {
     511                 : #if defined(ERROR_ON_CALL_BUNDLE_ALIGNMENT)
     512                 :     /* NOTE: Previously the validator recorded an error for call instructions
     513                 :      * that were not aligned against the end of a bundle, as these, while
     514                 :      * safe, are not correct with the current code generation idioms.
     515                 :      * This #if defined(ERROR_ON_CALL_BUNDLE_ALIGNMENT) was added to allow
     516                 :      * experimentation with different call/return idioms.
     517                 :      */
     518                 :     ValidatePrintInstructionError(dinst, "Bad call alignment", vstate);
     519                 :     /* This makes bad call alignment a fatal error. */
     520                 :     NCStatsBadAlignment(vstate);
     521                 : #endif
     522               0 :   }
     523               0 : }
     524                 : 
     525               0 : static void ValidateJmp8(const NCDecoderInst *dinst) {
     526               0 :   int8_t offset = NCInstBytesByteInline(&dinst->inst_bytes,
     527                 :                                         dinst->inst.prefixbytes+1);
     528               0 :   struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     529               0 :   NCStatsCheckTarget(vstate);
     530               0 :   RememberJumpTarget(dinst, offset, vstate);
     531               0 : }
     532                 : 
     533               0 : static void ValidateJmpz(const NCDecoderInst *dinst) {
     534               0 :   NCInstBytesPtr opcode;
     535               0 :   uint8_t opcode0;
     536               0 :   int32_t offset;
     537               0 :   NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     538               0 :   NCInstBytesPtrInitInc(&opcode, &dinst->inst_bytes,
     539                 :                         dinst->inst.prefixbytes);
     540               0 :   opcode0 = NCInstBytesByteInline(&opcode, 0);
     541               0 :   NCStatsCheckTarget(vstate);
     542               0 :   if (opcode0 == 0x0f) {
     543                 :     /* Multbyte opcode. Intruction is of form:
     544                 :      *    0F80 .. 0F8F: jCC $Jz
     545                 :      */
     546               0 :     NCInstBytesPtr opcode_2;
     547               0 :     NCInstBytesPtrInitInc(&opcode_2, &opcode, 2);
     548               0 :     offset = NCInstBytesInt32(&opcode_2, dinst->inst.immbytes);
     549               0 :   } else {
     550                 :     /* Single byte opcode. Must be one of:
     551                 :      *    E8: call $Jz
     552                 :      *    E9: jmp $Jx
     553                 :      */
     554               0 :     NCInstBytesPtr opcode_1;
     555               0 :     NCInstBytesPtrInitInc(&opcode_1, &opcode, 1);
     556               0 :     offset = NCInstBytesInt32(&opcode_1, dinst->inst.immbytes);
     557                 :     /* as a courtesy, check call alignment correctness */
     558               0 :     if (opcode0 == 0xe8) ValidateCallAlignment(dinst);
     559                 :   }
     560               0 :   RememberJumpTarget(dinst, offset, vstate);
     561               0 : }
     562                 : 
     563                 : /*
     564                 :  * The NaCl five-byte safe indirect calling sequence looks like this:
     565                 :  *   83 e0 e0                 and  $0xe0,%eax
     566                 :  *   ff d0                    call *%eax
     567                 :  * The call may be replaced with a ff e0 jmp. Any register may
     568                 :  * be used, not just eax. The validator requires exactly this
     569                 :  * sequence.
     570                 :  * Note: The code above assumes 32-bit alignment. Change e0 as appropriate
     571                 :  * if a different alignment is used.
     572                 :  */
     573               0 : static void ValidateIndirect5(const NCDecoderInst *dinst) {
     574               0 :   NCInstBytesPtr jmpopcode;
     575               0 :   NCInstBytesPtr andopcode;
     576               0 :   uint8_t               mrm;
     577               0 :   uint8_t               targetreg;
     578               0 :   const uint8_t         kReg_ESP = 4;
     579               0 :   NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     580                 : 
     581               0 :   struct NCDecoderInst *andinst = PreviousInst(dinst, 1);
     582               0 :   if ((andinst == NULL) || (andinst->inst.bytes.length != 3)) {
     583               0 :     NCBadInstructionError(dinst, "Unsafe indirect jump");
     584               0 :     NCStatsUnsafeIndirect(vstate);
     585               0 :     return;
     586                 :   }
     587                 :   /* note: no prefixbytes allowed */
     588               0 :   NCInstBytesPtrInitInc(&jmpopcode, &dinst->inst_bytes, 0);
     589                 :   /* note: no prefixbytes allowed */
     590               0 :   NCInstBytesPtrInitInc(&andopcode, &andinst->inst_bytes, 0);
     591               0 :   mrm = NCInstBytesByteInline(&jmpopcode, 1);
     592                 :   /* Note that the modrm_rm field holds the
     593                 :    * target addr the modrm_reg is the opcode.
     594                 :    */
     595               0 :   targetreg = modrm_rmInline(mrm);
     596               0 :   NCStatsCheckTarget(vstate);
     597               0 :   NCStatsTargetIndirect(vstate);
     598               0 :   do {
     599                 :     /* no prefix bytes allowed */
     600               0 :     if (dinst->inst.prefixbytes != 0) break;
     601               0 :     if (dinst->inst.prefixbytes != 0) break;
     602                 :     /* Check all the opcodes. */
     603                 :     /* In GROUP5, 2 => call, 4 => jmp */
     604               0 :     if (NCInstBytesByteInline(&jmpopcode, 0) != 0xff) break;
     605               0 :     if ((modrm_regInline(mrm) != 2) && (modrm_regInline(mrm) != 4)) break;
     606                 :     /* Issue 32: disallow unsafe call/jump indirection */
     607                 :     /* example:    ff 12     call (*edx)               */
     608                 :     /* Reported by defend.the.world on 11 Dec 2008     */
     609               0 :     if (modrm_modInline(mrm) != 3) break;
     610               0 :     if (targetreg == kReg_ESP) break;
     611               0 :     if (NCInstBytesByteInline(&andopcode, 0) != 0x83) break;
     612                 :     /* check modrm bytes of or and and instructions */
     613               0 :     if (NCInstBytesByteInline(&andopcode, 1) != (0xe0 | targetreg)) break;
     614                 :     /* check mask */
     615               0 :     if (NCInstBytesByteInline(&andopcode, 2) !=
     616               0 :         (0x0ff & ~vstate->bundle_mask)) break;
     617                 :     /* All checks look good. Make the sequence 'atomic.' */
     618               0 :     ForgetInstructionBoundary(dinst, vstate);
     619                 :     /* as a courtesy, check call alignment correctness */
     620               0 :     if (modrm_regInline(mrm) == 2) ValidateCallAlignment(dinst);
     621               0 :     return;
     622                 :   } while (0);
     623               0 :   NCBadInstructionError(dinst, "Unsafe indirect jump");
     624               0 :   NCStatsUnsafeIndirect(vstate);
     625               0 : }
     626                 : 
     627                 : /* Checks if the set of prefixes are allowed for the instruction.
     628                 :  * By default, we only allow prefixes if they have been allowed
     629                 :  * by the bad prefix mask generated inside ncdecode_table.c.
     630                 :  * These masks are defined by the NaClInstType of the instruction.
     631                 :  * See ncdecode_table.c for more details on how these masks are set.
     632                 :  *
     633                 :  * Currently:
     634                 :  * Only 386, 386L, 386R, 386RE instruction allow  Data 16
     635                 :  * (unless used as part of instruction selection in a multibyte instruction).
     636                 :  * Only 386, JMP8, and JMPZ allow segment registers prefixes.
     637                 :  * Only 386L and CMPXCHG8B allow the LOCK prefix.
     638                 :  * Only 386R and 386RE instructions allow the REP prefix.
     639                 :  * Only 386RE instructions allow the REPNE prefix.
     640                 :  *
     641                 :  * Note: The prefixmask does not include the prefix value (if any) used to
     642                 :  * select multiple byte instructions. Such prefixes have been moved to
     643                 :  * opcode_prefixmask, so that the selection (based on that prefix) has
     644                 :  * been recorded.
     645                 :  *
     646                 :  * In general, we do not allow multiple prefixes. Exceptions are as
     647                 :  * follows:
     648                 :  *   1 - Data 16 is allowed on lock instructions, so that 2 byte values
     649                 :  *       can be locked.
     650                 :  *   2 - Multibyte instructions that are selected
     651                 :  *       using prefix values Data 16, REP and REPNE, can only have
     652                 :  *       one of these prefixes (Combinations of these three prefixes
     653                 :  *       are not allowed for such multibyte instructions).
     654                 :  *   3 - Locks are only allowed on instructions with type 386L. The
     655                 :  *       exception is inst cmpxch8b, which also can have a lock.
     656                 :  *   4 - The only two prefix byte combination allowed is Data 16 and Lock.
     657                 :  *   5 - Long nops that are hard coded can contain more than one prefix.
     658                 :  *       See ncdecode.c for details (they don't use ValidatePrefixes).
     659                 :  */
     660               0 : static Bool ValidatePrefixes(const NCDecoderInst *dinst) {
     661               0 :   if (dinst->inst.prefixbytes == 0) return TRUE;
     662                 : 
     663               0 :   if ((dinst->inst.prefixmask &
     664                 :        BadPrefixMask[dinst->opinfo->insttype]) != 0) {
     665               0 :     return FALSE;
     666                 :   }
     667                 : 
     668                 :   /* If a multibyte instruction is using a selection prefix, be
     669                 :    * sure that there is no conflict with other selection prefixes.
     670                 :    */
     671               0 :   if ((dinst->inst.opcode_prefixmask != 0) &&
     672                 :       ((dinst->inst.prefixmask &
     673                 :         (kPrefixDATA16 | kPrefixREPNE | kPrefixREP)) != 0)) {
     674               0 :     return FALSE;
     675                 :   }
     676                 : 
     677                 :   /* Only allow a lock if it is a 386L instruction, or the special
     678                 :    * cmpxchg8b instruction.
     679                 :    */
     680               0 :   if (dinst->inst.prefixmask & kPrefixLOCK) {
     681               0 :     if ((dinst->opinfo->insttype != NACLi_386L) &&
     682                 :         (dinst->opinfo->insttype != NACLi_CMPXCHG8B)) {
     683               0 :       return FALSE;
     684                 :     }
     685               0 :   }
     686                 : 
     687                 :   /* Only allow more than one prefix if two prefixes, and they are
     688                 :    * data 16 and lock.
     689                 :    */
     690               0 :   if ((dinst->inst.prefixbytes > 1) &&
     691                 :       !((dinst->inst.prefixbytes == 2) &&
     692                 :         (dinst->inst.prefixmask == (kPrefixLOCK | kPrefixDATA16)) &&
     693                 :         /* Be sure data 16 (66) appears before lock (f0) prefix. */
     694                 :         (dinst->inst.lock_prefix_index == 1))) {
     695               0 :     return FALSE;
     696                 :   }
     697                 : 
     698               0 :   return TRUE;
     699               0 : }
     700                 : 
     701                 : static const size_t kMaxValidInstLength = 11;
     702                 : 
     703                 : /* The modrm mod field is a two-bit value. Values 00, 01, and, 10
     704                 :  * define memory references. Value 11 defines register accesses instead
     705                 :  * of memory.
     706                 :  */
     707                 : static const int kModRmModFieldDefinesRegisterRef = 0x3;
     708                 : 
     709               0 : static Bool ValidateInst(const NCDecoderInst *dinst) {
     710               0 :   NaClCPUFeaturesX86 *cpufeatures;
     711               0 :   int squashme = 0;
     712               0 :   NCValidatorState* vstate;
     713               0 :   if (dinst == NULL) return TRUE;
     714               0 :   vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     715                 : 
     716               0 :   OpcodeHisto(NCInstBytesByteInline(&dinst->inst_bytes,
     717                 :                                     dinst->inst.prefixbytes),
     718                 :               vstate);
     719                 :   /* For testing only, this mode disables inter-instruction checks. */
     720               0 :   if (!NACL_FLAG_unsafe_single_inst32_mode) {
     721               0 :     RememberInstructionBoundary(dinst, vstate);
     722               0 :   }
     723                 : 
     724               0 :   cpufeatures = &vstate->cpufeatures;
     725                 : 
     726               0 :   if (!ValidatePrefixes(dinst)) {
     727               0 :     NCBadInstructionError(dinst, "Bad prefix usage");
     728               0 :     NCStatsBadPrefix(vstate);
     729               0 :   }
     730                 : 
     731               0 :   if ((dinst->opinfo->insttype != NACLi_NOP) &&
     732                 :       ((size_t) (dinst->inst.bytes.length - dinst->inst.prefixbytes)
     733                 :        > kMaxValidInstLength)) {
     734               0 :     NCBadInstructionError(dinst, "Instruction too long");
     735               0 :     NCStatsBadInstLength(vstate);
     736               0 :   }
     737                 : 
     738               0 :   switch (dinst->opinfo->insttype) {
     739                 :     case NACLi_NOP:
     740                 :     case NACLi_386:
     741                 :     case NACLi_386L:
     742                 :     case NACLi_386R:
     743                 :     case NACLi_386RE:
     744               0 :       break;
     745                 :     case NACLi_LAHF:
     746                 :       if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_TARGET_SUBARCH == 64)
     747               0 :         squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LAHF);
     748               0 :       break;
     749                 :     case NACLi_JMP8:
     750               0 :       ValidateJmp8(dinst);
     751               0 :       break;
     752                 :     case NACLi_JMPZ:
     753               0 :       ValidateJmpz(dinst);
     754               0 :       break;
     755                 :     case NACLi_INDIRECT:
     756               0 :       ValidateIndirect5(dinst);
     757               0 :       break;
     758                 :     case NACLi_X87:
     759                 :     case NACLi_X87_FSINCOS:
     760               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87);
     761               0 :       break;
     762                 :     case NACLi_SFENCE_CLFLUSH:
     763               0 :       squashme = ValidateSFenceClFlush(dinst);
     764               0 :       break;
     765                 :     case NACLi_CMPXCHG8B:
     766                 :       /* Only allow if the modrm mod field accesses memory.
     767                 :        * This stops us from accepting f00f on multiple bytes.
     768                 :        * http://en.wikipedia.org/wiki/Pentium_F00F_bug
     769                 :        */
     770               0 :       if (modrm_modInline(dinst->inst.mrm)
     771                 :           == kModRmModFieldDefinesRegisterRef) {
     772               0 :         NCBadInstructionError(dinst, "Illegal instruction");
     773               0 :         NCStatsIllegalInst(vstate);
     774               0 :       }
     775               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CX8);
     776               0 :       break;
     777                 :     case NACLi_CMOV:
     778               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV);
     779               0 :       break;
     780                 :     case NACLi_FCMOV:
     781               0 :       squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV) &&
     782               0 :                    NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87));
     783               0 :       break;
     784                 :     case NACLi_RDTSC:
     785               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_TSC);
     786               0 :       break;
     787                 :     case NACLi_MMX:
     788               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX);
     789               0 :       break;
     790                 :     case NACLi_MMXSSE2:
     791                 :       /* Note: We accept these instructions if either MMX or SSE2 bits */
     792                 :       /* are set, in case MMX instructions go away someday...          */
     793               0 :       squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX) ||
     794               0 :                    NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2));
     795               0 :       break;
     796                 :     case NACLi_SSE:
     797               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE);
     798               0 :       break;
     799                 :     case NACLi_SSE2:
     800               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2);
     801               0 :       break;
     802                 :     case NACLi_SSE3:
     803               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE3);
     804               0 :       break;
     805                 :     case NACLi_SSE4A:
     806               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE4A);
     807               0 :       break;
     808                 :     case NACLi_SSE41:
     809               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE41);
     810               0 :       break;
     811                 :     case NACLi_SSE42:
     812               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE42);
     813               0 :       break;
     814                 :     case NACLi_MOVBE:
     815               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MOVBE);
     816               0 :       break;
     817                 :     case NACLi_POPCNT:
     818               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_POPCNT);
     819               0 :       break;
     820                 :     case NACLi_LZCNT:
     821               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LZCNT);
     822               0 :       break;
     823                 :     case NACLi_SSSE3:
     824               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSSE3);
     825               0 :       break;
     826                 :     case NACLi_3DNOW:
     827               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_3DNOW);
     828               0 :       break;
     829                 :     case NACLi_E3DNOW:
     830               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_E3DNOW);
     831               0 :       break;
     832                 :     case NACLi_SSE2x:
     833                 :       /* This case requires CPUID checking code */
     834                 :       /* Note: DATA16 prefix required. The generated table
     835                 :        * for group 14 (which the only 2 SSE2x instructions are in),
     836                 :        * allows instructions with and without a 66 prefix. However,
     837                 :        * the SSE2x instructions psrldq and pslldq are only allowed
     838                 :        * with the 66 prefix. Hence, this code has been added to
     839                 :        * do this check.
     840                 :        */
     841               0 :       if (!(dinst->inst.opcode_prefixmask & kPrefixDATA16)) {
     842               0 :         NCBadInstructionError(dinst, "Bad prefix usage");
     843               0 :         NCStatsBadPrefix(vstate);
     844               0 :       }
     845               0 :       squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2);
     846               0 :       break;
     847                 : 
     848                 :     case NACLi_RETURN:
     849               0 :       NCBadInstructionError(dinst, "ret instruction (not allowed)");
     850               0 :       NCStatsReturn(vstate);
     851                 :       /* ... and fall through to illegal instruction code */
     852                 :     case NACLi_EMMX:
     853                 :       /* EMMX needs to be supported someday but isn't ready yet. */
     854                 :     case NACLi_INVALID:
     855                 :     case NACLi_ILLEGAL:
     856                 :     case NACLi_SYSTEM:
     857                 :     case NACLi_RDMSR:
     858                 :     case NACLi_RDTSCP:
     859                 :     case NACLi_SYSCALL:
     860                 :     case NACLi_SYSENTER:
     861                 :     case NACLi_LONGMODE:
     862                 :     case NACLi_SVM:
     863                 :     case NACLi_OPINMRM:
     864                 :     case NACLi_3BYTE:
     865                 :     case NACLi_CMPXCHG16B: {
     866               0 :         NCBadInstructionError(dinst, "Illegal instruction");
     867               0 :         NCStatsIllegalInst(vstate);
     868               0 :         break;
     869                 :       }
     870                 :     case NACLi_UNDEFINED: {
     871               0 :         NCBadInstructionError(dinst, "Undefined instruction");
     872               0 :         NCStatsIllegalInst(vstate);
     873               0 :         NCStatsInternalError(vstate);
     874               0 :         break;
     875                 :       }
     876                 :     default:
     877               0 :       NCBadInstructionError(dinst, "Undefined instruction type");
     878               0 :       NCStatsInternalError(vstate);
     879               0 :       break;
     880                 :   }
     881               0 :   if (squashme) {
     882               0 :     if (vstate->readonly_text) {
     883               0 :       NCBadInstructionError(dinst,
     884                 :                             "Illegal instruction for fixed-feature CPU mode");
     885               0 :       NCStatsIllegalInst(vstate);
     886               0 :     } else {
     887               0 :       NCStubOutMem(vstate, dinst->dstate->memory.mpc,
     888                 :                    dinst->dstate->memory.read_length);
     889                 :     }
     890               0 :   }
     891               0 :   return TRUE;
     892               0 : }
     893                 : 
     894               0 : Bool UnsafePartialValidateInst(const NCDecoderInst *dinst) {
     895               0 :   NCValidatorState *vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
     896               0 :   Bool result = FALSE;
     897                 : 
     898               0 :   NACL_FLAG_unsafe_single_inst32_mode = TRUE;
     899               0 :   NCStatsInit(vstate);
     900               0 :   if (ValidateInst(dinst)) {
     901               0 :     result = vstate->stats.sawfailure == 0;
     902               0 :   };
     903               0 :   NACL_FLAG_unsafe_single_inst32_mode = FALSE;
     904               0 :   return result;
     905                 : }
     906                 : 
     907                 : /*
     908                 :  * Validate that two nacljmps are byte-for-byte identical.  Note that
     909                 :  * one of the individual jumps must be validated in isolation with
     910                 :  * ValidateIndirect5() before this is called.
     911                 :  */
     912               0 : static void ValidateIndirect5Replacement(NCDecoderInst *dinst_old,
     913               0 :                                          NCDecoderInst *dinst_new) {
     914               0 :   do {
     915                 :     /* check that the and-guard is 3 bytes and bit-for-bit identical */
     916               0 :     NCDecoderInst *andinst_old = PreviousInst(dinst_old, 1);
     917               0 :     NCDecoderInst *andinst_new = PreviousInst(dinst_new, 1);
     918               0 :     if ((andinst_old == NULL) || (andinst_old->inst.bytes.length != 3)) break;
     919               0 :     if ((andinst_new == NULL) || (andinst_new->inst.bytes.length != 3)) break;
     920               0 :     if (memcmp(andinst_old->inst.bytes.byte,
     921               0 :                andinst_new->inst.bytes.byte, 3) != 0) break;
     922                 : 
     923                 :     /* check that the indirect-jmp is 2 bytes and bit-for-bit identical */
     924               0 :     if (dinst_old->inst.bytes.length != 2) break;
     925               0 :     if (dinst_new->inst.bytes.length != 2) break;
     926               0 :     if (memcmp(dinst_old->inst.bytes.byte,
     927               0 :                dinst_new->inst.bytes.byte, 2) != 0) break;
     928                 : 
     929               0 :     return;
     930                 :   } while (0);
     931               0 :   NCBadInstructionError(dinst_new,
     932                 :                         "Replacement indirect jump must match original");
     933               0 :   NCStatsUnsafeIndirect(NCVALIDATOR_STATE_DOWNCAST(dinst_new->dstate));
     934               0 : }
     935                 : 
     936                 : /*
     937                 :  * Check that mstate_new is a valid replacement instruction for mstate_old.
     938                 :  * Note that mstate_old was validated when it was inserted originally.
     939                 :  */
     940               0 : static Bool ValidateInstReplacement(NCDecoderStatePair* tthis,
     941               0 :                                     NCDecoderInst *dinst_old,
     942               0 :                                     NCDecoderInst *dinst_new) {
     943               0 :   dinst_new->unchanged = memcmp(dinst_old->inst.bytes.byte,
     944                 :                                 dinst_new->inst.bytes.byte,
     945                 :                                 dinst_new->inst.bytes.length) == 0;
     946               0 :   ValidateInst(dinst_new);
     947                 : 
     948               0 :   if (dinst_old->opinfo->insttype == NACLi_INDIRECT
     949                 :     || dinst_new->opinfo->insttype == NACLi_INDIRECT) {
     950                 :     /* Verify that nacljmps never change */
     951               0 :     ValidateIndirect5Replacement(dinst_old, dinst_new);
     952               0 :   }
     953               0 :   return TRUE;
     954                 : }
     955                 : 
     956                 : /* Create the decoder state for the validator state, using the
     957                 :  * given parameters.
     958                 :  */
     959               0 : static void NCValidateDStateInit(NCValidatorState *vstate,
     960               0 :                                  uint8_t *mbase, NaClPcAddress vbase,
     961               0 :                                  NaClMemorySize sz) {
     962               0 :   NCDecoderState* dstate = &vstate->dstate;
     963                 :   /* Note: Based on the current API, we must grab the error reporter
     964                 :    * and reinstall it, after the call to NCValidateDStateInit, so that
     965                 :    * we don't replace it with the default error reporter.
     966                 :    */
     967               0 :   NaClErrorReporter* reporter = dstate->error_reporter;
     968               0 :   NCDecoderStateConstruct(dstate, mbase, vbase, sz,
     969                 :                           vstate->inst_buffer, kNCValidatorInstBufferSize);
     970               0 :   dstate->action_fn = ValidateInst;
     971               0 :   dstate->new_segment_fn = (NCDecoderStateMethod) NCStatsNewSegment;
     972               0 :   dstate->segmentation_error_fn = (NCDecoderStateMethod) NCStatsSegFault;
     973               0 :   dstate->internal_error_fn = (NCDecoderStateMethod) NCStatsInternalError;
     974               0 :   NCDecoderStateSetErrorReporter(dstate, reporter);
     975               0 : }
     976                 : 
     977               0 : void NCValidateSegment(uint8_t *mbase, NaClPcAddress vbase, NaClMemorySize sz,
     978               0 :                        struct NCValidatorState *vstate) {
     979                 :   /* Sanity checks */
     980                 :   /* TODO(ncbray): remove redundant vbase/size args. */
     981               0 :   if ((vbase & vstate->bundle_mask) != 0) {
     982               0 :     ValidatePrintOffsetError(0, "Bad vbase alignment", vstate);
     983               0 :     NCStatsSegFault(vstate);
     984               0 :     return;
     985                 :   }
     986               0 :   if (vbase != vstate->iadrbase) {
     987               0 :     ValidatePrintOffsetError(0, "Mismatched vbase addresses", vstate);
     988               0 :     NCStatsSegFault(vstate);
     989               0 :     return;
     990                 :   }
     991               0 :   if (sz != vstate->codesize) {
     992               0 :     ValidatePrintOffsetError(0, "Mismatched code size", vstate);
     993               0 :     NCStatsSegFault(vstate);
     994               0 :     return;
     995                 :   }
     996                 : 
     997               0 :   sz = NCHaltTrimSize(mbase, sz, vstate->bundle_size);
     998               0 :   vstate->codesize = sz;
     999                 : 
    1000               0 :   if (sz == 0) {
    1001               0 :     ValidatePrintOffsetError(0, "Bad text segment (zero size)", vstate);
    1002               0 :     NCStatsSegFault(vstate);
    1003               0 :     return;
    1004                 :   }
    1005               0 :   NCValidateDStateInit(vstate, mbase, vbase, sz);
    1006               0 :   NCDecoderStateDecode(&vstate->dstate);
    1007               0 :   NCDecoderStateDestruct(&vstate->dstate);
    1008               0 : }
    1009                 : 
    1010               0 : int NCValidateSegmentPair(uint8_t *mbase_old, uint8_t *mbase_new,
    1011               0 :                           NaClPcAddress vbase, size_t sz,
    1012               0 :                           const NaClCPUFeaturesX86 *features) {
    1013                 :   /* TODO(karl): Refactor to use inheritance from NCDecoderStatePair? */
    1014               0 :   NCDecoderStatePair pair;
    1015               0 :   NCValidatorState* new_vstate;
    1016               0 :   NCValidatorState* old_vstate;
    1017                 : 
    1018               0 :   int result = 0;
    1019                 : 
    1020                 :   /* Verify that we actually have a segment to walk. */
    1021               0 :   if (sz == 0) {
    1022               0 :     printf("VALIDATOR: %"NACL_PRIxNaClPcAddress
    1023                 :            ": Bad text segment (zero size)\n", vbase);
    1024               0 :     return 0;
    1025                 :   }
    1026                 : 
    1027               0 :   old_vstate = NCValidateInit(vbase, sz, FALSE, features);
    1028               0 :   if (old_vstate != NULL) {
    1029               0 :     NCValidateDStateInit(old_vstate, mbase_old, vbase, sz);
    1030               0 :     new_vstate = NCValidateInit(vbase, sz, FALSE, features);
    1031               0 :     if (new_vstate != NULL) {
    1032               0 :       NCValidateDStateInit(new_vstate, mbase_new, vbase, sz);
    1033                 : 
    1034               0 :       NCDecoderStatePairConstruct(&pair,
    1035                 :                                   &old_vstate->dstate,
    1036                 :                                   &new_vstate->dstate,
    1037                 :                                   NULL);  /* copy_func */
    1038               0 :       pair.action_fn = ValidateInstReplacement;
    1039               0 :       if (NCDecoderStatePairDecode(&pair)) {
    1040               0 :         result = 1;
    1041               0 :       } else {
    1042               0 :         ValidatePrintOffsetError(0, "Replacement not applied!\n", new_vstate);
    1043                 :       }
    1044               0 :       if (NCValidateFinish(new_vstate)) {
    1045                 :         /* Errors occurred during validation. */
    1046               0 :         result = 0;
    1047               0 :       }
    1048               0 :       NCDecoderStatePairDestruct(&pair);
    1049               0 :       NCDecoderStateDestruct(&new_vstate->dstate);
    1050               0 :       NCValidateFreeState(&new_vstate);
    1051               0 :     }
    1052               0 :     NCDecoderStateDestruct(&old_vstate->dstate);
    1053               0 :     NCValidateFreeState(&old_vstate);
    1054               0 :   }
    1055               0 :   return result;
    1056               0 : }
    1057                 : 
    1058                 : /* Walk the collected information on instruction boundaries and jump targets,
    1059                 :  * and verify that they are legal.
    1060                 :  */
    1061               0 : static void NCJumpSummarize(struct NCValidatorState* vstate) {
    1062               0 :   uint32_t offset;
    1063                 : 
    1064                 :   /* Verify that jumps are to the beginning of instructions, and that the
    1065                 :    * jumped to instruction is not in the middle of a native client pattern.
    1066                 :    */
    1067               0 :   dprint(("CheckTargets: %"NACL_PRIxNaClPcAddress"-%"NACL_PRIxNaClPcAddress"\n",
    1068                 :           vstate->iadrbase, vstate->iadrbase+vstate->codesize));
    1069               0 :   for (offset = 0; offset < vstate->codesize; offset += 1) {
    1070               0 :     if (NCGetAdrTable(offset, vstate->kttable)) {
    1071               0 :       NCStatsCheckTarget(vstate);
    1072               0 :       if (!NCGetAdrTable(offset, vstate->vttable)) {
    1073               0 :         ValidatePrintOffsetError(offset, "Bad jump target", vstate);
    1074               0 :         NCStatsBadTarget(vstate);
    1075               0 :       }
    1076               0 :     }
    1077               0 :   }
    1078                 : 
    1079                 :   /* check basic block boundaries */
    1080               0 :   for (offset = 0; offset < vstate->codesize; offset += vstate->bundle_size) {
    1081               0 :     if (!NCGetAdrTable(offset, vstate->vttable)) {
    1082               0 :       ValidatePrintOffsetError(offset, "Bad basic block alignment", vstate);
    1083               0 :       NCStatsBadAlignment(vstate);
    1084               0 :     }
    1085               0 :   }
    1086               0 : }

Generated by: LCOV version 1.7