Chromium Code Reviews| 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 |
| index af07366eb4e26c2ea8210a03a4f8fb84b7005a06..3f85a2fbd4d584a8b04cb5c2f4678a8b8e62c447 100644 |
| --- a/src/trusted/validator/x86/testing/enuminsts/vdiff.c |
| +++ b/src/trusted/validator/x86/testing/enuminsts/vdiff.c |
| @@ -16,6 +16,9 @@ |
| #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" |
| @@ -24,6 +27,7 @@ |
| #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" |
| @@ -49,6 +53,10 @@ static Bool gSilent = FALSE; |
| */ |
| static Bool gSkipRepeatReports = FALSE; |
| +/* Set to true to enable checking of mnemonics (opcode names). |
| + */ |
| +static Bool gCheckMnemonics = TRUE; |
| + |
| /* Count of errors that have a high certainty of being exploitable. */ |
| static int gSawLethalError = 0; |
| @@ -61,12 +69,18 @@ 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 char *gArgv0 = "argv0"; |
| +static const char *gArgv0 = "argv0"; |
| +#define FLAG_EasyDiff "--easydiff" |
| /* Records that unexpected internal error occurred. */ |
| void InternalError(const char *why) { |
| @@ -145,7 +159,6 @@ static void CheckMnemonics(NaClEnumerator* pinst, NaClEnumerator* dinst) { |
| /* avoid redundant messages... */ |
| if (NotOpcodeRepeat(prod_opcode)) { |
| printf("Warning: OPCODE MISMATCH: %s != %s\n", prod_opcode, dfa_opcode); |
| - /* PrintDisassembledInstructionVariants(pinst, dinst); */ |
| } |
| } |
| } |
| @@ -174,8 +187,10 @@ static void IncrErrors() { |
| } |
| static void PrintStats() { |
| - printf("valid: %" NACL_PRIu64 "\n", gVDiffStats.valid); |
| - printf("invalid: %" NACL_PRIu64 "\n", gVDiffStats.invalid); |
| + 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(" =? %" NACL_PRIu64 " valid + invalid + errors\n", |
| @@ -220,7 +235,7 @@ static void TryOneInstruction(uint8_t *itext, size_t nbytes) { |
| /* and they agree on critical details. */ |
| IncrValid(); |
| /* Warn if decoders disagree opcode name. */ |
| - CheckMnemonics(&pinst, &dinst); |
| + if (gCheckMnemonics) CheckMnemonics(&pinst, &dinst); |
| } else { |
| DecoderError("LENGTH MISMATCH", &pinst, &dinst, ""); |
| IncrErrors(); |
| @@ -239,19 +254,40 @@ static void TryOneInstruction(uint8_t *itext, size_t nbytes) { |
| IncrInvalid(); |
| } |
| - /* no error */ |
| 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. This is used to create output that is easy to diff when |
| + * a random REX byte is used. |
| + */ |
| +static char* StrPrefix(const unsigned int prefix, char* ps, char* str) { |
|
Karl
2012/08/28 21:23:52
It is not clear to me what the arguments here are.
Brad Chen
2012/08/28 22:09:42
Done.
|
| + 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(unsigned int prefix, size_t prefix_length) { |
| - const int kInstByteCount = NACL_ENUM_MAX_INSTRUCTION_BYTES; |
| - const int kIterByteCount = 3; |
| +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; |
| @@ -260,7 +296,7 @@ static void TestAllWithPrefix(unsigned int prefix, size_t prefix_length) { |
| if ((gPrefix > 0) && (gPrefix != prefix)) return; |
| - PrintProgress("TestAllWithPrefix(%x)\n", prefix); |
| + PrintProgress("TestAllWithPrefix(%s)\n", print_prefix); |
| /* set up prefix */ |
| memcpy(itext, &prefix, prefix_length); |
| /* set up filler bytes */ |
| @@ -288,21 +324,59 @@ static void TestAllWithPrefix(unsigned int prefix, size_t prefix_length) { |
| } |
| } |
| +#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 */ |
|
Karl
2012/08/28 21:23:52
Add a blank line here?
Brad Chen
2012/08/28 22:09:42
Done.
|
| +/* Generate a random REX prefix, to use for the entire run. */ |
| +static uint32_t RandomRexPrefix() { |
| + 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 |
| + |
| /* For x86-64, enhance the iteration by looping through REX prefixes. |
| */ |
| -static void TestAllWithPrefixREX(unsigned int prefix, size_t prefix_length) { |
| +static void WithREX(TestAllFunction testall, |
| + const unsigned int prefix, |
| + const size_t prefix_length) { |
| + char pstr[kBufferSize]; |
| #if NACL_TARGET_SUBARCH == 64 |
| - unsigned char REXp; |
| + unsigned char irex; |
| unsigned int rprefix; |
| /* test with REX prefixes */ |
| - for (REXp = 0x40; REXp < 0x50; REXp++) { |
| - rprefix = (prefix << 8 | REXp); |
| - printf("Testing with prefix %x\n", rprefix); |
| - TestAllWithPrefix(rprefix, prefix_length + 1); |
| + printf("WithREX(testall, %x, %d, %d)\n", prefix, |
| + (int)prefix_length, gEasyDiffMode); |
| + if (gEasyDiffMode) { |
| + printf("With random REX prefix.\n"); |
| + irex = RandomRexPrefix(); |
| + rprefix = (prefix << 8 | irex); |
|
Karl
2012/08/28 21:23:52
Could this operation be converted to a MACRO. It a
Brad Chen
2012/08/28 22:09:42
Done.
|
| + testall(rprefix, prefix_length + 1, StrPrefix(prefix, "XX", pstr)); |
| + } else { |
| + for (irex = kREXBase; irex < kREXMax; irex++) { |
| + rprefix = (prefix << 8 | irex); |
| + printf("With REX prefix %x\n", rprefix); |
| + testall(rprefix, prefix_length + 1, StrPrefix(rprefix, "", pstr)); |
| + } |
| } |
| #endif |
| /* test with no REX prefix */ |
| - TestAllWithPrefix(prefix, prefix_length); |
| + testall(prefix, prefix_length, StrPrefix(prefix, NULL, pstr)); |
| } |
| /* For all prefixes, call TestAllWithPrefix() to enumrate and test |
| @@ -314,24 +388,22 @@ static void TestAllInstructions() { |
| * an integer. For example, for integer prefix 0x3a0f, 0f will |
| * go in instruction byte 0, and 3a in byte 1. |
| */ |
| - /* TODO(bradchen): extend enuminsts-64 to iterate over 64-bit prefixes. */ |
| - TestAllWithPrefixREX(0, 0); |
| - TestAllWithPrefixREX(0x0f, 1); |
| - TestAllWithPrefixREX(0x0ff2, 2); |
| - TestAllWithPrefixREX(0x0ff3, 2); |
| - TestAllWithPrefixREX(0x0f66, 2); |
| - TestAllWithPrefixREX(0x0f0f, 2); |
| - TestAllWithPrefixREX(0x380f, 2); |
| - TestAllWithPrefixREX(0x3a0f, 2); |
| - TestAllWithPrefixREX(0x380f66, 3); |
| - TestAllWithPrefixREX(0x380ff2, 3); |
| - TestAllWithPrefixREX(0x3a0f66, 3); |
| + 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+ */ |
| } |
| /* Used to test one instruction at a time, for example, in regression |
| * testing, or for instruction arguments from the command line. |
| */ |
| -static void TestOneInstruction(char *asciihex) { |
| +static void TestOneInstruction(const char *asciihex) { |
| InstByteArray ibytes; |
| int nbytes; |
| @@ -380,7 +452,7 @@ static void RunRegressionTests() { |
| TestOneInstruction("664001d8"); /* legal; REX after data16 */ |
| TestOneInstruction("414001d8"); /* illegal; two REX bytes */ |
| - /* Reset the opcode repeat test, so as not to silence erros */ |
| + /* Reset the opcode repeat test, so as not to silence errors */ |
| /* that happened in the regression suite. */ |
| (void)NotOpcodeRepeat(""); |
| } |
| @@ -390,28 +462,43 @@ extern NaClEnumeratorDecoder* RegisterNaClDecoder(); |
| extern NaClEnumeratorDecoder* RegisterRagelDecoder(); |
| /* Initialize the set of available decoders. */ |
| -static void NaClInitializeAvailableDecoders() { |
| +static void VDiffInitializeAvailableDecoders() { |
| vProd = RegisterNaClDecoder(); |
| vDFA = RegisterRagelDecoder(); |
| } |
| -int main(int argc, char *argv[]) { |
| - int testargs; |
| +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; |
| + gCheckMnemonics = FALSE; |
| + nextarg += 1; |
| + } |
| + return nextarg; |
| +} |
| + |
| +int main(const int argc, const char *argv[]) { |
| + int nextarg; |
| NaClLogModuleInit(); |
| NaClLogSetVerbosity(LOG_FATAL); |
| - NaClInitializeAvailableDecoders(); |
| - |
| - gArgv0 = argv[0]; |
| - testargs = 1; |
| +#if NACL_LINUX || NACL_OSX |
| + srandom(time(NULL)); |
| +#endif |
| + VDiffInitializeAvailableDecoders(); |
| - if (testargs == argc) { |
| + nextarg = ParseArgv(argc, argv); |
| + if (nextarg == argc) { |
| if (gPrefix == 0) RunRegressionTests(); |
| TestAllInstructions(); |
| } else { |
| int i; |
| gVerbose = TRUE; |
| - for (i = testargs; i < argc; ++i) { |
| + for (i = nextarg; i < argc; ++i) { |
| TestOneInstruction(argv[i]); |
| } |
| } |