Index: src/trusted/validator/x86/testing/enuminsts/vdiff.c |
diff --git a/src/trusted/validator/x86/testing/enuminsts/vdiff.c b/src/trusted/validator/x86/testing/enuminsts/vdiff.c |
deleted file mode 100644 |
index fd905adff75f5549da85617882ebfec1a5838907..0000000000000000000000000000000000000000 |
--- a/src/trusted/validator/x86/testing/enuminsts/vdiff.c |
+++ /dev/null |
@@ -1,557 +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. |
- */ |
- |
-/* vdiff.c |
- * exhaustive instruction enumeration test for x86 Native Client validators. |
- * |
- * This file is based on enuminsts.c, but specialized to comparing two |
- * validators instead of decoders. The enuminsts.c implementation also |
- * had a bunch of Xed-specific logic which complicated the validator |
- * comparison in unhelpful ways. |
- */ |
- |
-#ifndef NACL_TRUSTED_BUT_NOT_TCB |
-#error("This file is not meant for use in the TCB.") |
-#endif |
-#if NACL_WINDOWS |
-#define _CRT_RAND_S /* enable decl of rand_s() */ |
-#endif |
- |
-#include "native_client/src/trusted/validator/x86/testing/enuminsts/enuminsts.h" |
- |
-#include <ctype.h> |
-#include <stdio.h> |
-#include <string.h> |
-#include <stdlib.h> |
-#include <stdarg.h> |
-#include <time.h> |
- |
-#include "native_client/src/include/portability_io.h" |
-#include "native_client/src/shared/platform/nacl_log.h" |
-#include "native_client/src/shared/utils/flags.h" |
-#include "native_client/src/trusted/validator/x86/testing/enuminsts/str_utils.h" |
-#include "native_client/src/trusted/validator/x86/testing/enuminsts/text2hex.h" |
- |
-/* Defines the maximum buffer size used to hold text generated for the |
- * disassembly of instructions, and corresponding error messages. |
- */ |
-#define kBufferSize 1024 |
- |
-/* When true, print more messages (i.e. verbosely). */ |
-static Bool gVerbose = FALSE; |
- |
-/* When true, don't print out messages. That is, only print instructions |
- * defined by --print directives. |
- */ |
-static Bool gSilent = FALSE; |
- |
-/* Count of errors that have a high certainty of being exploitable. */ |
-static int gSawLethalError = 0; |
- |
-/* Defines the assumed text address for the test instruction */ |
-const int kTextAddress = 0x1000; |
- |
-/* If non-negative, defines the prefix to test. */ |
-static unsigned int gPrefix = 0; |
- |
-/* If non-negative, defines the opcode to test. */ |
-static int gOpcode = -1; |
- |
-/* This option triggers a set of behaviors that help produce repeatable |
- * output, for easier diffs on the buildbots. |
- */ |
-static Bool gEasyDiffMode; |
- |
-/* The production and new R-DFA validators */ |
-NaClEnumeratorDecoder* vProd; |
-NaClEnumeratorDecoder* vDFA; |
- |
-/* The name of the executable (i.e. argv[0] from the command line). */ |
-static const char *gArgv0 = "argv0"; |
-#define FLAG_EasyDiff "--easydiff" |
- |
-/* Records that unexpected internal error occurred. */ |
-void InternalError(const char *why) { |
- fprintf(stderr, "%s: Internal Error: %s\n", gArgv0, why); |
- gSawLethalError = 1; |
-} |
- |
-/* Records that a fatal (i.e. non-recoverable) error occurred. */ |
-void ReportFatalError(const char* why) { |
- char buffer[kBufferSize]; |
- SNPRINTF(buffer, kBufferSize, "%s - quitting!", why); |
- InternalError(buffer); |
- exit(1); |
-} |
- |
-/* Prints out the instruction each decoder disassembled */ |
-static void PrintDisassembledInstructionVariants(NaClEnumerator *pinst, |
- NaClEnumerator *dinst) { |
- vProd->_print_inst_fn(pinst); |
- vDFA->_print_inst_fn(dinst); |
-} |
- |
-/* Prints out progress messages. */ |
-static void PrintVProgress(const char* format, va_list ap) { |
- if (gSilent) { |
- /* Generating opcode sequences, so add special prefix so that we |
- * can print these out when read by the corresponding input decoder. |
- */ |
- printf("#PROGRESS#"); |
- } |
- vprintf(format, ap); |
-} |
- |
-static void PrintProgress(const char* format, ...) ATTRIBUTE_FORMAT_PRINTF(1,2); |
- |
-/* Prints out progress messages. */ |
-static void PrintProgress(const char* format, ...) { |
- va_list ap; |
- va_start(ap, format); |
- PrintVProgress(format, ap); |
- va_end(ap); |
-} |
- |
-/* Report a disagreement between decoders. |
- */ |
-static void DecoderError(const char *why, |
- NaClEnumerator *pinst, |
- NaClEnumerator *dinst, |
- const char *details) { |
- /* If reached, did not skip, so report the error. */ |
- printf("**** ERROR: %s: %s\n", why, (details == NULL ? "" : details)); |
- PrintDisassembledInstructionVariants(pinst, dinst); |
-} |
- |
-static void PrintBytes(FILE *f, uint8_t* bytes, size_t len) { |
- size_t i; |
- for (i = 0; i < len; i++) { |
- fprintf(f, "%02x", bytes[i]); |
- } |
-} |
- |
-struct vdiff_stats { |
- int64_t tried; |
- int64_t valid; |
- int64_t invalid; |
- int64_t errors; |
- int64_t ignored; |
-} gVDiffStats = {0, 0, 0, 0, 0}; |
- |
-static void IncrTried(void) { |
- gVDiffStats.tried += 1; |
-} |
- |
-static void IncrValid(void) { |
- gVDiffStats.valid += 1; |
-} |
- |
-static void IncrInvalid(void) { |
- gVDiffStats.invalid += 1; |
-} |
- |
-static void IncrErrors(void) { |
- gVDiffStats.errors += 1; |
-} |
- |
-static void IncrIgnored(void) { |
- gVDiffStats.ignored += 1; |
-} |
- |
-static void PrintStats(void) { |
- printf("Stats:\n"); |
- if (!gEasyDiffMode) { |
- printf("valid: %" NACL_PRIu64 "\n", gVDiffStats.valid); |
- printf("invalid: %" NACL_PRIu64 "\n", gVDiffStats.invalid); |
- } |
- printf("errors: %" NACL_PRIu64 "\n", gVDiffStats.errors); |
- printf("tried: %" NACL_PRIu64 "\n", gVDiffStats.tried); |
- printf("ignored: %" NACL_PRIu64 "\n", gVDiffStats.ignored); |
- printf(" =? %" NACL_PRIu64 " valid + invalid + errors + ignored\n", |
- gVDiffStats.valid + gVDiffStats.invalid + gVDiffStats.errors + |
- gVDiffStats.ignored); |
-} |
- |
-static void InitInst(NaClEnumerator *nacle, |
- uint8_t *itext, size_t nbytes) |
-{ |
- memcpy(nacle->_itext, itext, nbytes); |
- nacle->_num_bytes = nbytes; |
-} |
- |
-/* Print out decodings if specified on the command line. */ |
-/* Test comparison for a single instruction. */ |
-static void TryOneInstruction(uint8_t *itext, size_t nbytes) { |
- NaClEnumerator pinst; /* for prod validator */ |
- NaClEnumerator dinst; /* for dfa validator */ |
- Bool prod_okay, rdfa_okay; |
- |
- IncrTried(); |
- do { |
- if (gVerbose) { |
- printf("================"); |
- PrintBytes(stdout, itext, nbytes); |
- printf("\n"); |
- } |
- |
- /* Try to parse the sequence of test bytes. */ |
- InitInst(&pinst, itext, nbytes); |
- InitInst(&dinst, itext, nbytes); |
- vProd->_parse_inst_fn(&pinst, kTextAddress); |
- vDFA->_parse_inst_fn(&dinst, kTextAddress); |
- prod_okay = vProd->_maybe_inst_validates_fn(&pinst); |
- rdfa_okay = vDFA->_maybe_inst_validates_fn(&dinst); |
- |
- if (prod_okay && rdfa_okay) { |
- if (vProd->_inst_length_fn(&pinst) == |
- vDFA->_inst_length_fn(&dinst)) { |
- /* Both validators see a legal instruction, */ |
- /* and they agree on critical details. */ |
- IncrValid(); |
- } else { |
- DecoderError("LENGTH MISMATCH", &pinst, &dinst, ""); |
- IncrErrors(); |
- } |
- } else if (prod_okay && !rdfa_okay) { |
- /* |
- * 32bit production validator by design is unable to distingush a lot of |
- * instructions (the ones which work only with memory or only with |
- * registers). To avoid commiting multimegabyte golden file don't count |
- * these differences as substantial. It's not a security problem if we |
- * reject some valid x86 instructions and if we'll lose something |
- * important hopefully developers will remind us. |
- */ |
- if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && |
- NACL_TARGET_SUBARCH == 32) { |
- IncrIgnored(); |
- } else { |
- /* Validators disagree on instruction legality */ |
- DecoderError("VALIDATORS DISAGREE (prod accepts, RDFA rejects)", |
- &pinst, |
- &dinst, |
- ""); |
- IncrErrors(); |
- } |
- } else if (!prod_okay && rdfa_okay) { |
- /* Validators disagree on instruction legality */ |
- DecoderError("VALIDATORS DISAGREE (prod rejects, RDFA accepts)", |
- &pinst, |
- &dinst, |
- ""); |
- IncrErrors(); |
- } else { |
- /* Both validators see an illegal instruction */ |
- IncrInvalid(); |
- } |
- |
- if (gVerbose) { |
- PrintDisassembledInstructionVariants(&pinst, &dinst); |
- } |
- } while (0); |
-} |
- |
-/* A function type for instruction "TestAll" functions. |
- * Parameters: |
- * prefix: up to four bytes of prefix. |
- * prefix_length: size_t on [0..4] specifying length of prefix. |
- * print_prefix: For easy diff of test output, avoid printing |
- * the value of a randomly selected REX prefix. |
- */ |
-typedef void (*TestAllFunction)(const unsigned int prefix, |
- const size_t prefix_length, |
- const char* print_prefix); |
- |
-/* Create a char* rendition of a prefix string, appending bytes |
- * in ps. When using a randomly generated REX prefix on the bots, |
- * it's useful to avoid printing the actual REX prefix so that |
- * output can be diffed from run-to-run. For example, instead of |
- * printing "0F45" you might print "0FXX". Parameters: |
- * prefix: The part of the prefix value to print |
- * ps: 'postscript', string to append to prefix value |
- * str: where to put the ASCII version of the prefix |
- */ |
-static char* StrPrefix(const unsigned int prefix, char* ps, char* str) { |
- sprintf(str, "%x%s", prefix, (ps == NULL) ? "" : ps); |
- return str; |
-} |
- |
-/* Enumerate and test all 24-bit opcode+modrm+sib patterns for a |
- * particular prefix. |
- */ |
-static void TestAllWithPrefix(const unsigned int prefix, |
- const size_t prefix_length, |
- const char* print_prefix) { |
- const size_t kInstByteCount = NACL_ENUM_MAX_INSTRUCTION_BYTES; |
- const size_t kIterByteCount = 3; |
- InstByteArray itext; |
- size_t i; |
- int op, modrm, sib; |
- int min_op; |
- int max_op; |
- |
- if ((gPrefix > 0) && (gPrefix != prefix)) return; |
- |
- PrintProgress("TestAllWithPrefix(%s)\n", print_prefix); |
- /* set up prefix */ |
- memcpy(itext, &prefix, prefix_length); |
- /* set up filler bytes */ |
- for (i = prefix_length + kIterByteCount; i < kInstByteCount; i++) { |
- itext[i] = (uint8_t)i; |
- } |
- if (gOpcode < 0) { |
- min_op = 0; |
- max_op = 256; |
- } else { |
- min_op = gOpcode; |
- max_op = gOpcode + 1; |
- } |
- for (op = min_op; op < max_op; op++) { |
- itext[prefix_length] = op; |
- if (!gEasyDiffMode) PrintProgress("%02x 00 00\n", op); |
- for (modrm = 0; modrm < 256; modrm++) { |
- itext[prefix_length + 1] = modrm; |
- for (sib = 0; sib < 256; sib++) { |
- itext[prefix_length + 2] = sib; |
- TryOneInstruction(itext, kInstByteCount); |
- } |
- } |
- } |
-} |
- |
-/* For 3DNow!, the operand byte goes at the end. Format is: |
- * 0F 0F [ModRM] [SIB] [displacement] imm8_opcode |
- * See AMD doc 24594, page 435. |
- */ |
-static void TestAll3DNow(const unsigned int prefix, |
- const size_t prefix_length, |
- const char* print_prefix) { |
- const size_t kInstByteCount = NACL_ENUM_MAX_INSTRUCTION_BYTES; |
- const size_t kIterByteCount = 3; |
- InstByteArray itext; |
- size_t i; |
- int op, modrm, sib; |
- |
- if ((gPrefix > 0) && (gPrefix != prefix)) return; |
- |
- PrintProgress("TestAll3DNow(%s)\n", print_prefix); |
- /* set up prefix */ |
- memcpy(itext, &prefix, prefix_length); |
- /* set up filler bytes */ |
- for (i = prefix_length + kIterByteCount; i < kInstByteCount; i++) { |
- itext[i] = (uint8_t)i; |
- } |
- |
- for (op = 0; op < 256; op++) { |
- if (!gEasyDiffMode) PrintProgress("%02x 00 00\n", op); |
- /* Use opcode as fill byte, forcing iteration through 3DNow opcodes. */ |
- for (i = prefix_length + 2; i < kIterByteCount; i++) itext[i] = op; |
- |
- for (modrm = 0; modrm < 256; modrm++) { |
- itext[prefix_length] = modrm; |
- for (sib = 0; sib < 256; sib++) { |
- itext[prefix_length + 1] = sib; |
- TryOneInstruction(itext, kInstByteCount); |
- } |
- } |
- } |
-} |
- |
-#if NACL_TARGET_SUBARCH == 64 |
-/* REX prefixes range from 0x40 to 0x4f. */ |
-const uint32_t kREXBase = 0x40; |
-const uint32_t kREXRange = 0x10; |
-const uint32_t kREXMax = 0x50; /* kREXBase + kREXRange */ |
- |
-/* Generate a random REX prefix, to use for the entire run. */ |
-static uint32_t RandomRexPrefix(void) { |
- static uint32_t static_rex_prefix = 0; |
- |
- if (0 == static_rex_prefix) { |
-#if NACL_LINUX || NACL_OSX |
- static_rex_prefix = kREXBase + (random() % kREXRange); |
-#elif NACL_WINDOWS |
- if (rand_s(&static_rex_prefix) != 0) { |
- ReportFatalError("rand_s() failed\n"); |
- } else { |
- static_rex_prefix = kREXBase + (static_rex_prefix % kREXRange); |
- } |
-#else |
-# error "Unknown operating system." |
-#endif |
- } |
- return static_rex_prefix; |
-} |
-#endif |
- |
-#define AppendPrefixByte(oldprefix, pbyte) (((oldprefix) << 8) | (pbyte)) |
-/* For x86-64, enhance the iteration by looping through REX prefixes. |
- */ |
-static void WithREX(TestAllFunction testall, |
- const unsigned int prefix, |
- const size_t prefix_length) { |
- char pstr[kBufferSize]; |
-#if NACL_TARGET_SUBARCH == 64 |
- unsigned char irex; |
- unsigned int rprefix; |
- /* test with REX prefixes */ |
- printf("WithREX(testall, %x, %d, %d)\n", prefix, |
- (int)prefix_length, gEasyDiffMode); |
- if (gEasyDiffMode) { |
- printf("With random REX prefix.\n"); |
- irex = RandomRexPrefix(); |
- rprefix = AppendPrefixByte(prefix, irex); |
- testall(rprefix, prefix_length + 1, StrPrefix(prefix, "XX", pstr)); |
- } else { |
- for (irex = kREXBase; irex < kREXMax; irex++) { |
- rprefix = AppendPrefixByte(prefix, irex); |
- printf("With REX prefix %x\n", rprefix); |
- testall(rprefix, prefix_length + 1, StrPrefix(rprefix, "", pstr)); |
- } |
- } |
-#endif |
- /* test with no REX prefix */ |
- testall(prefix, prefix_length, StrPrefix(prefix, NULL, pstr)); |
-} |
-#undef AppendPrefixByte |
- |
-/* For all prefixes, call TestAllWithPrefix() to enumrate and test |
- * all instructions. |
- */ |
-static void TestAllInstructions(void) { |
- /* NOTE: Prefix byte order needs to be reversed when written as |
- * an integer. For example, for integer prefix 0x3a0f, 0f will |
- * go in instruction byte 0, and 3a in byte 1. |
- */ |
- WithREX(TestAllWithPrefix, 0, 0); |
- WithREX(TestAllWithPrefix, 0x0f, 1); /* two-byte opcode */ |
- WithREX(TestAllWithPrefix, 0x0ff2, 2); /* SSE2 */ |
- WithREX(TestAllWithPrefix, 0x0ff3, 2); /* SSE */ |
- WithREX(TestAllWithPrefix, 0x0f66, 2); /* SSE2 */ |
- WithREX(TestAllWithPrefix, 0x380f, 2); /* SSSE3 */ |
- WithREX(TestAllWithPrefix, 0x3a0f, 2); /* SSE4 */ |
- WithREX(TestAllWithPrefix, 0x380f66, 3); /* SSE4+ */ |
- WithREX(TestAllWithPrefix, 0x380ff2, 3); /* SSE4+ */ |
- WithREX(TestAllWithPrefix, 0x3a0f66, 3); /* SSE4+ */ |
- WithREX(TestAllWithPrefix, 0x0ff366, 3); /* SSE4+ */ |
- WithREX(TestAll3DNow, 0x0f0f, 2); |
-} |
- |
-/* Used to test one instruction at a time, for example, in regression |
- * testing, or for instruction arguments from the command line. |
- */ |
-static void TestOneInstruction(const char *asciihex) { |
- InstByteArray ibytes; |
- int nbytes; |
- |
- nbytes = Text2Bytes(ibytes, asciihex, "Command-line argument", -1); |
- if (nbytes == 0) return; |
- if (gVerbose) { |
- int i; |
- printf("trying %s (", asciihex); |
- for (i = 0; i < nbytes; ++i) { |
- printf("%02x", ibytes[i]); |
- } |
- printf(")\n"); |
- } |
- TryOneInstruction(ibytes, (size_t) nbytes); |
-} |
- |
-/* A set of test cases that have caused problems in the past. |
- * This is a bit stale; most of the test cases came from xed_compare.py. |
- * Mostly this program has been tested by TestAllInstructions(), |
- * possible since this program is much faster than xed_compare.py |
- */ |
-static void RunRegressionTests(void) { |
- TestOneInstruction("0024c2"); |
- TestOneInstruction("017967"); |
- TestOneInstruction("0f12c0"); |
- TestOneInstruction("0f13c0"); |
- TestOneInstruction("0f17c0"); |
- TestOneInstruction("0f01c1"); |
- TestOneInstruction("0f00300000112233445566778899aa"); |
- TestOneInstruction("cc"); |
- TestOneInstruction("C3"); |
- TestOneInstruction("0f00290000112233445566778899aa"); |
- TestOneInstruction("80e4f7"); |
- TestOneInstruction("e9a0ffffff"); |
- TestOneInstruction("4883ec08"); |
- TestOneInstruction("0f00040500112233445566778899aa"); |
- /* Below are newly discovered mistakes in call instructions, where the wrong |
- * byte length was required by x86-64 nacl validator. |
- */ |
- TestOneInstruction("262e7e00"); |
- TestOneInstruction("2e3e7900"); |
- /* From the AMD manual, "An instruction may have only one REX prefix */ |
- /* which must immediately precede the opcode or first excape byte */ |
- /* in the instruction encoding." */ |
- TestOneInstruction("406601d8"); /* illegal; REX before data16 */ |
- TestOneInstruction("664001d8"); /* legal; REX after data16 */ |
- TestOneInstruction("414001d8"); /* illegal; two REX bytes */ |
- |
- /* And some tests for degenerate prefix patterns */ |
- TestOneInstruction("666690"); |
- TestOneInstruction("6690"); |
- TestOneInstruction("666666666666666666666690"); |
- TestOneInstruction("66454490"); |
- TestOneInstruction("66454f90"); |
- TestOneInstruction("456690"); |
-} |
- |
-/* Define decoders that can be registered. */ |
-extern NaClEnumeratorDecoder* RegisterNaClDecoder(void); |
-extern NaClEnumeratorDecoder* RegisterRagelDecoder(void); |
- |
-/* Initialize the set of available decoders. */ |
-static void VDiffInitializeAvailableDecoders(void) { |
- vProd = RegisterNaClDecoder(); |
- vDFA = RegisterRagelDecoder(); |
-} |
- |
-static int ParseArgv(const int argc, const char* argv[]) { |
- int nextarg; |
- |
- gArgv0 = argv[0]; |
- nextarg = 1; |
- if (nextarg < argc && |
- 0 == strcmp(argv[nextarg], FLAG_EasyDiff)) { |
- gEasyDiffMode = TRUE; |
- nextarg += 1; |
- } |
- return nextarg; |
-} |
- |
-static char g_standard_output_buffer[4 << 10]; |
- |
-int main(const int argc, const char *argv[]) { |
- int nextarg; |
- |
- NaClLogModuleInit(); |
- NaClLogSetVerbosity(LOG_FATAL); |
- if (0 != setvbuf(stdout, g_standard_output_buffer, _IOLBF, |
- sizeof g_standard_output_buffer)) { |
- NaClLog(LOG_FATAL, "vdiff: setvbuf failed\n"); |
- } |
-#if NACL_LINUX || NACL_OSX |
- srandom(time(NULL)); |
-#endif |
- VDiffInitializeAvailableDecoders(); |
- |
- nextarg = ParseArgv(argc, argv); |
- if (nextarg == argc) { |
- if (gPrefix == 0) RunRegressionTests(); |
- TestAllInstructions(); |
- } else { |
- int i; |
- gVerbose = TRUE; |
- for (i = nextarg; i < argc; ++i) { |
- TestOneInstruction(argv[i]); |
- } |
- } |
- PrintStats(); |
- |
- /* exit with non-zero error code if there were errors. */ |
- exit(gVDiffStats.errors != 0); |
-} |