Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(305)

Unified Diff: src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.c

Issue 625923004: Delete old x86 validator. (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: rebase master Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
- }
- }
-}
« no previous file with comments | « src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.h ('k') | src/trusted/validator/x86/ncval_seg_sfi/ncvalidate_detailed.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698