| Index: src/trusted/validator_mips/validator_tests.cc | 
| diff --git a/src/trusted/validator_mips/validator_tests.cc b/src/trusted/validator_mips/validator_tests.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..a1dbc4f9d2522e41ae3b0348ea9d75bf544785be | 
| --- /dev/null | 
| +++ b/src/trusted/validator_mips/validator_tests.cc | 
| @@ -0,0 +1,438 @@ | 
| +/* | 
| + * Copyright 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. | 
| + */ | 
| + | 
| +/* | 
| + * Unit tests for the MIPS validator | 
| + * | 
| + * These tests use the google-test framework (gtest for short) to exercise the | 
| + * MIPS validator.  The tests currently fall into two rough categories: | 
| + *  1. Simple method-level tests that exercise the validator's primitive | 
| + *     capabilities, and | 
| + *  2. Instruction pattern tests that run the entire validator. | 
| + * | 
| + * All instruction pattern tests use hand-assembled machine code fragments, | 
| + * embedded as byte arrays.  This is somewhat ugly, but deliberate: it isolates | 
| + * these tests from gas, which may be buggy or not installed.  It also lets us | 
| + * hand-craft malicious bit patterns that gas may refuse to produce. | 
| + * | 
| + * To write a new instruction pattern, or make sense of an existing one, use | 
| + * MIPS32 Instruction Set Reference (available online). Instructions in this | 
| + * file are written as 32-bit integers so the hex encoding matches the docs. | 
| + */ | 
| + | 
| +#include <vector> | 
| +#include <string> | 
| +#include <sstream> | 
| + | 
| +#include "gtest/gtest.h" | 
| +#include "native_client/src/include/nacl_macros.h" | 
| + | 
| +#include "native_client/src/trusted/validator_mips/validator.h" | 
| + | 
| +using std::vector; | 
| +using std::string; | 
| +using std::ostringstream; | 
| + | 
| +using nacl_mips_dec::Register; | 
| +using nacl_mips_dec::RegisterList; | 
| +using nacl_mips_dec::Instruction; | 
| + | 
| +using nacl_mips_val::SfiValidator; | 
| +using nacl_mips_val::ProblemSink; | 
| +using nacl_mips_val::CodeSegment; | 
| + | 
| +namespace { | 
| + | 
| +typedef uint32_t mips_inst; | 
| + | 
| +/* | 
| + * We use these parameters to initialize the validator, below.  They are | 
| + * somewhat different from the parameters used in real NaCl systems, to test | 
| + * degrees of validator freedom that we don't currently exercise in prod. | 
| + */ | 
| +// Number of bytes in each bundle. | 
| +static const uint32_t kBytesPerBundle = 16; | 
| +// Limit code to 256MiB. | 
| +static const uint32_t kCodeRegionSize = 0x10000000; | 
| +// Limit data to 1GiB. | 
| +static const uint32_t kDataRegionSize = 0x40000000; | 
| + | 
| + | 
| +/* | 
| + * Support code | 
| + */ | 
| + | 
| +// Simply records the arguments given to report_problem, below. | 
| +struct ProblemRecord { | 
| +  uint32_t vaddr; | 
| +  nacl_mips_dec::SafetyLevel safety; | 
| +  nacl::string problem_code; | 
| +  uint32_t ref_vaddr; | 
| +}; | 
| + | 
| +// A ProblemSink that records all calls (implementation of the Spy pattern) | 
| +class ProblemSpy : public ProblemSink { | 
| + public: | 
| +  virtual void ReportProblem(uint32_t vaddr, nacl_mips_dec::SafetyLevel safety, | 
| +      const nacl::string &problem_code, uint32_t ref_vaddr = 0) { | 
| +    _problems.push_back( | 
| +        (ProblemRecord) { vaddr, safety, problem_code, ref_vaddr }); | 
| +  } | 
| + | 
| +  /* | 
| +   * We want *all* the errors that the validator produces. Note that this means | 
| +   * we're not testing the should_continue functionality. This is probably | 
| +   * okay. | 
| +   */ | 
| +  virtual bool ShouldContinue() { return true; } | 
| + | 
| +  vector<ProblemRecord> &problems() { return _problems; } | 
| + | 
| + private: | 
| +  vector<ProblemRecord> _problems; | 
| +}; | 
| + | 
| +/* | 
| + * Coordinates the fixture objects used by test cases below. This is | 
| + * forward-declared to the greatest extent possible so we can get on to the | 
| + * important test stuff below. | 
| + */ | 
| +class ValidatorTests : public ::testing::Test { | 
| + protected: | 
| +  ValidatorTests(); | 
| + | 
| +  // Utility method for validating a sequence of bytes. | 
| +  bool Validate(const mips_inst *pattern, | 
| +                size_t inst_count, | 
| +                uint32_t start_addr, | 
| +                ProblemSink *sink); | 
| + | 
| +  /* | 
| +   * Tests a pattern that's expected to pass. | 
| +   */ | 
| +  void ValidationShouldPass(const mips_inst *pattern, | 
| +                            size_t inst_count, | 
| +                            uint32_t base_addr, | 
| +                            const string &msg); | 
| + | 
| +  /* | 
| +   * Tests a pattern that is forbidden in the SFI model. | 
| +   * | 
| +   * Note that the 'msg1' and 'msg2' parameters are merely concatentated in the | 
| +   * output. | 
| +   */ | 
| +  vector<ProblemRecord> ValidationShouldFail(const mips_inst *pattern, | 
| +                                             size_t inst_count, | 
| +                                             uint32_t base_addr, | 
| +                                             const string &msg); | 
| + | 
| +  SfiValidator _validator; | 
| +}; | 
| + | 
| + | 
| +/* | 
| + * Primitive tests checking various constructor properties.  Any of these | 
| + * failing would be a very bad sign indeed. | 
| + */ | 
| + | 
| +TEST_F(ValidatorTests, RecognizesDataAddressRegisters) { | 
| +  /* | 
| +   * Note that the logic below needs to be kept in sync with the definition | 
| +   * of kAbiDataAddrRegisters at the top of this file. | 
| +   * | 
| +   * This test is pretty trivial -- we can exercise the data_address_register | 
| +   * functionality more deeply with pattern tests below. | 
| +   */ | 
| +  for (int i = 0; i < 31; i++) { | 
| +    Register reg(i); | 
| +    if (reg.Equals(nacl_mips_dec::kRegisterStack)) { | 
| +      EXPECT_TRUE(_validator.IsDataAddressRegister(reg)) | 
| +          << "Stack pointer must be a data address register."; | 
| +    } else { | 
| +      EXPECT_FALSE(_validator.IsDataAddressRegister(reg)) | 
| +          << "Only the stack pointer must be a data address register."; | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +/* | 
| + * Code validation tests | 
| + */ | 
| + | 
| +// This is where untrusted code starts.  Most tests use this. | 
| +static const uint32_t kDefaultBaseAddr = 0x20000; | 
| + | 
| +/* | 
| + * Here are some examples of safe stores permitted in a Native Client | 
| + * program for MIPS32. | 
| + */ | 
| + | 
| +struct AnnotatedInstruction { | 
| +  mips_inst inst; | 
| +  const char *about; | 
| +}; | 
| + | 
| +static const AnnotatedInstruction examples_of_safe_stores[] = { | 
| +  { 0xa1490200, "sb t1, 1024(t2) : store byte" }, | 
| +  { 0xad490200, "sw t1, 1024(t2) : store word" }, | 
| +  { 0xa5490200, "sh t1, 1024(t2) : store halfword" }, | 
| +  { 0xa9490200, "swl t1, 1024(t2) : store word left" }, | 
| +  { 0xb9490200, "swr t1, 1024(t2) : store word right" }, | 
| +}; | 
| + | 
| +static const AnnotatedInstruction examples_of_safe_masks[] = { | 
| +  { (10 << 21 | 15 << 16 | 10 << 11 | 36), | 
| +    "and t2,t2,t7 : simple store masking" }, | 
| +}; | 
| + | 
| +TEST_F(ValidatorTests, SafeMaskedStores) { | 
| +  /* | 
| +   * Produces examples of masked stores using the safe store table (above) | 
| +   * and the list of possible masking instructions (above). | 
| +   */ | 
| + | 
| +  for (unsigned m = 0; m < NACL_ARRAY_SIZE(examples_of_safe_masks); m++) { | 
| +    for (unsigned s = 0; s < NACL_ARRAY_SIZE(examples_of_safe_stores); | 
| +         s++) { | 
| +      ostringstream message; | 
| +      message << examples_of_safe_masks[m].about | 
| +              << ", " | 
| +              << examples_of_safe_stores[s].about; | 
| +      mips_inst program[] = { | 
| +        examples_of_safe_masks[m].inst, | 
| +        examples_of_safe_stores[s].inst, | 
| +      }; | 
| +      ValidationShouldPass(program, | 
| +                           2, | 
| +                           kDefaultBaseAddr, | 
| +                           message.str()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +TEST_F(ValidatorTests, IncorrectStores) { | 
| +  /* | 
| +   * Produces incorrect examples of masked stores using the safe store table | 
| +   * (above) and the list of possible masking instructions (above). | 
| +   * By switching order of instructions (first store, then mask instruction) | 
| +   * we form incorrect pseudo-instruction. | 
| +   */ | 
| + | 
| +  for (unsigned m = 0; m < NACL_ARRAY_SIZE(examples_of_safe_stores); m++) { | 
| +     for (unsigned s = 0; s < NACL_ARRAY_SIZE(examples_of_safe_masks); s++) { | 
| +       ostringstream bad_message; | 
| +       bad_message << examples_of_safe_stores[m].about | 
| +                   << ", " | 
| +                   << examples_of_safe_masks[s].about; | 
| +       mips_inst bad_program[] = { | 
| +           examples_of_safe_stores[m].inst, | 
| +           examples_of_safe_masks[s].inst, | 
| +       }; | 
| + | 
| +       ValidationShouldFail(bad_program, | 
| +                            2, | 
| +                            kDefaultBaseAddr, | 
| +                            bad_message.str()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +static const AnnotatedInstruction examples_of_safe_jump_masks[] = { | 
| +  { (10 << 21 | 14 << 16 | 10 << 11 | 36), | 
| +    "and t2,t2,t6 : simple jump masking" }, | 
| +}; | 
| + | 
| +static const AnnotatedInstruction examples_of_safe_jumps[] = { | 
| +  { ((10<<21| 31 << 11 |9) ), "simple jump jalr t2" }, | 
| +  { (10<<21|8),               "simple jump jr t2" }, | 
| +}; | 
| + | 
| +static const AnnotatedInstruction nop_instruction[] = { | 
| +  {  0, "nop"}, | 
| +}; | 
| + | 
| +TEST_F(ValidatorTests, SafeMaskedJumps) { | 
| +  /* | 
| +   * Produces examples of masked jumps using the safe jump table | 
| +   * (above) and the list of possible masking instructions (above). | 
| +   */ | 
| +  for (unsigned m = 0; m < NACL_ARRAY_SIZE(examples_of_safe_jump_masks); m++) { | 
| +    for (unsigned s = 0; s < NACL_ARRAY_SIZE(examples_of_safe_jumps); s++) { | 
| +      ostringstream message; | 
| +      message << examples_of_safe_jump_masks[m].about | 
| +              << ", " | 
| +              << examples_of_safe_jumps[s].about; | 
| +      mips_inst program[] = { | 
| +        nop_instruction[0].inst, | 
| +        examples_of_safe_jump_masks[m].inst, | 
| +        examples_of_safe_jumps[s].inst, | 
| +      }; | 
| +      ValidationShouldPass(program, | 
| +                           3, | 
| +                           kDefaultBaseAddr, | 
| +                           message.str()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +TEST_F(ValidatorTests, IncorrectJumps) { | 
| +  /* | 
| +   * Produces examples of incorrect jumps using the safe jump table | 
| +   * (above) and the list of possible masking instructions (above). | 
| +   * By switching order of instructions (first jump, then mask instruction) | 
| +   * we form incorrect pseudo-instruction. | 
| +   */ | 
| +  for (unsigned m = 0; m < NACL_ARRAY_SIZE(examples_of_safe_jumps); m++) { | 
| +    for (unsigned s = 0; s < NACL_ARRAY_SIZE(examples_of_safe_jump_masks); | 
| +         s++) { | 
| +      ostringstream bad_message; | 
| +      bad_message << examples_of_safe_jumps[m].about | 
| +                  << ", " | 
| +                  << examples_of_safe_jump_masks[s].about; | 
| + | 
| +      mips_inst bad_program[] = { | 
| +        examples_of_safe_jumps[m].inst, | 
| +        examples_of_safe_jump_masks[s].inst, | 
| +      }; | 
| + | 
| +      ValidationShouldFail(bad_program, | 
| +                           3, | 
| +                           kDefaultBaseAddr, | 
| +                           bad_message.str()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +static const AnnotatedInstruction examples_of_safe_stack_masks[] = { | 
| +  { (29 << 21 | 15 << 16 | 29 << 11 | 36), | 
| +    "and sp,sp,t7 : simple stack masking" }, | 
| +}; | 
| + | 
| + | 
| +static const AnnotatedInstruction examples_of_safe_stack_ops[] = { | 
| +  { (29<<21|8<<16|29<<11|32), "add  sp,sp,t0 : stack addition" }, | 
| +  { (29<<21|8<<16|29<<11|34), "sub  sp,sp,t0 : stack substraction" }, | 
| +}; | 
| + | 
| +TEST_F(ValidatorTests, SafeMaskedStack) { | 
| +  /* | 
| +   * Produces examples of safe pseudo-ops on stack using the safe stack op table | 
| +   * and the list of possible masking instructions (above). | 
| +   */ | 
| +  for (unsigned m = 0; m < NACL_ARRAY_SIZE(examples_of_safe_stack_ops); m++) { | 
| +    for (unsigned s = 0; s < NACL_ARRAY_SIZE(examples_of_safe_stack_masks); | 
| +         s++) { | 
| +      ostringstream message; | 
| +      message << examples_of_safe_stack_ops[m].about | 
| +              << ", " | 
| +              << examples_of_safe_stack_masks[s].about; | 
| +      mips_inst program[] = { | 
| +         examples_of_safe_stack_ops[m].inst, | 
| +         examples_of_safe_stack_masks[s].inst, | 
| +      }; | 
| +      ValidationShouldPass(program, | 
| +                           2, | 
| +                           kDefaultBaseAddr, | 
| +                           message.str()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +TEST_F(ValidatorTests, IncorrectStackOps) { | 
| +  /* | 
| +   * Produces examples of incorrect pseudo-ops on stack using the safe stack op | 
| +   * table and the list of possible masking instructions (above). | 
| +   * With switching order of instructions (first mask instruction, then stack | 
| +   * operation) we form incorrect pseudo-instruction. | 
| +   */ | 
| +  for (unsigned m = 0; m < NACL_ARRAY_SIZE(examples_of_safe_stack_masks); m++) { | 
| +    for (unsigned s = 0; s < NACL_ARRAY_SIZE(examples_of_safe_stack_ops); s++) { | 
| +      ostringstream bad_message; | 
| +      bad_message << examples_of_safe_stack_masks[m].about | 
| +                  << ", " | 
| +                  << examples_of_safe_stack_ops[s].about; | 
| +      mips_inst bad_program[] = { | 
| +        examples_of_safe_stack_masks[m].inst, | 
| +        examples_of_safe_stack_ops[s].inst, | 
| +        nop_instruction[0].inst | 
| +      }; | 
| + | 
| +      ValidationShouldFail(bad_program, | 
| +                           3, | 
| +                           kDefaultBaseAddr, | 
| +                           bad_message.str()); | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +/* | 
| + * Implementation of the ValidatorTests utility methods.  These are documented | 
| + * toward the top of this file. | 
| + */ | 
| +ValidatorTests::ValidatorTests() | 
| +  : _validator(kBytesPerBundle, | 
| +               kCodeRegionSize, | 
| +               kDataRegionSize, | 
| +               nacl_mips_dec::kRegListReserved, | 
| +               nacl_mips_dec::kRegisterStack) {} | 
| + | 
| +bool ValidatorTests::Validate(const mips_inst *pattern, | 
| +                              size_t inst_count, | 
| +                              uint32_t start_addr, | 
| +                              ProblemSink *sink) { | 
| +  // We think in instructions; CodeSegment thinks in bytes. | 
| +  const uint8_t *bytes = reinterpret_cast<const uint8_t *>(pattern); | 
| +  CodeSegment segment(bytes, start_addr, inst_count * sizeof(mips_inst)); | 
| + | 
| +  vector<CodeSegment> segments; | 
| +  segments.push_back(segment); | 
| + | 
| +  return _validator.Validate(segments, sink); | 
| +} | 
| + | 
| +void ValidatorTests::ValidationShouldPass(const mips_inst *pattern, | 
| +                                          size_t inst_count, | 
| +                                          uint32_t base_addr, | 
| +                                          const string &msg) { | 
| +  ASSERT_TRUE( | 
| +      _validator.BundleForAddress(base_addr).BeginAddr() == base_addr) | 
| +      << "base_addr parameter must be bundle-aligned"; | 
| + | 
| +  ProblemSpy spy; | 
| + | 
| +  bool validation_result = Validate(pattern, inst_count, base_addr, &spy); | 
| +  ASSERT_TRUE(validation_result) << msg << " should pass at " << base_addr; | 
| +  vector<ProblemRecord> &problems = spy.problems(); | 
| +  EXPECT_EQ(0U, problems.size()) << msg | 
| +      << " should have no problems when located at " << base_addr; | 
| +} | 
| + | 
| +vector<ProblemRecord> ValidatorTests::ValidationShouldFail( | 
| +    const mips_inst *pattern, | 
| +    size_t inst_count, | 
| +    uint32_t base_addr, | 
| +    const string &msg) { | 
| + | 
| +  ProblemSpy spy; | 
| +  bool validation_result = Validate(pattern, inst_count, base_addr, &spy); | 
| +  EXPECT_FALSE(validation_result) << "Expected to fail: " << msg; | 
| + | 
| +  vector<ProblemRecord> problems = spy.problems(); | 
| +  // Would use ASSERT here, but cannot ASSERT in non-void functions :-( | 
| +  EXPECT_NE(0U, problems.size()) | 
| +      << "Must report validation problems: " << msg; | 
| + | 
| +  // The rest of the checking is done in the caller. | 
| +  return problems; | 
| +} | 
| + | 
| +};  // anonymous namespace | 
| + | 
| +// Test driver function. | 
| +int main(int argc, char *argv[]) { | 
| +  testing::InitGoogleTest(&argc, argv); | 
| +  return RUN_ALL_TESTS(); | 
| +} | 
|  |