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

Unified Diff: src/trusted/validator_mips/validator.cc

Issue 9979025: [MIPS] Adding validator for MIPS architecture. (Closed) Base URL: http://src.chromium.org/native_client/trunk/src/native_client/
Patch Set: Rebased patch, conflict resolved. Created 8 years, 7 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
« no previous file with comments | « src/trusted/validator_mips/validator.h ('k') | src/trusted/validator_mips/validator_mips.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/trusted/validator_mips/validator.cc
diff --git a/src/trusted/validator_mips/validator.cc b/src/trusted/validator_mips/validator.cc
new file mode 100644
index 0000000000000000000000000000000000000000..eff0dad37e0a52ac4f967baf8abea38a3240f687
--- /dev/null
+++ b/src/trusted/validator_mips/validator.cc
@@ -0,0 +1,492 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include "native_client/src/trusted/service_runtime/nacl_config.h"
+#include "native_client/src/trusted/validator_mips/validator.h"
+#include "native_client/src/include/nacl_macros.h"
+
+
+using nacl_mips_dec::Instruction;
+using nacl_mips_dec::ClassDecoder;
+using nacl_mips_dec::Register;
+using nacl_mips_dec::RegisterList;
+
+using nacl_mips_dec::kRegisterJumpMask;
+using nacl_mips_dec::kRegisterLoadStoreMask;
+
+using nacl_mips_dec::kInstrSize;
+using nacl_mips_dec::kInstrAlign;
+
+using std::vector;
+
+namespace nacl_mips_val {
+
+/*
+ * TODO(petarj): MIPS validator and runtime port need an external security
+ * review before they are delivered in products.
+ */
+
+/*********************************************************
+ * Implementations of patterns used in the first pass.
+ *
+ * N.B. IF YOU ADD A PATTERN HERE, REGISTER IT BELOW.
+ * See the list in apply_patterns.
+ *********************************************************/
+
+// A possible result from a validator pattern.
+enum PatternMatch {
+ // The pattern does not apply to the instructions it was given.
+ NO_MATCH,
+ // The pattern matches, and is safe; do not allow jumps to split it.
+ PATTERN_SAFE,
+ // The pattern matches, and has detected a problem.
+ PATTERN_UNSAFE
+};
+
+
+/*********************************************************
+ * One instruction patterns.
+ *********************************************************/
+
+/*
+ * Checks for instructions that are not allowed.
+ */
+static PatternMatch CheckSafety(const SfiValidator &sfi,
+ const DecodedInstruction &inst,
+ ProblemSink *out) {
+ UNREFERENCED_PARAMETER(sfi);
+ if (nacl_mips_dec::MAY_BE_SAFE != inst.safety()) {
+ out->ReportProblem(inst.addr(), inst.safety(), kProblemUnsafe);
+ return PATTERN_UNSAFE;
+ }
+ return PATTERN_SAFE;
+}
+
+/*
+ * Checks for instructions that alter read-only registers.
+ */
+static PatternMatch CheckReadOnly(const SfiValidator &sfi,
+ const DecodedInstruction &inst,
+ ProblemSink *out) {
+ if (inst.IsDestGprReg(sfi.read_only_registers())) {
+ out->ReportProblem(inst.addr(), inst.safety(), kProblemReadOnlyRegister);
+ return PATTERN_UNSAFE;
+ }
+ return NO_MATCH;
+}
+
+/*
+ * Checks the location of linking branches -- in order to be correct, the
+ * branches must be in the slot before the last slot.
+ *
+ */
+static PatternMatch CheckCallPosition(const SfiValidator &sfi,
+ const DecodedInstruction &inst,
+ ProblemSink *out) {
+ if (inst.IsJal()) {
+ uint32_t end_addr = sfi.BundleForAddress(inst.addr()).EndAddr();
+ uint32_t branch_slot = end_addr - (2 * kInstrSize);
+ if (inst.addr() != branch_slot) {
+ out->ReportProblem(inst.addr(), inst.safety(), kProblemMisalignedCall);
+ return PATTERN_UNSAFE;
+ }
+ return PATTERN_SAFE;
+ }
+ return NO_MATCH;
+}
+
+/*
+ * Checks for jumps to unsafe area.
+ */
+static PatternMatch CheckJumpDestAddr(const SfiValidator &sfi,
+ const DecodedInstruction &instr,
+ ProblemSink *out) {
+ if (instr.IsDirectJump()) {
+ uint32_t dest_addr = instr.DestAddr();
+ if (dest_addr < sfi.trampoline_region_start()) {
+ // Safe guard region, allow jumps if somebody is suicidal.
+ return PATTERN_SAFE;
+ } else if (dest_addr < sfi.code_region_start()) {
+ // Trampoline region, allow only 0mod16.
+ if ((dest_addr & (sfi.bytes_per_bundle() - 1)) != 0) {
+ out->ReportProblem(instr.addr(), instr.safety(),
+ kProblemUnalignedJumpToTrampoline);
+ return PATTERN_UNSAFE;
+ }
+ return PATTERN_SAFE;
+ } else {
+ // Jumps outside of unsafe region are not allowed.
+ if ((dest_addr & ~(sfi.code_address_mask())) != 0) {
+ out->ReportProblem(instr.addr(), instr.safety(),
+ kProblemBranchInvalidDest);
+ return PATTERN_UNSAFE;
+ }
+ // Another pattern is responsible for checking if it lands inside of a
+ // pseudo-instruction.
+ return PATTERN_SAFE;
+ }
+ }
+ return NO_MATCH;
+}
+
+/*********************************************************
+ * Two instruction patterns.
+ *********************************************************/
+
+/*
+ * Checks if indirect jumps are guarded properly.
+ */
+static PatternMatch CheckJmpReg(const SfiValidator &sfi,
+ const DecodedInstruction &first,
+ const DecodedInstruction &second,
+ ProblemSink *out) {
+ UNREFERENCED_PARAMETER(sfi);
+ if (second.IsJmpReg()) {
+ if (first.IsMask(second.TargetReg(), kRegisterJumpMask)) {
+ return PATTERN_SAFE;
+ }
+ out->ReportProblem(second.addr(), second.safety(),
+ kProblemUnsafeJumpRegister);
+ return PATTERN_UNSAFE;
+ }
+ return NO_MATCH;
+}
+
+
+/*
+ * Checks if change of the data register ($sp) is followed by load/store mask.
+ */
+static PatternMatch CheckDataRegisterUpdate(const SfiValidator &sfi,
+ const DecodedInstruction &first,
+ const DecodedInstruction &second,
+ ProblemSink *out) {
+ if (first.IsDestGprReg(sfi.data_address_registers())
+ && !first.IsMask(first.DestGprReg(), kRegisterLoadStoreMask)) {
+ if (second.IsMask(first.DestGprReg(), kRegisterLoadStoreMask)) {
+ return PATTERN_SAFE;
+ }
+ out->ReportProblem(first.addr(), first.safety(), kProblemUnsafeDataWrite);
+ return PATTERN_UNSAFE;
+ }
+ return NO_MATCH;
+}
+
+/*
+ * Checks if data register ($sp) change is in the delay slot.
+ */
+static PatternMatch CheckDataRegisterDslot(const SfiValidator &sfi,
+ const DecodedInstruction &first,
+ const DecodedInstruction &second,
+ ProblemSink *out) {
+ if (second.IsDestGprReg(sfi.data_address_registers())
+ && !second.IsMask(second.DestGprReg(), kRegisterLoadStoreMask)) {
+ if (first.HasDelaySlot()) {
+ out->ReportProblem(second.addr(), second.safety(),
+ kProblemDataRegInDelaySlot);
+ return PATTERN_UNSAFE;
+ }
+ }
+ return NO_MATCH;
+}
+
+/*
+ * Checks if load and store instructions are preceded by load/store mask.
+ */
+static PatternMatch CheckLoadStore(const SfiValidator &sfi,
+ const DecodedInstruction &first,
+ const DecodedInstruction &second,
+ ProblemSink *out) {
+ if (second.IsLoadStore()) {
+ Register base_addr_reg = second.BaseAddressRegister();
+ if (!sfi.data_address_registers().ContainsAll(base_addr_reg)) {
+ if (first.IsMask(base_addr_reg, kRegisterLoadStoreMask)) {
+ return PATTERN_SAFE;
+ }
+ out->ReportProblem(second.addr(), second.safety(),
+ kProblemUnsafeLoadStore);
+ return PATTERN_UNSAFE;
+ }
+ }
+ return NO_MATCH;
+}
+
+
+/*********************************************************
+ * Pseudo-instruction patterns.
+ *********************************************************/
+
+/*
+ * Checks if a pseudo-instruction that starts with instr will cross bundle
+ * border (i.e. if it starts in one and ends in second).
+ * The exception to this rule are pseudo-instructions altering the data register
+ * value (because mask is the second instruction).
+ */
+static PatternMatch CheckBundleCross(const SfiValidator &sfi,
+ const DecodedInstruction instr,
+ ProblemSink *out) {
+ uint32_t begin_addr = sfi.BundleForAddress(instr.addr()).BeginAddr();
+ if ((instr.addr() == begin_addr) && !instr.IsDataRegMask()) {
+ out->ReportProblem(instr.addr(), instr.safety(),
+ kProblemPatternCrossesBundle);
+ return PATTERN_UNSAFE;
+ }
+ return PATTERN_SAFE;
+}
+
+/*
+ * Checks if branch instruction will jump in the middle of pseudo-instruction.
+ */
+static PatternMatch CheckJumpToPseudo(const SfiValidator &sfi,
+ const std::vector<CodeSegment> &segms,
+ const DecodedInstruction pseudoinstr,
+ const AddressSet &branches,
+ const AddressSet &branch_targets,
+ ProblemSink *out) {
+ uint32_t target_va = pseudoinstr.addr();
+ if (branch_targets.Contains(target_va)) {
+ std::vector<DecodedInstruction> instrs;
+ if (sfi.FindBranch(segms, branches, target_va, instrs)) {
+ for (uint32_t i = 0; i < instrs.size(); i++) {
+ out->ReportProblem(instrs[i].addr(), instrs[i].safety(),
+ kProblemBranchSplitsPattern);
+ }
+ return PATTERN_UNSAFE;
+ } else {
+ assert(0);
+ }
+ }
+ return PATTERN_SAFE;
+}
+
+
+/*********************************************************
+ *
+ * Implementation of SfiValidator itself.
+ *
+ *********************************************************/
+SfiValidator::SfiValidator(uint32_t bytes_per_bundle,
+ uint32_t code_region_bytes,
+ uint32_t data_region_bytes,
+ RegisterList read_only_registers,
+ RegisterList data_address_registers)
+ : bytes_per_bundle_(bytes_per_bundle),
+ code_region_bytes_(code_region_bytes),
+ data_address_mask_(~(data_region_bytes - 1)),
+ code_address_mask_((code_region_bytes - 1) & kInstrAlign), // 0x0FFFFFFC
+ read_only_registers_(read_only_registers),
+ data_address_registers_(data_address_registers),
+ decode_state_(nacl_mips_dec::init_decode()) {}
+
+bool SfiValidator::Validate(const vector<CodeSegment> &segments,
+ ProblemSink *out) {
+ uint32_t base = segments[0].BeginAddr();
+ uint32_t size = segments.back().EndAddr() - base;
+ AddressSet branches(base, size);
+ AddressSet branch_targets(base, size);
+ AddressSet critical(base, size);
+
+ bool complete_success = true;
+
+ for (vector<CodeSegment>::const_iterator it = segments.begin();
+ it != segments.end(); ++it) {
+ complete_success &= ValidateFallthrough(*it, out, &branches,
+ &branch_targets, &critical);
+
+ if (!out->ShouldContinue()) {
+ return false;
+ }
+ }
+
+ complete_success &= ValidatePseudos(*this, segments,
+ branches, branch_targets, critical, out);
+ return complete_success;
+}
+
+bool SfiValidator::ValidateFallthrough(const CodeSegment &segment,
+ ProblemSink *out,
+ AddressSet *branches,
+ AddressSet *branch_targets,
+ AddressSet *critical) {
+ bool complete_success = true;
+
+ nacl_mips_dec::Forbidden initial_decoder;
+ // Initialize the previous instruction to a syscall, so patterns all fail.
+ DecodedInstruction prev(
+ 0, // Virtual address 0, which will be in a different bundle.
+ Instruction(0x0000000c), // syscall.
+ initial_decoder); // and ensure that it decodes as Forbidden.
+
+ for (uint32_t va = segment.BeginAddr(); va != segment.EndAddr();
+ va += kInstrSize) {
+ DecodedInstruction inst(va, segment[va],
+ nacl_mips_dec::decode(segment[va], decode_state_));
+
+ complete_success &= ApplyPatterns(inst, out);
+ if (!out->ShouldContinue()) return false;
+
+ complete_success &= ApplyPatterns(prev, inst, critical, out);
+ if (!out->ShouldContinue()) return false;
+
+ if (inst.IsDirectJump()) {
+ branches->Add(inst.addr());
+ branch_targets->Add(inst.DestAddr());
+ }
+
+ prev = inst;
+ }
+ return complete_success;
+}
+
+bool SfiValidator::ValidatePseudos(const SfiValidator &sfi,
+ const std::vector<CodeSegment> &segments,
+ const AddressSet &branches,
+ const AddressSet &branch_targets,
+ const AddressSet &critical,
+ ProblemSink* out) {
+ bool complete_success = true;
+ vector<CodeSegment>::const_iterator seg_it = segments.begin();
+
+ for (AddressSet::Iterator it = critical.Begin(); !it.Equals(critical.End());
+ it.Next()) {
+ uint32_t va = it.GetAddress();
+
+ while (!seg_it->ContainsAddress(va)) {
+ ++seg_it;
+ }
+
+ const CodeSegment &segment = *seg_it;
+ DecodedInstruction inst_p(va,
+ segment[va],
+ nacl_mips_dec::decode(segment[va],
+ decode_state_));
+
+ // Check if the pseudo-instruction will cross bundle borders.
+ complete_success &= CheckBundleCross(sfi, inst_p, out);
+
+ // Check if direct jumps destination is inside of a pseudo-instruction.
+ complete_success &= CheckJumpToPseudo(sfi, segments, inst_p, branches,
+ branch_targets, out);
+ }
+
+ return complete_success;
+}
+
+
+bool SfiValidator::ApplyPatterns(const DecodedInstruction &inst,
+ ProblemSink *out) {
+ // Single-instruction patterns.
+ typedef PatternMatch (*OneInstPattern)(const SfiValidator &,
+ const DecodedInstruction &,
+ ProblemSink *out);
+ static const OneInstPattern one_inst_patterns[] = {
+ &CheckSafety,
+ &CheckReadOnly,
+ &CheckCallPosition,
+ &CheckJumpDestAddr
+ };
+
+ bool complete_success = true;
+
+ for (uint32_t i = 0; i < NACL_ARRAY_SIZE(one_inst_patterns); i++) {
+ PatternMatch r = one_inst_patterns[i](*this, inst, out);
+ switch (r) {
+ case PATTERN_SAFE:
+ case NO_MATCH:
+ break;
+
+ case PATTERN_UNSAFE:
+ complete_success = false;
+ break;
+ }
+ }
+ return complete_success;
+}
+
+bool SfiValidator::ApplyPatterns(const DecodedInstruction &first,
+ const DecodedInstruction &second, AddressSet *critical, ProblemSink *out) {
+ // Type for two-instruction pattern functions.
+ typedef PatternMatch (*TwoInstPattern)(const SfiValidator &,
+ const DecodedInstruction &first,
+ const DecodedInstruction &second,
+ ProblemSink *out);
+ // The list of patterns -- defined in static functions up top.
+ static const TwoInstPattern two_inst_patterns[] = {
+ &CheckJmpReg,
+ &CheckDataRegisterUpdate,
+ &CheckDataRegisterDslot,
+ &CheckLoadStore
+ };
+
+ bool complete_success = true;
+
+ for (uint32_t i = 0; i < NACL_ARRAY_SIZE(two_inst_patterns); i++) {
+ PatternMatch r = two_inst_patterns[i](*this, first, second, out);
+ switch (r) {
+ case NO_MATCH:
+ break;
+ case PATTERN_UNSAFE:
+ // Pattern is in charge of reporting specific issue.
+ complete_success = false;
+ break;
+ case PATTERN_SAFE:
+ critical->Add(second.addr());
+ break;
+ }
+ }
+ return complete_success;
+}
+
+bool SfiValidator::IsDataAddressRegister(Register reg) const {
+ return data_address_registers_[reg];
+}
+
+bool SfiValidator::IsBundleHead(uint32_t address) const {
+ return (address % bytes_per_bundle_) == 0;
+}
+
+const Bundle SfiValidator::BundleForAddress(uint32_t address) const {
+ uint32_t base = address - (address % bytes_per_bundle_);
+ return Bundle(base, bytes_per_bundle_);
+}
+
+bool SfiValidator::FindBranch(const std::vector<CodeSegment> &segments,
+ const AddressSet &branches,
+ uint32_t dest_address,
+ std::vector<DecodedInstruction> &instrs) const {
+ vector<CodeSegment>::const_iterator seg_it = segments.begin();
+
+ for (AddressSet::Iterator it = branches.Begin(); !it.Equals(branches.End());
+ it.Next()) {
+ uint32_t va = it.GetAddress();
+
+ while (!seg_it->ContainsAddress(va)) {
+ ++seg_it;
+ }
+
+ const CodeSegment &segment = *seg_it;
+ DecodedInstruction instr = DecodedInstruction(va, segment[va],
+ nacl_mips_dec::decode(segment[va], decode_state_));
+ if (instr.DestAddr() == dest_address) {
+ instrs.push_back(instr);
+ }
+ }
+ if (!instrs.empty()) return true;
+ return false;
+}
+/*
+ * DecodedInstruction.
+ */
+DecodedInstruction::DecodedInstruction(uint32_t vaddr,
+ Instruction inst,
+ const ClassDecoder &decoder)
+ : vaddr_(vaddr),
+ inst_(inst),
+ decoder_(&decoder),
+ safety_(decoder.safety(inst_))
+{}
+
+} // namespace
« no previous file with comments | « src/trusted/validator_mips/validator.h ('k') | src/trusted/validator_mips/validator_mips.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698