OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. |
| 5 */ |
| 6 |
| 7 #include <assert.h> |
| 8 #include "native_client/src/trusted/service_runtime/nacl_config.h" |
| 9 #include "native_client/src/trusted/validator_mips/validator.h" |
| 10 #include "native_client/src/include/nacl_macros.h" |
| 11 |
| 12 |
| 13 using nacl_mips_dec::Instruction; |
| 14 using nacl_mips_dec::ClassDecoder; |
| 15 using nacl_mips_dec::Register; |
| 16 using nacl_mips_dec::RegisterList; |
| 17 |
| 18 using nacl_mips_dec::kRegisterJumpMask; |
| 19 using nacl_mips_dec::kRegisterLoadStoreMask; |
| 20 |
| 21 using nacl_mips_dec::kInstrSize; |
| 22 using nacl_mips_dec::kInstrAlign; |
| 23 |
| 24 using std::vector; |
| 25 |
| 26 namespace nacl_mips_val { |
| 27 |
| 28 /* |
| 29 * TODO(petarj): MIPS validator and runtime port need an external security |
| 30 * review before they are delivered in products. |
| 31 */ |
| 32 |
| 33 /********************************************************* |
| 34 * Implementations of patterns used in the first pass. |
| 35 * |
| 36 * N.B. IF YOU ADD A PATTERN HERE, REGISTER IT BELOW. |
| 37 * See the list in apply_patterns. |
| 38 *********************************************************/ |
| 39 |
| 40 // A possible result from a validator pattern. |
| 41 enum PatternMatch { |
| 42 // The pattern does not apply to the instructions it was given. |
| 43 NO_MATCH, |
| 44 // The pattern matches, and is safe; do not allow jumps to split it. |
| 45 PATTERN_SAFE, |
| 46 // The pattern matches, and has detected a problem. |
| 47 PATTERN_UNSAFE |
| 48 }; |
| 49 |
| 50 |
| 51 /********************************************************* |
| 52 * One instruction patterns. |
| 53 *********************************************************/ |
| 54 |
| 55 /* |
| 56 * Checks for instructions that are not allowed. |
| 57 */ |
| 58 static PatternMatch CheckSafety(const SfiValidator &sfi, |
| 59 const DecodedInstruction &inst, |
| 60 ProblemSink *out) { |
| 61 UNREFERENCED_PARAMETER(sfi); |
| 62 if (nacl_mips_dec::MAY_BE_SAFE != inst.safety()) { |
| 63 out->ReportProblem(inst.addr(), inst.safety(), kProblemUnsafe); |
| 64 return PATTERN_UNSAFE; |
| 65 } |
| 66 return PATTERN_SAFE; |
| 67 } |
| 68 |
| 69 /* |
| 70 * Checks for instructions that alter read-only registers. |
| 71 */ |
| 72 static PatternMatch CheckReadOnly(const SfiValidator &sfi, |
| 73 const DecodedInstruction &inst, |
| 74 ProblemSink *out) { |
| 75 if (inst.IsDestGprReg(sfi.read_only_registers())) { |
| 76 out->ReportProblem(inst.addr(), inst.safety(), kProblemReadOnlyRegister); |
| 77 return PATTERN_UNSAFE; |
| 78 } |
| 79 return NO_MATCH; |
| 80 } |
| 81 |
| 82 /* |
| 83 * Checks the location of linking branches -- in order to be correct, the |
| 84 * branches must be in the slot before the last slot. |
| 85 * |
| 86 */ |
| 87 static PatternMatch CheckCallPosition(const SfiValidator &sfi, |
| 88 const DecodedInstruction &inst, |
| 89 ProblemSink *out) { |
| 90 if (inst.IsJal()) { |
| 91 uint32_t end_addr = sfi.BundleForAddress(inst.addr()).EndAddr(); |
| 92 uint32_t branch_slot = end_addr - (2 * kInstrSize); |
| 93 if (inst.addr() != branch_slot) { |
| 94 out->ReportProblem(inst.addr(), inst.safety(), kProblemMisalignedCall); |
| 95 return PATTERN_UNSAFE; |
| 96 } |
| 97 return PATTERN_SAFE; |
| 98 } |
| 99 return NO_MATCH; |
| 100 } |
| 101 |
| 102 /* |
| 103 * Checks for jumps to unsafe area. |
| 104 */ |
| 105 static PatternMatch CheckJumpDestAddr(const SfiValidator &sfi, |
| 106 const DecodedInstruction &instr, |
| 107 ProblemSink *out) { |
| 108 if (instr.IsDirectJump()) { |
| 109 uint32_t dest_addr = instr.DestAddr(); |
| 110 if (dest_addr < sfi.trampoline_region_start()) { |
| 111 // Safe guard region, allow jumps if somebody is suicidal. |
| 112 return PATTERN_SAFE; |
| 113 } else if (dest_addr < sfi.code_region_start()) { |
| 114 // Trampoline region, allow only 0mod16. |
| 115 if ((dest_addr & (sfi.bytes_per_bundle() - 1)) != 0) { |
| 116 out->ReportProblem(instr.addr(), instr.safety(), |
| 117 kProblemUnalignedJumpToTrampoline); |
| 118 return PATTERN_UNSAFE; |
| 119 } |
| 120 return PATTERN_SAFE; |
| 121 } else { |
| 122 // Jumps outside of unsafe region are not allowed. |
| 123 if ((dest_addr & ~(sfi.code_address_mask())) != 0) { |
| 124 out->ReportProblem(instr.addr(), instr.safety(), |
| 125 kProblemBranchInvalidDest); |
| 126 return PATTERN_UNSAFE; |
| 127 } |
| 128 // Another pattern is responsible for checking if it lands inside of a |
| 129 // pseudo-instruction. |
| 130 return PATTERN_SAFE; |
| 131 } |
| 132 } |
| 133 return NO_MATCH; |
| 134 } |
| 135 |
| 136 /********************************************************* |
| 137 * Two instruction patterns. |
| 138 *********************************************************/ |
| 139 |
| 140 /* |
| 141 * Checks if indirect jumps are guarded properly. |
| 142 */ |
| 143 static PatternMatch CheckJmpReg(const SfiValidator &sfi, |
| 144 const DecodedInstruction &first, |
| 145 const DecodedInstruction &second, |
| 146 ProblemSink *out) { |
| 147 UNREFERENCED_PARAMETER(sfi); |
| 148 if (second.IsJmpReg()) { |
| 149 if (first.IsMask(second.TargetReg(), kRegisterJumpMask)) { |
| 150 return PATTERN_SAFE; |
| 151 } |
| 152 out->ReportProblem(second.addr(), second.safety(), |
| 153 kProblemUnsafeJumpRegister); |
| 154 return PATTERN_UNSAFE; |
| 155 } |
| 156 return NO_MATCH; |
| 157 } |
| 158 |
| 159 |
| 160 /* |
| 161 * Checks if change of the data register ($sp) is followed by load/store mask. |
| 162 */ |
| 163 static PatternMatch CheckDataRegisterUpdate(const SfiValidator &sfi, |
| 164 const DecodedInstruction &first, |
| 165 const DecodedInstruction &second, |
| 166 ProblemSink *out) { |
| 167 if (first.IsDestGprReg(sfi.data_address_registers()) |
| 168 && !first.IsMask(first.DestGprReg(), kRegisterLoadStoreMask)) { |
| 169 if (second.IsMask(first.DestGprReg(), kRegisterLoadStoreMask)) { |
| 170 return PATTERN_SAFE; |
| 171 } |
| 172 out->ReportProblem(first.addr(), first.safety(), kProblemUnsafeDataWrite); |
| 173 return PATTERN_UNSAFE; |
| 174 } |
| 175 return NO_MATCH; |
| 176 } |
| 177 |
| 178 /* |
| 179 * Checks if data register ($sp) change is in the delay slot. |
| 180 */ |
| 181 static PatternMatch CheckDataRegisterDslot(const SfiValidator &sfi, |
| 182 const DecodedInstruction &first, |
| 183 const DecodedInstruction &second, |
| 184 ProblemSink *out) { |
| 185 if (second.IsDestGprReg(sfi.data_address_registers()) |
| 186 && !second.IsMask(second.DestGprReg(), kRegisterLoadStoreMask)) { |
| 187 if (first.HasDelaySlot()) { |
| 188 out->ReportProblem(second.addr(), second.safety(), |
| 189 kProblemDataRegInDelaySlot); |
| 190 return PATTERN_UNSAFE; |
| 191 } |
| 192 } |
| 193 return NO_MATCH; |
| 194 } |
| 195 |
| 196 /* |
| 197 * Checks if load and store instructions are preceded by load/store mask. |
| 198 */ |
| 199 static PatternMatch CheckLoadStore(const SfiValidator &sfi, |
| 200 const DecodedInstruction &first, |
| 201 const DecodedInstruction &second, |
| 202 ProblemSink *out) { |
| 203 if (second.IsLoadStore()) { |
| 204 Register base_addr_reg = second.BaseAddressRegister(); |
| 205 if (!sfi.data_address_registers().ContainsAll(base_addr_reg)) { |
| 206 if (first.IsMask(base_addr_reg, kRegisterLoadStoreMask)) { |
| 207 return PATTERN_SAFE; |
| 208 } |
| 209 out->ReportProblem(second.addr(), second.safety(), |
| 210 kProblemUnsafeLoadStore); |
| 211 return PATTERN_UNSAFE; |
| 212 } |
| 213 } |
| 214 return NO_MATCH; |
| 215 } |
| 216 |
| 217 |
| 218 /********************************************************* |
| 219 * Pseudo-instruction patterns. |
| 220 *********************************************************/ |
| 221 |
| 222 /* |
| 223 * Checks if a pseudo-instruction that starts with instr will cross bundle |
| 224 * border (i.e. if it starts in one and ends in second). |
| 225 * The exception to this rule are pseudo-instructions altering the data register |
| 226 * value (because mask is the second instruction). |
| 227 */ |
| 228 static PatternMatch CheckBundleCross(const SfiValidator &sfi, |
| 229 const DecodedInstruction instr, |
| 230 ProblemSink *out) { |
| 231 uint32_t begin_addr = sfi.BundleForAddress(instr.addr()).BeginAddr(); |
| 232 if ((instr.addr() == begin_addr) && !instr.IsDataRegMask()) { |
| 233 out->ReportProblem(instr.addr(), instr.safety(), |
| 234 kProblemPatternCrossesBundle); |
| 235 return PATTERN_UNSAFE; |
| 236 } |
| 237 return PATTERN_SAFE; |
| 238 } |
| 239 |
| 240 /* |
| 241 * Checks if branch instruction will jump in the middle of pseudo-instruction. |
| 242 */ |
| 243 static PatternMatch CheckJumpToPseudo(const SfiValidator &sfi, |
| 244 const std::vector<CodeSegment> &segms, |
| 245 const DecodedInstruction pseudoinstr, |
| 246 const AddressSet &branches, |
| 247 const AddressSet &branch_targets, |
| 248 ProblemSink *out) { |
| 249 uint32_t target_va = pseudoinstr.addr(); |
| 250 if (branch_targets.Contains(target_va)) { |
| 251 std::vector<DecodedInstruction> instrs; |
| 252 if (sfi.FindBranch(segms, branches, target_va, instrs)) { |
| 253 for (uint32_t i = 0; i < instrs.size(); i++) { |
| 254 out->ReportProblem(instrs[i].addr(), instrs[i].safety(), |
| 255 kProblemBranchSplitsPattern); |
| 256 } |
| 257 return PATTERN_UNSAFE; |
| 258 } else { |
| 259 assert(0); |
| 260 } |
| 261 } |
| 262 return PATTERN_SAFE; |
| 263 } |
| 264 |
| 265 |
| 266 /********************************************************* |
| 267 * |
| 268 * Implementation of SfiValidator itself. |
| 269 * |
| 270 *********************************************************/ |
| 271 SfiValidator::SfiValidator(uint32_t bytes_per_bundle, |
| 272 uint32_t code_region_bytes, |
| 273 uint32_t data_region_bytes, |
| 274 RegisterList read_only_registers, |
| 275 RegisterList data_address_registers) |
| 276 : bytes_per_bundle_(bytes_per_bundle), |
| 277 code_region_bytes_(code_region_bytes), |
| 278 data_address_mask_(~(data_region_bytes - 1)), |
| 279 code_address_mask_((code_region_bytes - 1) & kInstrAlign), // 0x0FFFFFFC |
| 280 read_only_registers_(read_only_registers), |
| 281 data_address_registers_(data_address_registers), |
| 282 decode_state_(nacl_mips_dec::init_decode()) {} |
| 283 |
| 284 bool SfiValidator::Validate(const vector<CodeSegment> &segments, |
| 285 ProblemSink *out) { |
| 286 uint32_t base = segments[0].BeginAddr(); |
| 287 uint32_t size = segments.back().EndAddr() - base; |
| 288 AddressSet branches(base, size); |
| 289 AddressSet branch_targets(base, size); |
| 290 AddressSet critical(base, size); |
| 291 |
| 292 bool complete_success = true; |
| 293 |
| 294 for (vector<CodeSegment>::const_iterator it = segments.begin(); |
| 295 it != segments.end(); ++it) { |
| 296 complete_success &= ValidateFallthrough(*it, out, &branches, |
| 297 &branch_targets, &critical); |
| 298 |
| 299 if (!out->ShouldContinue()) { |
| 300 return false; |
| 301 } |
| 302 } |
| 303 |
| 304 complete_success &= ValidatePseudos(*this, segments, |
| 305 branches, branch_targets, critical, out); |
| 306 return complete_success; |
| 307 } |
| 308 |
| 309 bool SfiValidator::ValidateFallthrough(const CodeSegment &segment, |
| 310 ProblemSink *out, |
| 311 AddressSet *branches, |
| 312 AddressSet *branch_targets, |
| 313 AddressSet *critical) { |
| 314 bool complete_success = true; |
| 315 |
| 316 nacl_mips_dec::Forbidden initial_decoder; |
| 317 // Initialize the previous instruction to a syscall, so patterns all fail. |
| 318 DecodedInstruction prev( |
| 319 0, // Virtual address 0, which will be in a different bundle. |
| 320 Instruction(0x0000000c), // syscall. |
| 321 initial_decoder); // and ensure that it decodes as Forbidden. |
| 322 |
| 323 for (uint32_t va = segment.BeginAddr(); va != segment.EndAddr(); |
| 324 va += kInstrSize) { |
| 325 DecodedInstruction inst(va, segment[va], |
| 326 nacl_mips_dec::decode(segment[va], decode_state_)); |
| 327 |
| 328 complete_success &= ApplyPatterns(inst, out); |
| 329 if (!out->ShouldContinue()) return false; |
| 330 |
| 331 complete_success &= ApplyPatterns(prev, inst, critical, out); |
| 332 if (!out->ShouldContinue()) return false; |
| 333 |
| 334 if (inst.IsDirectJump()) { |
| 335 branches->Add(inst.addr()); |
| 336 branch_targets->Add(inst.DestAddr()); |
| 337 } |
| 338 |
| 339 prev = inst; |
| 340 } |
| 341 return complete_success; |
| 342 } |
| 343 |
| 344 bool SfiValidator::ValidatePseudos(const SfiValidator &sfi, |
| 345 const std::vector<CodeSegment> &segments, |
| 346 const AddressSet &branches, |
| 347 const AddressSet &branch_targets, |
| 348 const AddressSet &critical, |
| 349 ProblemSink* out) { |
| 350 bool complete_success = true; |
| 351 vector<CodeSegment>::const_iterator seg_it = segments.begin(); |
| 352 |
| 353 for (AddressSet::Iterator it = critical.Begin(); !it.Equals(critical.End()); |
| 354 it.Next()) { |
| 355 uint32_t va = it.GetAddress(); |
| 356 |
| 357 while (!seg_it->ContainsAddress(va)) { |
| 358 ++seg_it; |
| 359 } |
| 360 |
| 361 const CodeSegment &segment = *seg_it; |
| 362 DecodedInstruction inst_p(va, |
| 363 segment[va], |
| 364 nacl_mips_dec::decode(segment[va], |
| 365 decode_state_)); |
| 366 |
| 367 // Check if the pseudo-instruction will cross bundle borders. |
| 368 complete_success &= CheckBundleCross(sfi, inst_p, out); |
| 369 |
| 370 // Check if direct jumps destination is inside of a pseudo-instruction. |
| 371 complete_success &= CheckJumpToPseudo(sfi, segments, inst_p, branches, |
| 372 branch_targets, out); |
| 373 } |
| 374 |
| 375 return complete_success; |
| 376 } |
| 377 |
| 378 |
| 379 bool SfiValidator::ApplyPatterns(const DecodedInstruction &inst, |
| 380 ProblemSink *out) { |
| 381 // Single-instruction patterns. |
| 382 typedef PatternMatch (*OneInstPattern)(const SfiValidator &, |
| 383 const DecodedInstruction &, |
| 384 ProblemSink *out); |
| 385 static const OneInstPattern one_inst_patterns[] = { |
| 386 &CheckSafety, |
| 387 &CheckReadOnly, |
| 388 &CheckCallPosition, |
| 389 &CheckJumpDestAddr |
| 390 }; |
| 391 |
| 392 bool complete_success = true; |
| 393 |
| 394 for (uint32_t i = 0; i < NACL_ARRAY_SIZE(one_inst_patterns); i++) { |
| 395 PatternMatch r = one_inst_patterns[i](*this, inst, out); |
| 396 switch (r) { |
| 397 case PATTERN_SAFE: |
| 398 case NO_MATCH: |
| 399 break; |
| 400 |
| 401 case PATTERN_UNSAFE: |
| 402 complete_success = false; |
| 403 break; |
| 404 } |
| 405 } |
| 406 return complete_success; |
| 407 } |
| 408 |
| 409 bool SfiValidator::ApplyPatterns(const DecodedInstruction &first, |
| 410 const DecodedInstruction &second, AddressSet *critical, ProblemSink *out) { |
| 411 // Type for two-instruction pattern functions. |
| 412 typedef PatternMatch (*TwoInstPattern)(const SfiValidator &, |
| 413 const DecodedInstruction &first, |
| 414 const DecodedInstruction &second, |
| 415 ProblemSink *out); |
| 416 // The list of patterns -- defined in static functions up top. |
| 417 static const TwoInstPattern two_inst_patterns[] = { |
| 418 &CheckJmpReg, |
| 419 &CheckDataRegisterUpdate, |
| 420 &CheckDataRegisterDslot, |
| 421 &CheckLoadStore |
| 422 }; |
| 423 |
| 424 bool complete_success = true; |
| 425 |
| 426 for (uint32_t i = 0; i < NACL_ARRAY_SIZE(two_inst_patterns); i++) { |
| 427 PatternMatch r = two_inst_patterns[i](*this, first, second, out); |
| 428 switch (r) { |
| 429 case NO_MATCH: |
| 430 break; |
| 431 case PATTERN_UNSAFE: |
| 432 // Pattern is in charge of reporting specific issue. |
| 433 complete_success = false; |
| 434 break; |
| 435 case PATTERN_SAFE: |
| 436 critical->Add(second.addr()); |
| 437 break; |
| 438 } |
| 439 } |
| 440 return complete_success; |
| 441 } |
| 442 |
| 443 bool SfiValidator::IsDataAddressRegister(Register reg) const { |
| 444 return data_address_registers_[reg]; |
| 445 } |
| 446 |
| 447 bool SfiValidator::IsBundleHead(uint32_t address) const { |
| 448 return (address % bytes_per_bundle_) == 0; |
| 449 } |
| 450 |
| 451 const Bundle SfiValidator::BundleForAddress(uint32_t address) const { |
| 452 uint32_t base = address - (address % bytes_per_bundle_); |
| 453 return Bundle(base, bytes_per_bundle_); |
| 454 } |
| 455 |
| 456 bool SfiValidator::FindBranch(const std::vector<CodeSegment> &segments, |
| 457 const AddressSet &branches, |
| 458 uint32_t dest_address, |
| 459 std::vector<DecodedInstruction> &instrs) const { |
| 460 vector<CodeSegment>::const_iterator seg_it = segments.begin(); |
| 461 |
| 462 for (AddressSet::Iterator it = branches.Begin(); !it.Equals(branches.End()); |
| 463 it.Next()) { |
| 464 uint32_t va = it.GetAddress(); |
| 465 |
| 466 while (!seg_it->ContainsAddress(va)) { |
| 467 ++seg_it; |
| 468 } |
| 469 |
| 470 const CodeSegment &segment = *seg_it; |
| 471 DecodedInstruction instr = DecodedInstruction(va, segment[va], |
| 472 nacl_mips_dec::decode(segment[va], decode_state_)); |
| 473 if (instr.DestAddr() == dest_address) { |
| 474 instrs.push_back(instr); |
| 475 } |
| 476 } |
| 477 if (!instrs.empty()) return true; |
| 478 return false; |
| 479 } |
| 480 /* |
| 481 * DecodedInstruction. |
| 482 */ |
| 483 DecodedInstruction::DecodedInstruction(uint32_t vaddr, |
| 484 Instruction inst, |
| 485 const ClassDecoder &decoder) |
| 486 : vaddr_(vaddr), |
| 487 inst_(inst), |
| 488 decoder_(&decoder), |
| 489 safety_(decoder.safety(inst_)) |
| 490 {} |
| 491 |
| 492 } // namespace |
OLD | NEW |