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

Side by Side 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, 6 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« 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