Index: src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.c |
diff --git a/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.c b/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.c |
deleted file mode 100644 |
index 28a66dd0d1b5f565e3b363652a86b74a89623b08..0000000000000000000000000000000000000000 |
--- a/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.c |
+++ /dev/null |
@@ -1,1086 +0,0 @@ |
-/* |
- * Copyright (c) 2012 The Native Client Authors. All rights reserved. |
- * Use of this source code is governed by a BSD-style license that can be |
- * found in the LICENSE file. |
- */ |
- |
-/* |
- * ncvalidate.c |
- * Validate x86 instructions for Native Client |
- * |
- */ |
- |
-#include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.h" |
- |
-#include <stdio.h> |
-#include <stdlib.h> |
-#include <errno.h> |
-#include <string.h> |
-#include <assert.h> |
- |
-#include "native_client/src/include/portability.h" |
-#include "native_client/src/shared/platform/nacl_check.h" |
-#include "native_client/src/trusted/validator/x86/halt_trim.h" |
-#include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncdecode.h" |
-#include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate_internaltypes.h" |
- |
-#include "native_client/src/trusted/validator/x86/ncinstbuffer_inl.c" |
-#include "native_client/src/trusted/validator/x86/x86_insts_inl.c" |
- |
-#if NACL_TARGET_SUBARCH == 64 |
-#include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixmask_64.h" |
-#else |
-#include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixmask_32.h" |
-#endif |
- |
-/* debugging stuff */ |
-#define DEBUGGING 0 |
-#if DEBUGGING |
-#define dprint(args) do { printf args; } while (0) |
-#else |
-#define dprint(args) do { if (0) { printf args; } } while (0) |
-/* allows DCE but compiler can still do format string checks */ |
-#endif /* DEBUGGING */ |
- |
-/* TODO(bradchen) verbosity needs to be controllable via commandline flags */ |
-#define VERBOSE 1 |
-#if VERBOSE |
-#define vprint(vstate, args) \ |
- do { \ |
- NaClErrorReporter* reporter = vstate->dstate.error_reporter; \ |
- (*reporter->printf) args; \ |
- } while (0) |
-#else |
-#define vprint(vstate, args) \ |
- do { \ |
- if (0) { \ |
- NaClErrorReporter* reporter = vstate->dstate.error_reporter; \ |
- (*reporter->printf) args; \ |
- } \ |
- } while (0) |
-/* allows DCE but compiler can still do format string checks */ |
-#endif /* VERBOSE */ |
- |
-static const uint8_t kNaClFullStop = 0xf4; /* x86 HALT opcode */ |
- |
-/* Define how many diagnostic error messages are printed by the validator. |
- * A value of zero generates no diagnostics. |
- * A value >0 allows up to that many diagnostic error messages. |
- * A negative value prints all diagnostic error messages. |
- */ |
-static int kMaxDiagnostics = 0; |
- |
-/* This flag controls a mode for testing only in which inter-instruction |
- * checks are disabled. |
- */ |
-static Bool NACL_FLAG_unsafe_single_inst32_mode = FALSE; |
- |
-int NCValidatorGetMaxDiagnostics(void) { |
- return kMaxDiagnostics; |
-} |
- |
-void NCValidatorSetMaxDiagnostics(int new_value) { |
- kMaxDiagnostics = new_value; |
-} |
- |
-int NCValidatorDidStubOut(struct NCValidatorState *vstate) { |
- return vstate->stats.didstubout; |
-} |
- |
-/* This function is intended to only be called by ValidatePrintInstructionError |
- * and ValidatePrintOffsetError. |
- */ |
-static void ValidatePrintError(const NaClPcAddress addr, |
- const char *msg, |
- struct NCValidatorState *vstate) { |
- if (vstate->num_diagnostics != 0) { |
- NaClErrorReporter* reporter = vstate->dstate.error_reporter; |
- (*reporter->printf)( |
- reporter, |
- "VALIDATOR: %"NACL_PRIxNaClPcAddress": %s\n", addr, msg); |
- --(vstate->num_diagnostics); |
- if (vstate->num_diagnostics == 0) { |
- (*reporter->printf)( |
- reporter, |
- "VALIDATOR: Error limit reached, turning off diagnostics!\n"); |
- } |
- } |
-} |
- |
-static void ValidatePrintInstructionError(const struct NCDecoderInst *dinst, |
- const char *msg, |
- struct NCValidatorState *vstate) { |
- ValidatePrintError(NCPrintableInstructionAddress(dinst), msg, vstate); |
-} |
- |
-static void ValidatePrintOffsetError(const NaClPcAddress addr, |
- const char *msg, |
- struct NCValidatorState *vstate) { |
- ValidatePrintError(vstate->iadrbase + addr, msg, vstate); |
-} |
- |
-/* The low-level implementation for stubbing out an instruction. Always use |
- * this function to (ultimately) stub out instructions. This makes it possible |
- * to detect when the validator modifies the code. |
- */ |
-static void NCStubOutMem(struct NCValidatorState *vstate, void *ptr, |
- size_t num) { |
- vstate->stats.didstubout = 1; |
- memset(ptr, kNaClFullStop, num); |
-} |
- |
-void NCBadInstructionError(const struct NCDecoderInst *dinst, |
- const char *msg) { |
- NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- ValidatePrintInstructionError(dinst, msg, vstate); |
- if (vstate->do_stub_out) { |
- NCStubOutMem(vstate, dinst->dstate->memory.mpc, |
- dinst->dstate->memory.read_length); |
- } |
-} |
- |
-/* opcode histogram */ |
-#if VERBOSE == 1 |
-void OpcodeHisto(const uint8_t byte1, struct NCValidatorState *vstate) { |
- vstate->opcodehisto[byte1] += 1; |
-} |
- |
-static void InitOpcodeHisto(struct NCValidatorState *vstate) { |
- int i; |
- for (i = 0; i < 256; i += 1) vstate->opcodehisto[i] = 0; |
-} |
- |
-static void PrintOpcodeHisto(struct NCValidatorState *vstate) { |
- int i; |
- int printed_in_this_row = 0; |
- NaClErrorReporter* reporter = vstate->dstate.error_reporter; |
- if (!VERBOSE) return; |
- (*reporter->printf)(reporter, "\nOpcode Histogram;\n"); |
- for (i = 0; i < 256; ++i) { |
- if (0 != vstate->opcodehisto[i]) { |
- (*reporter->printf)(reporter, "%d\t0x%02x\t", vstate->opcodehisto[i], i); |
- ++printed_in_this_row; |
- if (printed_in_this_row > 3) { |
- printed_in_this_row = 0; |
- (*reporter->printf)(reporter, "\n"); |
- } |
- } |
- } |
- if (0 != printed_in_this_row) { |
- (*reporter->printf)(reporter, "\n"); |
- } |
-} |
-#else |
-#define OpcodeHisto(b, v) |
-#define InitOpcodeHisto(v) |
-#define PrintOpcodeHisto(f, v) |
-#endif /* VERBOSE == 1 */ |
- |
-/* statistics code */ |
-static void NCStatsInst(struct NCValidatorState *vstate) { |
- vstate->stats.instructions += 1; |
-} |
- |
-static void NCStatsCheckTarget(struct NCValidatorState *vstate) { |
- vstate->stats.checktarget += 1; |
-} |
- |
-static void NCStatsTargetIndirect(struct NCValidatorState *vstate) { |
- vstate->stats.targetindirect += 1; |
-} |
- |
-static void NCStatsSawFailure(struct NCValidatorState *vstate) { |
- vstate->stats.sawfailure = 1; |
-} |
- |
-void NCStatsInternalError(struct NCValidatorState *vstate) { |
- vstate->stats.internalerrors += 1; |
- NCStatsSawFailure(vstate); |
-} |
- |
-void NCStatsBadAlignment(struct NCValidatorState *vstate) { |
- vstate->stats.badalignment += 1; |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsSegFault(struct NCValidatorState *vstate) { |
- vstate->stats.segfaults += 1; |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsNewSegment(struct NCValidatorState *vstate) { |
- vstate->stats.segments += 1; |
- if (vstate->stats.segments > 1) { |
- vprint(vstate, (reporter, "error: multiple segments\n")); |
- NCStatsSawFailure(vstate); |
- } |
-} |
- |
-void NCStatsBadTarget(struct NCValidatorState *vstate) { |
- vstate->stats.badtarget += 1; |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsUnsafeIndirect(struct NCValidatorState *vstate) { |
- vstate->stats.unsafeindirect += 1; |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsReturn(struct NCValidatorState *vstate) { |
- vstate->stats.returns += 1; |
- NCStatsUnsafeIndirect(vstate); |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsIllegalInst(struct NCValidatorState *vstate) { |
- vstate->stats.illegalinst += 1; |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsBadPrefix(struct NCValidatorState *vstate) { |
- vstate->stats.badprefix += 1; |
- vstate->stats.illegalinst += 1; /* a bad prefix is also an invalid inst */ |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsBadInstLength(struct NCValidatorState *vstate) { |
- vstate->stats.badinstlength += 1; |
- NCStatsSawFailure(vstate); |
-} |
- |
-static void NCStatsInit(struct NCValidatorState *vstate) { |
- vstate->stats.instructions = 0; |
- vstate->stats.segments = 0; |
- vstate->stats.checktarget = 0; |
- vstate->stats.targetindirect = 0; |
- vstate->stats.badtarget = 0; |
- vstate->stats.unsafeindirect = 0; |
- vstate->stats.returns = 0; |
- vstate->stats.illegalinst = 0; |
- vstate->stats.badalignment = 0; |
- vstate->stats.internalerrors = 0; |
- vstate->stats.badinstlength = 0; |
- vstate->stats.badprefix = 0; |
- vstate->stats.didstubout = 0; |
- vstate->stats.sawfailure = 0; |
- InitOpcodeHisto(vstate); |
-} |
- |
-void NCStatsPrint(struct NCValidatorState *vstate) { |
- NaClErrorReporter* reporter; |
- if (!VERBOSE || (vstate == NULL)) return; |
- reporter = vstate->dstate.error_reporter; |
- PrintOpcodeHisto(vstate); |
- (*reporter->printf)(reporter, "Analysis Summary:\n"); |
- (*reporter->printf)(reporter, "%d Checked instructions\n", |
- vstate->stats.instructions); |
- (*reporter->printf)(reporter, "%d checked jump targets\n", |
- vstate->stats.checktarget); |
- (*reporter->printf)( |
- reporter, "%d calls/jumps need dynamic checking (%0.2f%%)\n", |
- vstate->stats.targetindirect, |
- vstate->stats.instructions ? |
- 100.0 * vstate->stats.targetindirect/vstate->stats.instructions : 0); |
- if (vstate->stats.didstubout) { |
- (*reporter->printf)(reporter, "Some instructions were replaced with HLTs"); |
- } |
- (*reporter->printf)(reporter, "\nProblems:\n"); |
- (*reporter->printf)(reporter, "%d illegal instructions\n", |
- vstate->stats.illegalinst); |
- (*reporter->printf)(reporter, |
- "%d bad jump targets\n", vstate->stats.badtarget); |
- (*reporter->printf)( |
- reporter, "%d illegal unprotected indirect jumps (including ret)\n", |
- vstate->stats.unsafeindirect); |
- (*reporter->printf)(reporter, "%d instruction alignment defects\n", |
- vstate->stats.badalignment); |
- (*reporter->printf)(reporter, "%d segmentation errors\n", |
- vstate->stats.segfaults); |
- (*reporter->printf)(reporter, "%d bad prefix\n", |
- vstate->stats.badprefix); |
- (*reporter->printf)(reporter, "%d bad instruction length\n", |
- vstate->stats.badinstlength); |
- (*reporter->printf)(reporter, "%d internal errors\n", |
- vstate->stats.internalerrors); |
-} |
- |
-/***********************************************************************/ |
-/* jump target table */ |
-const uint8_t nc_iadrmasks[8] = {0x01, 0x02, 0x04, 0x08, |
- 0x10, 0x20, 0x40, 0x80}; |
- |
-/* forward declarations, needed for registration */ |
-static Bool ValidateInst(const NCDecoderInst *dinst); |
-static Bool ValidateInstReplacement(NCDecoderStatePair* tthis, |
- NCDecoderInst *dinst_old, |
- NCDecoderInst *dinst_new); |
-static void NCJumpSummarize(struct NCValidatorState* vstate); |
- |
-struct NCValidatorState *NCValidateInit(const NaClPcAddress vbase, |
- const NaClPcAddress codesize, |
- const int readonly_text, |
- const NaClCPUFeaturesX86 *features) { |
- struct NCValidatorState *vstate = NULL; |
- const int bundle_size = 32; |
- |
- dprint(("NCValidateInit(%"NACL_PRIxNaClPcAddressAll |
- ", %"NACL_PRIxNaClMemorySizeAll", %08x)\n", vbase, codesize, |
- bundle_size)); |
- do { |
- if (features == NULL) |
- break; |
- if ((vbase & (bundle_size - 1)) != 0) |
- break; |
- dprint(("ncv_init(%"NACL_PRIxNaClPcAddress", %"NACL_PRIxNaClMemorySize |
- ")\n", vbase, codesize)); |
- vstate = (struct NCValidatorState *)calloc(1, sizeof(*vstate)); |
- if (vstate == NULL) |
- break; |
- /* Record default error reporter here, since we don't construct |
- * the decoder state until the call to NCValidateSegment. This allows |
- * us to update the error reporter in the decoder state properly. |
- */ |
- vstate->dstate.error_reporter = &kNCNullErrorReporter; |
- vstate->num_diagnostics = kMaxDiagnostics; |
- vstate->iadrbase = vbase; |
- vstate->codesize = codesize; |
- vstate->bundle_size = bundle_size; |
- vstate->bundle_mask = bundle_size - 1; |
- vstate->vttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1); |
- vstate->kttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1); |
- vstate->pattern_nonfirst_insts_table = NULL; |
- vstate->summarize_fn = NCJumpSummarize; |
- vstate->do_stub_out = 0; |
- vstate->readonly_text = readonly_text; |
- if (vstate->vttable == NULL || vstate->kttable == NULL) |
- break; |
- dprint((" allocated tables\n")); |
- NCStatsInit(vstate); |
- NaClCopyCPUFeaturesX86(&vstate->cpufeatures, features); |
- return vstate; |
- } while (0); |
- /* Failure. Clean up memory before returning. */ |
- if (NULL != vstate) { |
- if (NULL != vstate->kttable) |
- free(vstate->kttable); |
- if (NULL != vstate->vttable) |
- free(vstate->vttable); |
- free(vstate); |
- } |
- return NULL; |
-} |
- |
-void NCValidateSetStubOutMode(struct NCValidatorState *vstate, |
- int do_stub_out) { |
- vstate->do_stub_out = do_stub_out; |
- /* We also turn off error diagnostics, under the assumption |
- * you don't want them. (Note: if the user wants them, |
- * you can run ncval to get them)/ |
- */ |
- if (do_stub_out) { |
- NCValidateSetNumDiagnostics(vstate, 0); |
- } |
-} |
- |
-void NCValidateSetNumDiagnostics(struct NCValidatorState* vstate, |
- int num_diagnostics) { |
- vstate->num_diagnostics = num_diagnostics; |
-} |
- |
-void NCValidateSetErrorReporter(struct NCValidatorState* state, |
- NaClErrorReporter* error_reporter) { |
- NCDecoderStateSetErrorReporter(&state->dstate, error_reporter); |
-} |
- |
-static INLINE void RememberInstructionBoundary(const NCDecoderInst *dinst, |
- struct NCValidatorState *vstate) { |
- /* The decoder should never pass us an out-of-bounds instruction. */ |
- CHECK(dinst->inst_addr < vstate->codesize); |
- if (NCGetAdrTable(dinst->inst_addr, vstate->vttable)) { |
- vprint(vstate, (reporter, |
- "RememberIP: Saw inst at %"NACL_PRIxNaClPcAddressAll |
- " twice\n", NCPrintableInstructionAddress(dinst))); |
- NCStatsInternalError(vstate); |
- return; |
- } |
- NCStatsInst(vstate); |
- NCSetAdrTable(dinst->inst_addr, vstate->vttable); |
-} |
- |
-static void RememberJumpTarget(const NCDecoderInst *dinst, int32_t jump_offset, |
- struct NCValidatorState *vstate) { |
- NaClPcAddress target = (dinst->inst_addr + dinst->inst.bytes.length |
- + jump_offset); |
- |
- /* For testing only, this mode disables inter-instruction checks. */ |
- if (NACL_FLAG_unsafe_single_inst32_mode) return; |
- |
- if (target < vstate->codesize) { |
- NCSetAdrTable(target, vstate->kttable); |
- } else if ((target & vstate->bundle_mask) == 0) { |
- /* Allow bundle-aligned jumps. */ |
- } else if (dinst->unchanged) { |
- /* If we are replacing this instruction during dynamic code modification |
- * and it has not changed, the jump target must be valid because the |
- * instruction has been previously validated. However, we may be only |
- * replacing a subsection of the code segment and therefore may not have |
- * information about instruction boundaries outside of the code being |
- * replaced. Therefore, we allow unaligned direct jumps outside of the code |
- * being validated if and only if the instruction is unchanged. |
- * If dynamic code replacement is not being performed, inst->unchanged |
- * should always be false. |
- */ |
- } else { |
- ValidatePrintInstructionError(dinst, "JUMP TARGET out of range", vstate); |
- NCStatsBadTarget(vstate); |
- } |
-} |
- |
-static void ForgetInstructionBoundary(const NCDecoderInst *dinst, |
- struct NCValidatorState *vstate) { |
- /* The decoder should never pass us an out-of-bounds instruction. */ |
- CHECK(dinst->inst_addr < vstate->codesize); |
- NCClearAdrTable(dinst->inst_addr, vstate->vttable); |
- if (NULL != vstate->pattern_nonfirst_insts_table) { |
- NCSetAdrTable(dinst->inst_addr, vstate->pattern_nonfirst_insts_table); |
- } |
-} |
- |
-int NCValidateFinish(struct NCValidatorState *vstate) { |
- if (vstate == NULL) { |
- dprint(("validator not initialized. Did you call ncvalidate_init()?\n")); |
- /* non-zero indicates failure */ |
- return 1; |
- } |
- |
- /* If we are stubbing out code, the following checks don't provide any |
- * usefull information, so quit early. |
- */ |
- if (vstate->do_stub_out) return vstate->stats.sawfailure; |
- |
- /* Double check that the base address matches the alignment constraint. */ |
- if (vstate->iadrbase & vstate->bundle_mask) { |
- /* This should never happen because the alignment of iadrbase is */ |
- /* checked in NCValidateInit(). */ |
- ValidatePrintOffsetError(0, "Bad base address alignment", vstate); |
- NCStatsBadAlignment(vstate); |
- } |
- |
- /* Apply summary analysis to collected data during pass over |
- * instructions. |
- */ |
- (*(vstate->summarize_fn))(vstate); |
- |
- /* Now that all the work is done, generate return code. */ |
- /* Return zero if there are no problems. */ |
- return (vstate->stats.sawfailure); |
-} |
- |
-void NCValidateFreeState(struct NCValidatorState **vstate) { |
- CHECK(*vstate != NULL); |
- free((*vstate)->vttable); |
- free((*vstate)->kttable); |
- free((*vstate)->pattern_nonfirst_insts_table); |
- free(*vstate); |
- *vstate = NULL; |
-} |
- |
-/* ValidateSFenceClFlush is called for the sfence/clflush opcode 0f ae /7 */ |
-/* It returns 0 if the current instruction is implemented, and 1 if not. */ |
-static int ValidateSFenceClFlush(const NCDecoderInst *dinst) { |
- NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- uint8_t mrm = NCInstBytesByteInline(&dinst->inst_bytes, 2); |
- |
- if (modrm_modInline(mrm) == 3) { |
- /* this is an sfence */ |
- if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_FXSR)) |
- return 0; |
- return 1; |
- } else { |
- /* this is an clflush */ |
- if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_CLFLUSH)) |
- return 0; |
- return 1; |
- } |
-} |
- |
-static void ValidateCallAlignment(const NCDecoderInst *dinst) { |
- NaClPcAddress fallthru = dinst->inst_addr + dinst->inst.bytes.length; |
- struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- if (fallthru & vstate->bundle_mask) { |
-#if defined(ERROR_ON_CALL_BUNDLE_ALIGNMENT) |
- /* NOTE: Previously the validator recorded an error for call instructions |
- * that were not aligned against the end of a bundle, as these, while |
- * safe, are not correct with the current code generation idioms. |
- * This #if defined(ERROR_ON_CALL_BUNDLE_ALIGNMENT) was added to allow |
- * experimentation with different call/return idioms. |
- */ |
- ValidatePrintInstructionError(dinst, "Bad call alignment", vstate); |
- /* This makes bad call alignment a fatal error. */ |
- NCStatsBadAlignment(vstate); |
-#endif |
- } |
-} |
- |
-static void ValidateJmp8(const NCDecoderInst *dinst) { |
- int8_t offset = NCInstBytesByteInline(&dinst->inst_bytes, |
- dinst->inst.prefixbytes+1); |
- struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- NCStatsCheckTarget(vstate); |
- RememberJumpTarget(dinst, offset, vstate); |
-} |
- |
-static void ValidateJmpz(const NCDecoderInst *dinst) { |
- NCInstBytesPtr opcode; |
- uint8_t opcode0; |
- int32_t offset; |
- NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- NCInstBytesPtrInitInc(&opcode, &dinst->inst_bytes, |
- dinst->inst.prefixbytes); |
- opcode0 = NCInstBytesByteInline(&opcode, 0); |
- NCStatsCheckTarget(vstate); |
- if (opcode0 == 0x0f) { |
- /* Multbyte opcode. Intruction is of form: |
- * 0F80 .. 0F8F: jCC $Jz |
- */ |
- NCInstBytesPtr opcode_2; |
- NCInstBytesPtrInitInc(&opcode_2, &opcode, 2); |
- offset = NCInstBytesInt32(&opcode_2, dinst->inst.immbytes); |
- } else { |
- /* Single byte opcode. Must be one of: |
- * E8: call $Jz |
- * E9: jmp $Jx |
- */ |
- NCInstBytesPtr opcode_1; |
- NCInstBytesPtrInitInc(&opcode_1, &opcode, 1); |
- offset = NCInstBytesInt32(&opcode_1, dinst->inst.immbytes); |
- /* as a courtesy, check call alignment correctness */ |
- if (opcode0 == 0xe8) ValidateCallAlignment(dinst); |
- } |
- RememberJumpTarget(dinst, offset, vstate); |
-} |
- |
-/* |
- * The NaCl five-byte safe indirect calling sequence looks like this: |
- * 83 e0 e0 and $0xe0,%eax |
- * ff d0 call *%eax |
- * The call may be replaced with a ff e0 jmp. Any register may |
- * be used, not just eax. The validator requires exactly this |
- * sequence. |
- * Note: The code above assumes 32-bit alignment. Change e0 as appropriate |
- * if a different alignment is used. |
- */ |
-static void ValidateIndirect5(const NCDecoderInst *dinst) { |
- NCInstBytesPtr jmpopcode; |
- NCInstBytesPtr andopcode; |
- uint8_t mrm; |
- uint8_t targetreg; |
- const uint8_t kReg_ESP = 4; |
- NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- |
- struct NCDecoderInst *andinst = PreviousInst(dinst, 1); |
- if ((andinst == NULL) || (andinst->inst.bytes.length != 3)) { |
- NCBadInstructionError(dinst, "Unsafe indirect jump"); |
- NCStatsUnsafeIndirect(vstate); |
- return; |
- } |
- /* note: no prefixbytes allowed */ |
- NCInstBytesPtrInitInc(&jmpopcode, &dinst->inst_bytes, 0); |
- /* note: no prefixbytes allowed */ |
- NCInstBytesPtrInitInc(&andopcode, &andinst->inst_bytes, 0); |
- mrm = NCInstBytesByteInline(&jmpopcode, 1); |
- /* Note that the modrm_rm field holds the |
- * target addr the modrm_reg is the opcode. |
- */ |
- targetreg = modrm_rmInline(mrm); |
- NCStatsCheckTarget(vstate); |
- NCStatsTargetIndirect(vstate); |
- do { |
- /* no prefix bytes allowed */ |
- if (dinst->inst.prefixbytes != 0) break; |
- if (dinst->inst.prefixbytes != 0) break; |
- /* Check all the opcodes. */ |
- /* In GROUP5, 2 => call, 4 => jmp */ |
- if (NCInstBytesByteInline(&jmpopcode, 0) != 0xff) break; |
- if ((modrm_regInline(mrm) != 2) && (modrm_regInline(mrm) != 4)) break; |
- /* Issue 32: disallow unsafe call/jump indirection */ |
- /* example: ff 12 call (*edx) */ |
- /* Reported by defend.the.world on 11 Dec 2008 */ |
- if (modrm_modInline(mrm) != 3) break; |
- if (targetreg == kReg_ESP) break; |
- if (NCInstBytesByteInline(&andopcode, 0) != 0x83) break; |
- /* check modrm bytes of or and and instructions */ |
- if (NCInstBytesByteInline(&andopcode, 1) != (0xe0 | targetreg)) break; |
- /* check mask */ |
- if (NCInstBytesByteInline(&andopcode, 2) != |
- (0x0ff & ~vstate->bundle_mask)) break; |
- /* All checks look good. Make the sequence 'atomic.' */ |
- ForgetInstructionBoundary(dinst, vstate); |
- /* as a courtesy, check call alignment correctness */ |
- if (modrm_regInline(mrm) == 2) ValidateCallAlignment(dinst); |
- return; |
- } while (0); |
- NCBadInstructionError(dinst, "Unsafe indirect jump"); |
- NCStatsUnsafeIndirect(vstate); |
-} |
- |
-/* Checks if the set of prefixes are allowed for the instruction. |
- * By default, we only allow prefixes if they have been allowed |
- * by the bad prefix mask generated inside ncdecode_table.c. |
- * These masks are defined by the NaClInstType of the instruction. |
- * See ncdecode_table.c for more details on how these masks are set. |
- * |
- * Currently: |
- * Only 386, 386L, 386R, 386RE instruction allow Data 16 |
- * (unless used as part of instruction selection in a multibyte instruction). |
- * Only 386, JMP8, and JMPZ allow segment registers prefixes. |
- * Only 386L and CMPXCHG8B allow the LOCK prefix. |
- * Only 386R and 386RE instructions allow the REP prefix. |
- * Only 386RE instructions allow the REPNE prefix. |
- * |
- * Note: The prefixmask does not include the prefix value (if any) used to |
- * select multiple byte instructions. Such prefixes have been moved to |
- * opcode_prefixmask, so that the selection (based on that prefix) has |
- * been recorded. |
- * |
- * In general, we do not allow multiple prefixes. Exceptions are as |
- * follows: |
- * 1 - Data 16 is allowed on lock instructions, so that 2 byte values |
- * can be locked. |
- * 2 - Multibyte instructions that are selected |
- * using prefix values Data 16, REP and REPNE, can only have |
- * one of these prefixes (Combinations of these three prefixes |
- * are not allowed for such multibyte instructions). |
- * 3 - Locks are only allowed on instructions with type 386L. The |
- * exception is inst cmpxch8b, which also can have a lock. |
- * 4 - The only two prefix byte combination allowed is Data 16 and Lock. |
- * 5 - Long nops that are hard coded can contain more than one prefix. |
- * See ncdecode.c for details (they don't use ValidatePrefixes). |
- */ |
-static Bool ValidatePrefixes(const NCDecoderInst *dinst) { |
- if (dinst->inst.prefixbytes == 0) return TRUE; |
- |
- if ((dinst->inst.prefixmask & |
- BadPrefixMask[dinst->opinfo->insttype]) != 0) { |
- return FALSE; |
- } |
- |
- /* If a multibyte instruction is using a selection prefix, be |
- * sure that there is no conflict with other selection prefixes. |
- */ |
- if ((dinst->inst.opcode_prefixmask != 0) && |
- ((dinst->inst.prefixmask & |
- (kPrefixDATA16 | kPrefixREPNE | kPrefixREP)) != 0)) { |
- return FALSE; |
- } |
- |
- /* Only allow a lock if it is a 386L instruction, or the special |
- * cmpxchg8b instruction. |
- */ |
- if (dinst->inst.prefixmask & kPrefixLOCK) { |
- if ((dinst->opinfo->insttype != NACLi_386L) && |
- (dinst->opinfo->insttype != NACLi_CMPXCHG8B)) { |
- return FALSE; |
- } |
- } |
- |
- /* Only allow more than one prefix if two prefixes, and they are |
- * data 16 and lock. |
- */ |
- if ((dinst->inst.prefixbytes > 1) && |
- !((dinst->inst.prefixbytes == 2) && |
- (dinst->inst.prefixmask == (kPrefixLOCK | kPrefixDATA16)) && |
- /* Be sure data 16 (66) appears before lock (f0) prefix. */ |
- (dinst->inst.lock_prefix_index == 1))) { |
- return FALSE; |
- } |
- |
- return TRUE; |
-} |
- |
-static const size_t kMaxValidInstLength = 11; |
- |
-/* The modrm mod field is a two-bit value. Values 00, 01, and, 10 |
- * define memory references. Value 11 defines register accesses instead |
- * of memory. |
- */ |
-static const int kModRmModFieldDefinesRegisterRef = 0x3; |
- |
-static Bool ValidateInst(const NCDecoderInst *dinst) { |
- NaClCPUFeaturesX86 *cpufeatures; |
- int squashme = 0; |
- NCValidatorState* vstate; |
- if (dinst == NULL) return TRUE; |
- vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- |
- OpcodeHisto(NCInstBytesByteInline(&dinst->inst_bytes, |
- dinst->inst.prefixbytes), |
- vstate); |
- /* For testing only, this mode disables inter-instruction checks. */ |
- if (!NACL_FLAG_unsafe_single_inst32_mode) { |
- RememberInstructionBoundary(dinst, vstate); |
- } |
- |
- cpufeatures = &vstate->cpufeatures; |
- |
- if (!ValidatePrefixes(dinst)) { |
- NCBadInstructionError(dinst, "Bad prefix usage"); |
- NCStatsBadPrefix(vstate); |
- } |
- |
- if ((dinst->opinfo->insttype != NACLi_NOP) && |
- ((size_t) (dinst->inst.bytes.length - dinst->inst.prefixbytes) |
- > kMaxValidInstLength)) { |
- NCBadInstructionError(dinst, "Instruction too long"); |
- NCStatsBadInstLength(vstate); |
- } |
- |
- switch (dinst->opinfo->insttype) { |
- case NACLi_NOP: |
- case NACLi_386: |
- case NACLi_386L: |
- case NACLi_386R: |
- case NACLi_386RE: |
- break; |
- case NACLi_LAHF: |
- if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_TARGET_SUBARCH == 64) |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LAHF); |
- break; |
- case NACLi_JMP8: |
- ValidateJmp8(dinst); |
- break; |
- case NACLi_JMPZ: |
- ValidateJmpz(dinst); |
- break; |
- case NACLi_INDIRECT: |
- ValidateIndirect5(dinst); |
- break; |
- case NACLi_X87: |
- case NACLi_X87_FSINCOS: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87); |
- break; |
- case NACLi_SFENCE_CLFLUSH: |
- squashme = ValidateSFenceClFlush(dinst); |
- break; |
- case NACLi_CMPXCHG8B: |
- /* Only allow if the modrm mod field accesses memory. |
- * This stops us from accepting f00f on multiple bytes. |
- * http://en.wikipedia.org/wiki/Pentium_F00F_bug |
- */ |
- if (modrm_modInline(dinst->inst.mrm) |
- == kModRmModFieldDefinesRegisterRef) { |
- NCBadInstructionError(dinst, "Illegal instruction"); |
- NCStatsIllegalInst(vstate); |
- } |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CX8); |
- break; |
- case NACLi_CMOV: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV); |
- break; |
- case NACLi_FCMOV: |
- squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV) && |
- NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87)); |
- break; |
- case NACLi_RDTSC: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_TSC); |
- break; |
- case NACLi_MMX: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX); |
- break; |
- case NACLi_MMXSSE2: |
- /* Note: We accept these instructions if either MMX or SSE2 bits */ |
- /* are set, in case MMX instructions go away someday... */ |
- squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX) || |
- NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2)); |
- break; |
- case NACLi_SSE: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE); |
- break; |
- case NACLi_SSE2: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2); |
- break; |
- case NACLi_SSE3: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE3); |
- break; |
- case NACLi_SSE4A: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE4A); |
- break; |
- case NACLi_SSE41: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE41); |
- break; |
- case NACLi_SSE42: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE42); |
- break; |
- case NACLi_MOVBE: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MOVBE); |
- break; |
- case NACLi_POPCNT: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_POPCNT); |
- break; |
- case NACLi_LZCNT: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LZCNT); |
- break; |
- case NACLi_SSSE3: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSSE3); |
- break; |
- case NACLi_3DNOW: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_3DNOW); |
- break; |
- case NACLi_E3DNOW: |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_E3DNOW); |
- break; |
- case NACLi_SSE2x: |
- /* This case requires CPUID checking code */ |
- /* Note: DATA16 prefix required. The generated table |
- * for group 14 (which the only 2 SSE2x instructions are in), |
- * allows instructions with and without a 66 prefix. However, |
- * the SSE2x instructions psrldq and pslldq are only allowed |
- * with the 66 prefix. Hence, this code has been added to |
- * do this check. |
- */ |
- if (!(dinst->inst.opcode_prefixmask & kPrefixDATA16)) { |
- NCBadInstructionError(dinst, "Bad prefix usage"); |
- NCStatsBadPrefix(vstate); |
- } |
- squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2); |
- break; |
- |
- case NACLi_RETURN: |
- NCBadInstructionError(dinst, "ret instruction (not allowed)"); |
- NCStatsReturn(vstate); |
- /* ... and fall through to illegal instruction code */ |
- case NACLi_EMMX: |
- /* EMMX needs to be supported someday but isn't ready yet. */ |
- case NACLi_INVALID: |
- case NACLi_ILLEGAL: |
- case NACLi_SYSTEM: |
- case NACLi_RDMSR: |
- case NACLi_RDTSCP: |
- case NACLi_SYSCALL: |
- case NACLi_SYSENTER: |
- case NACLi_LONGMODE: |
- case NACLi_SVM: |
- case NACLi_OPINMRM: |
- case NACLi_3BYTE: |
- case NACLi_CMPXCHG16B: { |
- NCBadInstructionError(dinst, "Illegal instruction"); |
- NCStatsIllegalInst(vstate); |
- break; |
- } |
- case NACLi_UNDEFINED: { |
- NCBadInstructionError(dinst, "Undefined instruction"); |
- NCStatsIllegalInst(vstate); |
- NCStatsInternalError(vstate); |
- break; |
- } |
- default: |
- NCBadInstructionError(dinst, "Undefined instruction type"); |
- NCStatsInternalError(vstate); |
- break; |
- } |
- if (squashme) { |
- if (vstate->readonly_text) { |
- NCBadInstructionError(dinst, |
- "Illegal instruction for fixed-feature CPU mode"); |
- NCStatsIllegalInst(vstate); |
- } else { |
- NCStubOutMem(vstate, dinst->dstate->memory.mpc, |
- dinst->dstate->memory.read_length); |
- } |
- } |
- return TRUE; |
-} |
- |
-Bool UnsafePartialValidateInst(const NCDecoderInst *dinst) { |
- NCValidatorState *vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); |
- Bool result = FALSE; |
- |
- NACL_FLAG_unsafe_single_inst32_mode = TRUE; |
- NCStatsInit(vstate); |
- if (ValidateInst(dinst)) { |
- result = vstate->stats.sawfailure == 0; |
- }; |
- NACL_FLAG_unsafe_single_inst32_mode = FALSE; |
- return result; |
-} |
- |
-/* |
- * Validate that two nacljmps are byte-for-byte identical. Note that |
- * one of the individual jumps must be validated in isolation with |
- * ValidateIndirect5() before this is called. |
- */ |
-static void ValidateIndirect5Replacement(NCDecoderInst *dinst_old, |
- NCDecoderInst *dinst_new) { |
- do { |
- /* check that the and-guard is 3 bytes and bit-for-bit identical */ |
- NCDecoderInst *andinst_old = PreviousInst(dinst_old, 1); |
- NCDecoderInst *andinst_new = PreviousInst(dinst_new, 1); |
- if ((andinst_old == NULL) || (andinst_old->inst.bytes.length != 3)) break; |
- if ((andinst_new == NULL) || (andinst_new->inst.bytes.length != 3)) break; |
- if (memcmp(andinst_old->inst.bytes.byte, |
- andinst_new->inst.bytes.byte, 3) != 0) break; |
- |
- /* check that the indirect-jmp is 2 bytes and bit-for-bit identical */ |
- if (dinst_old->inst.bytes.length != 2) break; |
- if (dinst_new->inst.bytes.length != 2) break; |
- if (memcmp(dinst_old->inst.bytes.byte, |
- dinst_new->inst.bytes.byte, 2) != 0) break; |
- |
- return; |
- } while (0); |
- NCBadInstructionError(dinst_new, |
- "Replacement indirect jump must match original"); |
- NCStatsUnsafeIndirect(NCVALIDATOR_STATE_DOWNCAST(dinst_new->dstate)); |
-} |
- |
-/* |
- * Check that mstate_new is a valid replacement instruction for mstate_old. |
- * Note that mstate_old was validated when it was inserted originally. |
- */ |
-static Bool ValidateInstReplacement(NCDecoderStatePair* tthis, |
- NCDecoderInst *dinst_old, |
- NCDecoderInst *dinst_new) { |
- dinst_new->unchanged = memcmp(dinst_old->inst.bytes.byte, |
- dinst_new->inst.bytes.byte, |
- dinst_new->inst.bytes.length) == 0; |
- ValidateInst(dinst_new); |
- |
- if (dinst_old->opinfo->insttype == NACLi_INDIRECT |
- || dinst_new->opinfo->insttype == NACLi_INDIRECT) { |
- /* Verify that nacljmps never change */ |
- ValidateIndirect5Replacement(dinst_old, dinst_new); |
- } |
- return TRUE; |
-} |
- |
-/* Create the decoder state for the validator state, using the |
- * given parameters. |
- */ |
-static void NCValidateDStateInit(NCValidatorState *vstate, |
- uint8_t *mbase, NaClPcAddress vbase, |
- NaClMemorySize sz) { |
- NCDecoderState* dstate = &vstate->dstate; |
- /* Note: Based on the current API, we must grab the error reporter |
- * and reinstall it, after the call to NCValidateDStateInit, so that |
- * we don't replace it with the default error reporter. |
- */ |
- NaClErrorReporter* reporter = dstate->error_reporter; |
- NCDecoderStateConstruct(dstate, mbase, vbase, sz, |
- vstate->inst_buffer, kNCValidatorInstBufferSize); |
- dstate->action_fn = ValidateInst; |
- dstate->new_segment_fn = (NCDecoderStateMethod) NCStatsNewSegment; |
- dstate->segmentation_error_fn = (NCDecoderStateMethod) NCStatsSegFault; |
- dstate->internal_error_fn = (NCDecoderStateMethod) NCStatsInternalError; |
- NCDecoderStateSetErrorReporter(dstate, reporter); |
-} |
- |
-void NCValidateSegment(uint8_t *mbase, NaClPcAddress vbase, NaClMemorySize sz, |
- struct NCValidatorState *vstate) { |
- /* Sanity checks */ |
- /* TODO(ncbray): remove redundant vbase/size args. */ |
- if ((vbase & vstate->bundle_mask) != 0) { |
- ValidatePrintOffsetError(0, "Bad vbase alignment", vstate); |
- NCStatsSegFault(vstate); |
- return; |
- } |
- if (vbase != vstate->iadrbase) { |
- ValidatePrintOffsetError(0, "Mismatched vbase addresses", vstate); |
- NCStatsSegFault(vstate); |
- return; |
- } |
- if (sz != vstate->codesize) { |
- ValidatePrintOffsetError(0, "Mismatched code size", vstate); |
- NCStatsSegFault(vstate); |
- return; |
- } |
- |
- sz = NCHaltTrimSize(mbase, sz, vstate->bundle_size); |
- vstate->codesize = sz; |
- |
- if (sz == 0) { |
- ValidatePrintOffsetError(0, "Bad text segment (zero size)", vstate); |
- NCStatsSegFault(vstate); |
- return; |
- } |
- NCValidateDStateInit(vstate, mbase, vbase, sz); |
- NCDecoderStateDecode(&vstate->dstate); |
- NCDecoderStateDestruct(&vstate->dstate); |
-} |
- |
-int NCValidateSegmentPair(uint8_t *mbase_old, uint8_t *mbase_new, |
- NaClPcAddress vbase, size_t sz, |
- const NaClCPUFeaturesX86 *features) { |
- /* TODO(karl): Refactor to use inheritance from NCDecoderStatePair? */ |
- NCDecoderStatePair pair; |
- NCValidatorState* new_vstate; |
- NCValidatorState* old_vstate; |
- |
- int result = 0; |
- |
- /* Verify that we actually have a segment to walk. */ |
- if (sz == 0) { |
- printf("VALIDATOR: %"NACL_PRIxNaClPcAddress |
- ": Bad text segment (zero size)\n", vbase); |
- return 0; |
- } |
- |
- old_vstate = NCValidateInit(vbase, sz, FALSE, features); |
- if (old_vstate != NULL) { |
- NCValidateDStateInit(old_vstate, mbase_old, vbase, sz); |
- new_vstate = NCValidateInit(vbase, sz, FALSE, features); |
- if (new_vstate != NULL) { |
- NCValidateDStateInit(new_vstate, mbase_new, vbase, sz); |
- |
- NCDecoderStatePairConstruct(&pair, |
- &old_vstate->dstate, |
- &new_vstate->dstate, |
- NULL); /* copy_func */ |
- pair.action_fn = ValidateInstReplacement; |
- if (NCDecoderStatePairDecode(&pair)) { |
- result = 1; |
- } else { |
- ValidatePrintOffsetError(0, "Replacement not applied!\n", new_vstate); |
- } |
- if (NCValidateFinish(new_vstate)) { |
- /* Errors occurred during validation. */ |
- result = 0; |
- } |
- NCDecoderStatePairDestruct(&pair); |
- NCDecoderStateDestruct(&new_vstate->dstate); |
- NCValidateFreeState(&new_vstate); |
- } |
- NCDecoderStateDestruct(&old_vstate->dstate); |
- NCValidateFreeState(&old_vstate); |
- } |
- return result; |
-} |
- |
-/* Walk the collected information on instruction boundaries and jump targets, |
- * and verify that they are legal. |
- */ |
-static void NCJumpSummarize(struct NCValidatorState* vstate) { |
- uint32_t offset; |
- |
- /* Verify that jumps are to the beginning of instructions, and that the |
- * jumped to instruction is not in the middle of a native client pattern. |
- */ |
- dprint(("CheckTargets: %"NACL_PRIxNaClPcAddress"-%"NACL_PRIxNaClPcAddress"\n", |
- vstate->iadrbase, vstate->iadrbase+vstate->codesize)); |
- for (offset = 0; offset < vstate->codesize; offset += 1) { |
- if (NCGetAdrTable(offset, vstate->kttable)) { |
- NCStatsCheckTarget(vstate); |
- if (!NCGetAdrTable(offset, vstate->vttable)) { |
- ValidatePrintOffsetError(offset, "Bad jump target", vstate); |
- NCStatsBadTarget(vstate); |
- } |
- } |
- } |
- |
- /* check basic block boundaries */ |
- for (offset = 0; offset < vstate->codesize; offset += vstate->bundle_size) { |
- if (!NCGetAdrTable(offset, vstate->vttable)) { |
- ValidatePrintOffsetError(offset, "Bad basic block alignment", vstate); |
- NCStatsBadAlignment(vstate); |
- } |
- } |
-} |