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