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