| OLD | NEW |
| (Empty) |
| 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_inter
naltypes.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/ncbadprefixm
ask_64.h" | |
| 32 #else | |
| 33 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixm
ask_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 return kMaxDiagnostics; | |
| 80 } | |
| 81 | |
| 82 void NCValidatorSetMaxDiagnostics(int new_value) { | |
| 83 kMaxDiagnostics = new_value; | |
| 84 } | |
| 85 | |
| 86 int NCValidatorDidStubOut(struct NCValidatorState *vstate) { | |
| 87 return vstate->stats.didstubout; | |
| 88 } | |
| 89 | |
| 90 /* This function is intended to only be called by ValidatePrintInstructionError | |
| 91 * and ValidatePrintOffsetError. | |
| 92 */ | |
| 93 static void ValidatePrintError(const NaClPcAddress addr, | |
| 94 const char *msg, | |
| 95 struct NCValidatorState *vstate) { | |
| 96 if (vstate->num_diagnostics != 0) { | |
| 97 NaClErrorReporter* reporter = vstate->dstate.error_reporter; | |
| 98 (*reporter->printf)( | |
| 99 reporter, | |
| 100 "VALIDATOR: %"NACL_PRIxNaClPcAddress": %s\n", addr, msg); | |
| 101 --(vstate->num_diagnostics); | |
| 102 if (vstate->num_diagnostics == 0) { | |
| 103 (*reporter->printf)( | |
| 104 reporter, | |
| 105 "VALIDATOR: Error limit reached, turning off diagnostics!\n"); | |
| 106 } | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 static void ValidatePrintInstructionError(const struct NCDecoderInst *dinst, | |
| 111 const char *msg, | |
| 112 struct NCValidatorState *vstate) { | |
| 113 ValidatePrintError(NCPrintableInstructionAddress(dinst), msg, vstate); | |
| 114 } | |
| 115 | |
| 116 static void ValidatePrintOffsetError(const NaClPcAddress addr, | |
| 117 const char *msg, | |
| 118 struct NCValidatorState *vstate) { | |
| 119 ValidatePrintError(vstate->iadrbase + addr, msg, vstate); | |
| 120 } | |
| 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 static void NCStubOutMem(struct NCValidatorState *vstate, void *ptr, | |
| 127 size_t num) { | |
| 128 vstate->stats.didstubout = 1; | |
| 129 memset(ptr, kNaClFullStop, num); | |
| 130 } | |
| 131 | |
| 132 void NCBadInstructionError(const struct NCDecoderInst *dinst, | |
| 133 const char *msg) { | |
| 134 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 135 ValidatePrintInstructionError(dinst, msg, vstate); | |
| 136 if (vstate->do_stub_out) { | |
| 137 NCStubOutMem(vstate, dinst->dstate->memory.mpc, | |
| 138 dinst->dstate->memory.read_length); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 /* opcode histogram */ | |
| 143 #if VERBOSE == 1 | |
| 144 void OpcodeHisto(const uint8_t byte1, struct NCValidatorState *vstate) { | |
| 145 vstate->opcodehisto[byte1] += 1; | |
| 146 } | |
| 147 | |
| 148 static void InitOpcodeHisto(struct NCValidatorState *vstate) { | |
| 149 int i; | |
| 150 for (i = 0; i < 256; i += 1) vstate->opcodehisto[i] = 0; | |
| 151 } | |
| 152 | |
| 153 static void PrintOpcodeHisto(struct NCValidatorState *vstate) { | |
| 154 int i; | |
| 155 int printed_in_this_row = 0; | |
| 156 NaClErrorReporter* reporter = vstate->dstate.error_reporter; | |
| 157 if (!VERBOSE) return; | |
| 158 (*reporter->printf)(reporter, "\nOpcode Histogram;\n"); | |
| 159 for (i = 0; i < 256; ++i) { | |
| 160 if (0 != vstate->opcodehisto[i]) { | |
| 161 (*reporter->printf)(reporter, "%d\t0x%02x\t", vstate->opcodehisto[i], i); | |
| 162 ++printed_in_this_row; | |
| 163 if (printed_in_this_row > 3) { | |
| 164 printed_in_this_row = 0; | |
| 165 (*reporter->printf)(reporter, "\n"); | |
| 166 } | |
| 167 } | |
| 168 } | |
| 169 if (0 != printed_in_this_row) { | |
| 170 (*reporter->printf)(reporter, "\n"); | |
| 171 } | |
| 172 } | |
| 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 static void NCStatsInst(struct NCValidatorState *vstate) { | |
| 181 vstate->stats.instructions += 1; | |
| 182 } | |
| 183 | |
| 184 static void NCStatsCheckTarget(struct NCValidatorState *vstate) { | |
| 185 vstate->stats.checktarget += 1; | |
| 186 } | |
| 187 | |
| 188 static void NCStatsTargetIndirect(struct NCValidatorState *vstate) { | |
| 189 vstate->stats.targetindirect += 1; | |
| 190 } | |
| 191 | |
| 192 static void NCStatsSawFailure(struct NCValidatorState *vstate) { | |
| 193 vstate->stats.sawfailure = 1; | |
| 194 } | |
| 195 | |
| 196 void NCStatsInternalError(struct NCValidatorState *vstate) { | |
| 197 vstate->stats.internalerrors += 1; | |
| 198 NCStatsSawFailure(vstate); | |
| 199 } | |
| 200 | |
| 201 void NCStatsBadAlignment(struct NCValidatorState *vstate) { | |
| 202 vstate->stats.badalignment += 1; | |
| 203 NCStatsSawFailure(vstate); | |
| 204 } | |
| 205 | |
| 206 static void NCStatsSegFault(struct NCValidatorState *vstate) { | |
| 207 vstate->stats.segfaults += 1; | |
| 208 NCStatsSawFailure(vstate); | |
| 209 } | |
| 210 | |
| 211 static void NCStatsNewSegment(struct NCValidatorState *vstate) { | |
| 212 vstate->stats.segments += 1; | |
| 213 if (vstate->stats.segments > 1) { | |
| 214 vprint(vstate, (reporter, "error: multiple segments\n")); | |
| 215 NCStatsSawFailure(vstate); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 void NCStatsBadTarget(struct NCValidatorState *vstate) { | |
| 220 vstate->stats.badtarget += 1; | |
| 221 NCStatsSawFailure(vstate); | |
| 222 } | |
| 223 | |
| 224 static void NCStatsUnsafeIndirect(struct NCValidatorState *vstate) { | |
| 225 vstate->stats.unsafeindirect += 1; | |
| 226 NCStatsSawFailure(vstate); | |
| 227 } | |
| 228 | |
| 229 static void NCStatsReturn(struct NCValidatorState *vstate) { | |
| 230 vstate->stats.returns += 1; | |
| 231 NCStatsUnsafeIndirect(vstate); | |
| 232 NCStatsSawFailure(vstate); | |
| 233 } | |
| 234 | |
| 235 static void NCStatsIllegalInst(struct NCValidatorState *vstate) { | |
| 236 vstate->stats.illegalinst += 1; | |
| 237 NCStatsSawFailure(vstate); | |
| 238 } | |
| 239 | |
| 240 static void NCStatsBadPrefix(struct NCValidatorState *vstate) { | |
| 241 vstate->stats.badprefix += 1; | |
| 242 vstate->stats.illegalinst += 1; /* a bad prefix is also an invalid inst */ | |
| 243 NCStatsSawFailure(vstate); | |
| 244 } | |
| 245 | |
| 246 static void NCStatsBadInstLength(struct NCValidatorState *vstate) { | |
| 247 vstate->stats.badinstlength += 1; | |
| 248 NCStatsSawFailure(vstate); | |
| 249 } | |
| 250 | |
| 251 static void NCStatsInit(struct NCValidatorState *vstate) { | |
| 252 vstate->stats.instructions = 0; | |
| 253 vstate->stats.segments = 0; | |
| 254 vstate->stats.checktarget = 0; | |
| 255 vstate->stats.targetindirect = 0; | |
| 256 vstate->stats.badtarget = 0; | |
| 257 vstate->stats.unsafeindirect = 0; | |
| 258 vstate->stats.returns = 0; | |
| 259 vstate->stats.illegalinst = 0; | |
| 260 vstate->stats.badalignment = 0; | |
| 261 vstate->stats.internalerrors = 0; | |
| 262 vstate->stats.badinstlength = 0; | |
| 263 vstate->stats.badprefix = 0; | |
| 264 vstate->stats.didstubout = 0; | |
| 265 vstate->stats.sawfailure = 0; | |
| 266 InitOpcodeHisto(vstate); | |
| 267 } | |
| 268 | |
| 269 void NCStatsPrint(struct NCValidatorState *vstate) { | |
| 270 NaClErrorReporter* reporter; | |
| 271 if (!VERBOSE || (vstate == NULL)) return; | |
| 272 reporter = vstate->dstate.error_reporter; | |
| 273 PrintOpcodeHisto(vstate); | |
| 274 (*reporter->printf)(reporter, "Analysis Summary:\n"); | |
| 275 (*reporter->printf)(reporter, "%d Checked instructions\n", | |
| 276 vstate->stats.instructions); | |
| 277 (*reporter->printf)(reporter, "%d checked jump targets\n", | |
| 278 vstate->stats.checktarget); | |
| 279 (*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 if (vstate->stats.didstubout) { | |
| 285 (*reporter->printf)(reporter, "Some instructions were replaced with HLTs"); | |
| 286 } | |
| 287 (*reporter->printf)(reporter, "\nProblems:\n"); | |
| 288 (*reporter->printf)(reporter, "%d illegal instructions\n", | |
| 289 vstate->stats.illegalinst); | |
| 290 (*reporter->printf)(reporter, | |
| 291 "%d bad jump targets\n", vstate->stats.badtarget); | |
| 292 (*reporter->printf)( | |
| 293 reporter, "%d illegal unprotected indirect jumps (including ret)\n", | |
| 294 vstate->stats.unsafeindirect); | |
| 295 (*reporter->printf)(reporter, "%d instruction alignment defects\n", | |
| 296 vstate->stats.badalignment); | |
| 297 (*reporter->printf)(reporter, "%d segmentation errors\n", | |
| 298 vstate->stats.segfaults); | |
| 299 (*reporter->printf)(reporter, "%d bad prefix\n", | |
| 300 vstate->stats.badprefix); | |
| 301 (*reporter->printf)(reporter, "%d bad instruction length\n", | |
| 302 vstate->stats.badinstlength); | |
| 303 (*reporter->printf)(reporter, "%d internal errors\n", | |
| 304 vstate->stats.internalerrors); | |
| 305 } | |
| 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 struct NCValidatorState *NCValidateInit(const NaClPcAddress vbase, | |
| 320 const NaClPcAddress codesize, | |
| 321 const int readonly_text, | |
| 322 const NaClCPUFeaturesX86 *features) { | |
| 323 struct NCValidatorState *vstate = NULL; | |
| 324 const int bundle_size = 32; | |
| 325 | |
| 326 dprint(("NCValidateInit(%"NACL_PRIxNaClPcAddressAll | |
| 327 ", %"NACL_PRIxNaClMemorySizeAll", %08x)\n", vbase, codesize, | |
| 328 bundle_size)); | |
| 329 do { | |
| 330 if (features == NULL) | |
| 331 break; | |
| 332 if ((vbase & (bundle_size - 1)) != 0) | |
| 333 break; | |
| 334 dprint(("ncv_init(%"NACL_PRIxNaClPcAddress", %"NACL_PRIxNaClMemorySize | |
| 335 ")\n", vbase, codesize)); | |
| 336 vstate = (struct NCValidatorState *)calloc(1, sizeof(*vstate)); | |
| 337 if (vstate == NULL) | |
| 338 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 vstate->dstate.error_reporter = &kNCNullErrorReporter; | |
| 344 vstate->num_diagnostics = kMaxDiagnostics; | |
| 345 vstate->iadrbase = vbase; | |
| 346 vstate->codesize = codesize; | |
| 347 vstate->bundle_size = bundle_size; | |
| 348 vstate->bundle_mask = bundle_size - 1; | |
| 349 vstate->vttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1); | |
| 350 vstate->kttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1); | |
| 351 vstate->pattern_nonfirst_insts_table = NULL; | |
| 352 vstate->summarize_fn = NCJumpSummarize; | |
| 353 vstate->do_stub_out = 0; | |
| 354 vstate->readonly_text = readonly_text; | |
| 355 if (vstate->vttable == NULL || vstate->kttable == NULL) | |
| 356 break; | |
| 357 dprint((" allocated tables\n")); | |
| 358 NCStatsInit(vstate); | |
| 359 NaClCopyCPUFeaturesX86(&vstate->cpufeatures, features); | |
| 360 return vstate; | |
| 361 } while (0); | |
| 362 /* Failure. Clean up memory before returning. */ | |
| 363 if (NULL != vstate) { | |
| 364 if (NULL != vstate->kttable) | |
| 365 free(vstate->kttable); | |
| 366 if (NULL != vstate->vttable) | |
| 367 free(vstate->vttable); | |
| 368 free(vstate); | |
| 369 } | |
| 370 return NULL; | |
| 371 } | |
| 372 | |
| 373 void NCValidateSetStubOutMode(struct NCValidatorState *vstate, | |
| 374 int do_stub_out) { | |
| 375 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 if (do_stub_out) { | |
| 381 NCValidateSetNumDiagnostics(vstate, 0); | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 void NCValidateSetNumDiagnostics(struct NCValidatorState* vstate, | |
| 386 int num_diagnostics) { | |
| 387 vstate->num_diagnostics = num_diagnostics; | |
| 388 } | |
| 389 | |
| 390 void NCValidateSetErrorReporter(struct NCValidatorState* state, | |
| 391 NaClErrorReporter* error_reporter) { | |
| 392 NCDecoderStateSetErrorReporter(&state->dstate, error_reporter); | |
| 393 } | |
| 394 | |
| 395 static INLINE void RememberInstructionBoundary(const NCDecoderInst *dinst, | |
| 396 struct NCValidatorState *vstate) { | |
| 397 /* The decoder should never pass us an out-of-bounds instruction. */ | |
| 398 CHECK(dinst->inst_addr < vstate->codesize); | |
| 399 if (NCGetAdrTable(dinst->inst_addr, vstate->vttable)) { | |
| 400 vprint(vstate, (reporter, | |
| 401 "RememberIP: Saw inst at %"NACL_PRIxNaClPcAddressAll | |
| 402 " twice\n", NCPrintableInstructionAddress(dinst))); | |
| 403 NCStatsInternalError(vstate); | |
| 404 return; | |
| 405 } | |
| 406 NCStatsInst(vstate); | |
| 407 NCSetAdrTable(dinst->inst_addr, vstate->vttable); | |
| 408 } | |
| 409 | |
| 410 static void RememberJumpTarget(const NCDecoderInst *dinst, int32_t jump_offset, | |
| 411 struct NCValidatorState *vstate) { | |
| 412 NaClPcAddress target = (dinst->inst_addr + dinst->inst.bytes.length | |
| 413 + jump_offset); | |
| 414 | |
| 415 /* For testing only, this mode disables inter-instruction checks. */ | |
| 416 if (NACL_FLAG_unsafe_single_inst32_mode) return; | |
| 417 | |
| 418 if (target < vstate->codesize) { | |
| 419 NCSetAdrTable(target, vstate->kttable); | |
| 420 } else if ((target & vstate->bundle_mask) == 0) { | |
| 421 /* Allow bundle-aligned jumps. */ | |
| 422 } 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 } else { | |
| 434 ValidatePrintInstructionError(dinst, "JUMP TARGET out of range", vstate); | |
| 435 NCStatsBadTarget(vstate); | |
| 436 } | |
| 437 } | |
| 438 | |
| 439 static void ForgetInstructionBoundary(const NCDecoderInst *dinst, | |
| 440 struct NCValidatorState *vstate) { | |
| 441 /* The decoder should never pass us an out-of-bounds instruction. */ | |
| 442 CHECK(dinst->inst_addr < vstate->codesize); | |
| 443 NCClearAdrTable(dinst->inst_addr, vstate->vttable); | |
| 444 if (NULL != vstate->pattern_nonfirst_insts_table) { | |
| 445 NCSetAdrTable(dinst->inst_addr, vstate->pattern_nonfirst_insts_table); | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 int NCValidateFinish(struct NCValidatorState *vstate) { | |
| 450 if (vstate == NULL) { | |
| 451 dprint(("validator not initialized. Did you call ncvalidate_init()?\n")); | |
| 452 /* non-zero indicates failure */ | |
| 453 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 if (vstate->do_stub_out) return vstate->stats.sawfailure; | |
| 460 | |
| 461 /* Double check that the base address matches the alignment constraint. */ | |
| 462 if (vstate->iadrbase & vstate->bundle_mask) { | |
| 463 /* This should never happen because the alignment of iadrbase is */ | |
| 464 /* checked in NCValidateInit(). */ | |
| 465 ValidatePrintOffsetError(0, "Bad base address alignment", vstate); | |
| 466 NCStatsBadAlignment(vstate); | |
| 467 } | |
| 468 | |
| 469 /* Apply summary analysis to collected data during pass over | |
| 470 * instructions. | |
| 471 */ | |
| 472 (*(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 return (vstate->stats.sawfailure); | |
| 477 } | |
| 478 | |
| 479 void NCValidateFreeState(struct NCValidatorState **vstate) { | |
| 480 CHECK(*vstate != NULL); | |
| 481 free((*vstate)->vttable); | |
| 482 free((*vstate)->kttable); | |
| 483 free((*vstate)->pattern_nonfirst_insts_table); | |
| 484 free(*vstate); | |
| 485 *vstate = NULL; | |
| 486 } | |
| 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 static int ValidateSFenceClFlush(const NCDecoderInst *dinst) { | |
| 491 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 492 uint8_t mrm = NCInstBytesByteInline(&dinst->inst_bytes, 2); | |
| 493 | |
| 494 if (modrm_modInline(mrm) == 3) { | |
| 495 /* this is an sfence */ | |
| 496 if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_FXSR)) | |
| 497 return 0; | |
| 498 return 1; | |
| 499 } else { | |
| 500 /* this is an clflush */ | |
| 501 if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_CLFLUSH)) | |
| 502 return 0; | |
| 503 return 1; | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 static void ValidateCallAlignment(const NCDecoderInst *dinst) { | |
| 508 NaClPcAddress fallthru = dinst->inst_addr + dinst->inst.bytes.length; | |
| 509 struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 510 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 } | |
| 523 } | |
| 524 | |
| 525 static void ValidateJmp8(const NCDecoderInst *dinst) { | |
| 526 int8_t offset = NCInstBytesByteInline(&dinst->inst_bytes, | |
| 527 dinst->inst.prefixbytes+1); | |
| 528 struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 529 NCStatsCheckTarget(vstate); | |
| 530 RememberJumpTarget(dinst, offset, vstate); | |
| 531 } | |
| 532 | |
| 533 static void ValidateJmpz(const NCDecoderInst *dinst) { | |
| 534 NCInstBytesPtr opcode; | |
| 535 uint8_t opcode0; | |
| 536 int32_t offset; | |
| 537 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 538 NCInstBytesPtrInitInc(&opcode, &dinst->inst_bytes, | |
| 539 dinst->inst.prefixbytes); | |
| 540 opcode0 = NCInstBytesByteInline(&opcode, 0); | |
| 541 NCStatsCheckTarget(vstate); | |
| 542 if (opcode0 == 0x0f) { | |
| 543 /* Multbyte opcode. Intruction is of form: | |
| 544 * 0F80 .. 0F8F: jCC $Jz | |
| 545 */ | |
| 546 NCInstBytesPtr opcode_2; | |
| 547 NCInstBytesPtrInitInc(&opcode_2, &opcode, 2); | |
| 548 offset = NCInstBytesInt32(&opcode_2, dinst->inst.immbytes); | |
| 549 } else { | |
| 550 /* Single byte opcode. Must be one of: | |
| 551 * E8: call $Jz | |
| 552 * E9: jmp $Jx | |
| 553 */ | |
| 554 NCInstBytesPtr opcode_1; | |
| 555 NCInstBytesPtrInitInc(&opcode_1, &opcode, 1); | |
| 556 offset = NCInstBytesInt32(&opcode_1, dinst->inst.immbytes); | |
| 557 /* as a courtesy, check call alignment correctness */ | |
| 558 if (opcode0 == 0xe8) ValidateCallAlignment(dinst); | |
| 559 } | |
| 560 RememberJumpTarget(dinst, offset, vstate); | |
| 561 } | |
| 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 static void ValidateIndirect5(const NCDecoderInst *dinst) { | |
| 574 NCInstBytesPtr jmpopcode; | |
| 575 NCInstBytesPtr andopcode; | |
| 576 uint8_t mrm; | |
| 577 uint8_t targetreg; | |
| 578 const uint8_t kReg_ESP = 4; | |
| 579 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 580 | |
| 581 struct NCDecoderInst *andinst = PreviousInst(dinst, 1); | |
| 582 if ((andinst == NULL) || (andinst->inst.bytes.length != 3)) { | |
| 583 NCBadInstructionError(dinst, "Unsafe indirect jump"); | |
| 584 NCStatsUnsafeIndirect(vstate); | |
| 585 return; | |
| 586 } | |
| 587 /* note: no prefixbytes allowed */ | |
| 588 NCInstBytesPtrInitInc(&jmpopcode, &dinst->inst_bytes, 0); | |
| 589 /* note: no prefixbytes allowed */ | |
| 590 NCInstBytesPtrInitInc(&andopcode, &andinst->inst_bytes, 0); | |
| 591 mrm = NCInstBytesByteInline(&jmpopcode, 1); | |
| 592 /* Note that the modrm_rm field holds the | |
| 593 * target addr the modrm_reg is the opcode. | |
| 594 */ | |
| 595 targetreg = modrm_rmInline(mrm); | |
| 596 NCStatsCheckTarget(vstate); | |
| 597 NCStatsTargetIndirect(vstate); | |
| 598 do { | |
| 599 /* no prefix bytes allowed */ | |
| 600 if (dinst->inst.prefixbytes != 0) break; | |
| 601 if (dinst->inst.prefixbytes != 0) break; | |
| 602 /* Check all the opcodes. */ | |
| 603 /* In GROUP5, 2 => call, 4 => jmp */ | |
| 604 if (NCInstBytesByteInline(&jmpopcode, 0) != 0xff) break; | |
| 605 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 if (modrm_modInline(mrm) != 3) break; | |
| 610 if (targetreg == kReg_ESP) break; | |
| 611 if (NCInstBytesByteInline(&andopcode, 0) != 0x83) break; | |
| 612 /* check modrm bytes of or and and instructions */ | |
| 613 if (NCInstBytesByteInline(&andopcode, 1) != (0xe0 | targetreg)) break; | |
| 614 /* check mask */ | |
| 615 if (NCInstBytesByteInline(&andopcode, 2) != | |
| 616 (0x0ff & ~vstate->bundle_mask)) break; | |
| 617 /* All checks look good. Make the sequence 'atomic.' */ | |
| 618 ForgetInstructionBoundary(dinst, vstate); | |
| 619 /* as a courtesy, check call alignment correctness */ | |
| 620 if (modrm_regInline(mrm) == 2) ValidateCallAlignment(dinst); | |
| 621 return; | |
| 622 } while (0); | |
| 623 NCBadInstructionError(dinst, "Unsafe indirect jump"); | |
| 624 NCStatsUnsafeIndirect(vstate); | |
| 625 } | |
| 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 static Bool ValidatePrefixes(const NCDecoderInst *dinst) { | |
| 661 if (dinst->inst.prefixbytes == 0) return TRUE; | |
| 662 | |
| 663 if ((dinst->inst.prefixmask & | |
| 664 BadPrefixMask[dinst->opinfo->insttype]) != 0) { | |
| 665 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 if ((dinst->inst.opcode_prefixmask != 0) && | |
| 672 ((dinst->inst.prefixmask & | |
| 673 (kPrefixDATA16 | kPrefixREPNE | kPrefixREP)) != 0)) { | |
| 674 return FALSE; | |
| 675 } | |
| 676 | |
| 677 /* Only allow a lock if it is a 386L instruction, or the special | |
| 678 * cmpxchg8b instruction. | |
| 679 */ | |
| 680 if (dinst->inst.prefixmask & kPrefixLOCK) { | |
| 681 if ((dinst->opinfo->insttype != NACLi_386L) && | |
| 682 (dinst->opinfo->insttype != NACLi_CMPXCHG8B)) { | |
| 683 return FALSE; | |
| 684 } | |
| 685 } | |
| 686 | |
| 687 /* Only allow more than one prefix if two prefixes, and they are | |
| 688 * data 16 and lock. | |
| 689 */ | |
| 690 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 return FALSE; | |
| 696 } | |
| 697 | |
| 698 return TRUE; | |
| 699 } | |
| 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 static Bool ValidateInst(const NCDecoderInst *dinst) { | |
| 710 NaClCPUFeaturesX86 *cpufeatures; | |
| 711 int squashme = 0; | |
| 712 NCValidatorState* vstate; | |
| 713 if (dinst == NULL) return TRUE; | |
| 714 vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 715 | |
| 716 OpcodeHisto(NCInstBytesByteInline(&dinst->inst_bytes, | |
| 717 dinst->inst.prefixbytes), | |
| 718 vstate); | |
| 719 /* For testing only, this mode disables inter-instruction checks. */ | |
| 720 if (!NACL_FLAG_unsafe_single_inst32_mode) { | |
| 721 RememberInstructionBoundary(dinst, vstate); | |
| 722 } | |
| 723 | |
| 724 cpufeatures = &vstate->cpufeatures; | |
| 725 | |
| 726 if (!ValidatePrefixes(dinst)) { | |
| 727 NCBadInstructionError(dinst, "Bad prefix usage"); | |
| 728 NCStatsBadPrefix(vstate); | |
| 729 } | |
| 730 | |
| 731 if ((dinst->opinfo->insttype != NACLi_NOP) && | |
| 732 ((size_t) (dinst->inst.bytes.length - dinst->inst.prefixbytes) | |
| 733 > kMaxValidInstLength)) { | |
| 734 NCBadInstructionError(dinst, "Instruction too long"); | |
| 735 NCStatsBadInstLength(vstate); | |
| 736 } | |
| 737 | |
| 738 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 break; | |
| 745 case NACLi_LAHF: | |
| 746 if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_TARGET_SUBARCH == 64) | |
| 747 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LAHF); | |
| 748 break; | |
| 749 case NACLi_JMP8: | |
| 750 ValidateJmp8(dinst); | |
| 751 break; | |
| 752 case NACLi_JMPZ: | |
| 753 ValidateJmpz(dinst); | |
| 754 break; | |
| 755 case NACLi_INDIRECT: | |
| 756 ValidateIndirect5(dinst); | |
| 757 break; | |
| 758 case NACLi_X87: | |
| 759 case NACLi_X87_FSINCOS: | |
| 760 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87); | |
| 761 break; | |
| 762 case NACLi_SFENCE_CLFLUSH: | |
| 763 squashme = ValidateSFenceClFlush(dinst); | |
| 764 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 if (modrm_modInline(dinst->inst.mrm) | |
| 771 == kModRmModFieldDefinesRegisterRef) { | |
| 772 NCBadInstructionError(dinst, "Illegal instruction"); | |
| 773 NCStatsIllegalInst(vstate); | |
| 774 } | |
| 775 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CX8); | |
| 776 break; | |
| 777 case NACLi_CMOV: | |
| 778 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV); | |
| 779 break; | |
| 780 case NACLi_FCMOV: | |
| 781 squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV) && | |
| 782 NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87)); | |
| 783 break; | |
| 784 case NACLi_RDTSC: | |
| 785 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_TSC); | |
| 786 break; | |
| 787 case NACLi_MMX: | |
| 788 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX); | |
| 789 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 squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX) || | |
| 794 NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2)); | |
| 795 break; | |
| 796 case NACLi_SSE: | |
| 797 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE); | |
| 798 break; | |
| 799 case NACLi_SSE2: | |
| 800 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2); | |
| 801 break; | |
| 802 case NACLi_SSE3: | |
| 803 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE3); | |
| 804 break; | |
| 805 case NACLi_SSE4A: | |
| 806 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE4A); | |
| 807 break; | |
| 808 case NACLi_SSE41: | |
| 809 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE41); | |
| 810 break; | |
| 811 case NACLi_SSE42: | |
| 812 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE42); | |
| 813 break; | |
| 814 case NACLi_MOVBE: | |
| 815 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MOVBE); | |
| 816 break; | |
| 817 case NACLi_POPCNT: | |
| 818 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_POPCNT); | |
| 819 break; | |
| 820 case NACLi_LZCNT: | |
| 821 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LZCNT); | |
| 822 break; | |
| 823 case NACLi_SSSE3: | |
| 824 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSSE3); | |
| 825 break; | |
| 826 case NACLi_3DNOW: | |
| 827 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_3DNOW); | |
| 828 break; | |
| 829 case NACLi_E3DNOW: | |
| 830 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_E3DNOW); | |
| 831 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 if (!(dinst->inst.opcode_prefixmask & kPrefixDATA16)) { | |
| 842 NCBadInstructionError(dinst, "Bad prefix usage"); | |
| 843 NCStatsBadPrefix(vstate); | |
| 844 } | |
| 845 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2); | |
| 846 break; | |
| 847 | |
| 848 case NACLi_RETURN: | |
| 849 NCBadInstructionError(dinst, "ret instruction (not allowed)"); | |
| 850 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 NCBadInstructionError(dinst, "Illegal instruction"); | |
| 867 NCStatsIllegalInst(vstate); | |
| 868 break; | |
| 869 } | |
| 870 case NACLi_UNDEFINED: { | |
| 871 NCBadInstructionError(dinst, "Undefined instruction"); | |
| 872 NCStatsIllegalInst(vstate); | |
| 873 NCStatsInternalError(vstate); | |
| 874 break; | |
| 875 } | |
| 876 default: | |
| 877 NCBadInstructionError(dinst, "Undefined instruction type"); | |
| 878 NCStatsInternalError(vstate); | |
| 879 break; | |
| 880 } | |
| 881 if (squashme) { | |
| 882 if (vstate->readonly_text) { | |
| 883 NCBadInstructionError(dinst, | |
| 884 "Illegal instruction for fixed-feature CPU mode"); | |
| 885 NCStatsIllegalInst(vstate); | |
| 886 } else { | |
| 887 NCStubOutMem(vstate, dinst->dstate->memory.mpc, | |
| 888 dinst->dstate->memory.read_length); | |
| 889 } | |
| 890 } | |
| 891 return TRUE; | |
| 892 } | |
| 893 | |
| 894 Bool UnsafePartialValidateInst(const NCDecoderInst *dinst) { | |
| 895 NCValidatorState *vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); | |
| 896 Bool result = FALSE; | |
| 897 | |
| 898 NACL_FLAG_unsafe_single_inst32_mode = TRUE; | |
| 899 NCStatsInit(vstate); | |
| 900 if (ValidateInst(dinst)) { | |
| 901 result = vstate->stats.sawfailure == 0; | |
| 902 }; | |
| 903 NACL_FLAG_unsafe_single_inst32_mode = FALSE; | |
| 904 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 static void ValidateIndirect5Replacement(NCDecoderInst *dinst_old, | |
| 913 NCDecoderInst *dinst_new) { | |
| 914 do { | |
| 915 /* check that the and-guard is 3 bytes and bit-for-bit identical */ | |
| 916 NCDecoderInst *andinst_old = PreviousInst(dinst_old, 1); | |
| 917 NCDecoderInst *andinst_new = PreviousInst(dinst_new, 1); | |
| 918 if ((andinst_old == NULL) || (andinst_old->inst.bytes.length != 3)) break; | |
| 919 if ((andinst_new == NULL) || (andinst_new->inst.bytes.length != 3)) break; | |
| 920 if (memcmp(andinst_old->inst.bytes.byte, | |
| 921 andinst_new->inst.bytes.byte, 3) != 0) break; | |
| 922 | |
| 923 /* check that the indirect-jmp is 2 bytes and bit-for-bit identical */ | |
| 924 if (dinst_old->inst.bytes.length != 2) break; | |
| 925 if (dinst_new->inst.bytes.length != 2) break; | |
| 926 if (memcmp(dinst_old->inst.bytes.byte, | |
| 927 dinst_new->inst.bytes.byte, 2) != 0) break; | |
| 928 | |
| 929 return; | |
| 930 } while (0); | |
| 931 NCBadInstructionError(dinst_new, | |
| 932 "Replacement indirect jump must match original"); | |
| 933 NCStatsUnsafeIndirect(NCVALIDATOR_STATE_DOWNCAST(dinst_new->dstate)); | |
| 934 } | |
| 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 static Bool ValidateInstReplacement(NCDecoderStatePair* tthis, | |
| 941 NCDecoderInst *dinst_old, | |
| 942 NCDecoderInst *dinst_new) { | |
| 943 dinst_new->unchanged = memcmp(dinst_old->inst.bytes.byte, | |
| 944 dinst_new->inst.bytes.byte, | |
| 945 dinst_new->inst.bytes.length) == 0; | |
| 946 ValidateInst(dinst_new); | |
| 947 | |
| 948 if (dinst_old->opinfo->insttype == NACLi_INDIRECT | |
| 949 || dinst_new->opinfo->insttype == NACLi_INDIRECT) { | |
| 950 /* Verify that nacljmps never change */ | |
| 951 ValidateIndirect5Replacement(dinst_old, dinst_new); | |
| 952 } | |
| 953 return TRUE; | |
| 954 } | |
| 955 | |
| 956 /* Create the decoder state for the validator state, using the | |
| 957 * given parameters. | |
| 958 */ | |
| 959 static void NCValidateDStateInit(NCValidatorState *vstate, | |
| 960 uint8_t *mbase, NaClPcAddress vbase, | |
| 961 NaClMemorySize sz) { | |
| 962 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 NaClErrorReporter* reporter = dstate->error_reporter; | |
| 968 NCDecoderStateConstruct(dstate, mbase, vbase, sz, | |
| 969 vstate->inst_buffer, kNCValidatorInstBufferSize); | |
| 970 dstate->action_fn = ValidateInst; | |
| 971 dstate->new_segment_fn = (NCDecoderStateMethod) NCStatsNewSegment; | |
| 972 dstate->segmentation_error_fn = (NCDecoderStateMethod) NCStatsSegFault; | |
| 973 dstate->internal_error_fn = (NCDecoderStateMethod) NCStatsInternalError; | |
| 974 NCDecoderStateSetErrorReporter(dstate, reporter); | |
| 975 } | |
| 976 | |
| 977 void NCValidateSegment(uint8_t *mbase, NaClPcAddress vbase, NaClMemorySize sz, | |
| 978 struct NCValidatorState *vstate) { | |
| 979 /* Sanity checks */ | |
| 980 /* TODO(ncbray): remove redundant vbase/size args. */ | |
| 981 if ((vbase & vstate->bundle_mask) != 0) { | |
| 982 ValidatePrintOffsetError(0, "Bad vbase alignment", vstate); | |
| 983 NCStatsSegFault(vstate); | |
| 984 return; | |
| 985 } | |
| 986 if (vbase != vstate->iadrbase) { | |
| 987 ValidatePrintOffsetError(0, "Mismatched vbase addresses", vstate); | |
| 988 NCStatsSegFault(vstate); | |
| 989 return; | |
| 990 } | |
| 991 if (sz != vstate->codesize) { | |
| 992 ValidatePrintOffsetError(0, "Mismatched code size", vstate); | |
| 993 NCStatsSegFault(vstate); | |
| 994 return; | |
| 995 } | |
| 996 | |
| 997 sz = NCHaltTrimSize(mbase, sz, vstate->bundle_size); | |
| 998 vstate->codesize = sz; | |
| 999 | |
| 1000 if (sz == 0) { | |
| 1001 ValidatePrintOffsetError(0, "Bad text segment (zero size)", vstate); | |
| 1002 NCStatsSegFault(vstate); | |
| 1003 return; | |
| 1004 } | |
| 1005 NCValidateDStateInit(vstate, mbase, vbase, sz); | |
| 1006 NCDecoderStateDecode(&vstate->dstate); | |
| 1007 NCDecoderStateDestruct(&vstate->dstate); | |
| 1008 } | |
| 1009 | |
| 1010 int NCValidateSegmentPair(uint8_t *mbase_old, uint8_t *mbase_new, | |
| 1011 NaClPcAddress vbase, size_t sz, | |
| 1012 const NaClCPUFeaturesX86 *features) { | |
| 1013 /* TODO(karl): Refactor to use inheritance from NCDecoderStatePair? */ | |
| 1014 NCDecoderStatePair pair; | |
| 1015 NCValidatorState* new_vstate; | |
| 1016 NCValidatorState* old_vstate; | |
| 1017 | |
| 1018 int result = 0; | |
| 1019 | |
| 1020 /* Verify that we actually have a segment to walk. */ | |
| 1021 if (sz == 0) { | |
| 1022 printf("VALIDATOR: %"NACL_PRIxNaClPcAddress | |
| 1023 ": Bad text segment (zero size)\n", vbase); | |
| 1024 return 0; | |
| 1025 } | |
| 1026 | |
| 1027 old_vstate = NCValidateInit(vbase, sz, FALSE, features); | |
| 1028 if (old_vstate != NULL) { | |
| 1029 NCValidateDStateInit(old_vstate, mbase_old, vbase, sz); | |
| 1030 new_vstate = NCValidateInit(vbase, sz, FALSE, features); | |
| 1031 if (new_vstate != NULL) { | |
| 1032 NCValidateDStateInit(new_vstate, mbase_new, vbase, sz); | |
| 1033 | |
| 1034 NCDecoderStatePairConstruct(&pair, | |
| 1035 &old_vstate->dstate, | |
| 1036 &new_vstate->dstate, | |
| 1037 NULL); /* copy_func */ | |
| 1038 pair.action_fn = ValidateInstReplacement; | |
| 1039 if (NCDecoderStatePairDecode(&pair)) { | |
| 1040 result = 1; | |
| 1041 } else { | |
| 1042 ValidatePrintOffsetError(0, "Replacement not applied!\n", new_vstate); | |
| 1043 } | |
| 1044 if (NCValidateFinish(new_vstate)) { | |
| 1045 /* Errors occurred during validation. */ | |
| 1046 result = 0; | |
| 1047 } | |
| 1048 NCDecoderStatePairDestruct(&pair); | |
| 1049 NCDecoderStateDestruct(&new_vstate->dstate); | |
| 1050 NCValidateFreeState(&new_vstate); | |
| 1051 } | |
| 1052 NCDecoderStateDestruct(&old_vstate->dstate); | |
| 1053 NCValidateFreeState(&old_vstate); | |
| 1054 } | |
| 1055 return result; | |
| 1056 } | |
| 1057 | |
| 1058 /* Walk the collected information on instruction boundaries and jump targets, | |
| 1059 * and verify that they are legal. | |
| 1060 */ | |
| 1061 static void NCJumpSummarize(struct NCValidatorState* vstate) { | |
| 1062 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 dprint(("CheckTargets: %"NACL_PRIxNaClPcAddress"-%"NACL_PRIxNaClPcAddress"\n", | |
| 1068 vstate->iadrbase, vstate->iadrbase+vstate->codesize)); | |
| 1069 for (offset = 0; offset < vstate->codesize; offset += 1) { | |
| 1070 if (NCGetAdrTable(offset, vstate->kttable)) { | |
| 1071 NCStatsCheckTarget(vstate); | |
| 1072 if (!NCGetAdrTable(offset, vstate->vttable)) { | |
| 1073 ValidatePrintOffsetError(offset, "Bad jump target", vstate); | |
| 1074 NCStatsBadTarget(vstate); | |
| 1075 } | |
| 1076 } | |
| 1077 } | |
| 1078 | |
| 1079 /* check basic block boundaries */ | |
| 1080 for (offset = 0; offset < vstate->codesize; offset += vstate->bundle_size) { | |
| 1081 if (!NCGetAdrTable(offset, vstate->vttable)) { | |
| 1082 ValidatePrintOffsetError(offset, "Bad basic block alignment", vstate); | |
| 1083 NCStatsBadAlignment(vstate); | |
| 1084 } | |
| 1085 } | |
| 1086 } | |
| OLD | NEW |