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

Side by Side Diff: src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.c

Issue 625923004: Delete old x86 validator. (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: rebase master Created 6 years, 2 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 | Annotate | Revision Log
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 /*
8 * ncvalidate.c
9 * Validate x86 instructions for Native Client
10 *
11 */
12
13 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.h"
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <assert.h>
20
21 #include "native_client/src/include/portability.h"
22 #include "native_client/src/shared/platform/nacl_check.h"
23 #include "native_client/src/trusted/validator/x86/halt_trim.h"
24 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncdecode.h"
25 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/ncvalidate_inter naltypes.h"
26
27 #include "native_client/src/trusted/validator/x86/ncinstbuffer_inl.c"
28 #include "native_client/src/trusted/validator/x86/x86_insts_inl.c"
29
30 #if NACL_TARGET_SUBARCH == 64
31 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixm ask_64.h"
32 #else
33 #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixm ask_32.h"
34 #endif
35
36 /* debugging stuff */
37 #define DEBUGGING 0
38 #if DEBUGGING
39 #define dprint(args) do { printf args; } while (0)
40 #else
41 #define dprint(args) do { if (0) { printf args; } } while (0)
42 /* allows DCE but compiler can still do format string checks */
43 #endif /* DEBUGGING */
44
45 /* TODO(bradchen) verbosity needs to be controllable via commandline flags */
46 #define VERBOSE 1
47 #if VERBOSE
48 #define vprint(vstate, args) \
49 do { \
50 NaClErrorReporter* reporter = vstate->dstate.error_reporter; \
51 (*reporter->printf) args; \
52 } while (0)
53 #else
54 #define vprint(vstate, args) \
55 do { \
56 if (0) { \
57 NaClErrorReporter* reporter = vstate->dstate.error_reporter; \
58 (*reporter->printf) args; \
59 } \
60 } while (0)
61 /* allows DCE but compiler can still do format string checks */
62 #endif /* VERBOSE */
63
64 static const uint8_t kNaClFullStop = 0xf4; /* x86 HALT opcode */
65
66 /* Define how many diagnostic error messages are printed by the validator.
67 * A value of zero generates no diagnostics.
68 * A value >0 allows up to that many diagnostic error messages.
69 * A negative value prints all diagnostic error messages.
70 */
71 static int kMaxDiagnostics = 0;
72
73 /* This flag controls a mode for testing only in which inter-instruction
74 * checks are disabled.
75 */
76 static Bool NACL_FLAG_unsafe_single_inst32_mode = FALSE;
77
78 int NCValidatorGetMaxDiagnostics(void) {
79 return kMaxDiagnostics;
80 }
81
82 void NCValidatorSetMaxDiagnostics(int new_value) {
83 kMaxDiagnostics = new_value;
84 }
85
86 int NCValidatorDidStubOut(struct NCValidatorState *vstate) {
87 return vstate->stats.didstubout;
88 }
89
90 /* This function is intended to only be called by ValidatePrintInstructionError
91 * and ValidatePrintOffsetError.
92 */
93 static void ValidatePrintError(const NaClPcAddress addr,
94 const char *msg,
95 struct NCValidatorState *vstate) {
96 if (vstate->num_diagnostics != 0) {
97 NaClErrorReporter* reporter = vstate->dstate.error_reporter;
98 (*reporter->printf)(
99 reporter,
100 "VALIDATOR: %"NACL_PRIxNaClPcAddress": %s\n", addr, msg);
101 --(vstate->num_diagnostics);
102 if (vstate->num_diagnostics == 0) {
103 (*reporter->printf)(
104 reporter,
105 "VALIDATOR: Error limit reached, turning off diagnostics!\n");
106 }
107 }
108 }
109
110 static void ValidatePrintInstructionError(const struct NCDecoderInst *dinst,
111 const char *msg,
112 struct NCValidatorState *vstate) {
113 ValidatePrintError(NCPrintableInstructionAddress(dinst), msg, vstate);
114 }
115
116 static void ValidatePrintOffsetError(const NaClPcAddress addr,
117 const char *msg,
118 struct NCValidatorState *vstate) {
119 ValidatePrintError(vstate->iadrbase + addr, msg, vstate);
120 }
121
122 /* The low-level implementation for stubbing out an instruction. Always use
123 * this function to (ultimately) stub out instructions. This makes it possible
124 * to detect when the validator modifies the code.
125 */
126 static void NCStubOutMem(struct NCValidatorState *vstate, void *ptr,
127 size_t num) {
128 vstate->stats.didstubout = 1;
129 memset(ptr, kNaClFullStop, num);
130 }
131
132 void NCBadInstructionError(const struct NCDecoderInst *dinst,
133 const char *msg) {
134 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
135 ValidatePrintInstructionError(dinst, msg, vstate);
136 if (vstate->do_stub_out) {
137 NCStubOutMem(vstate, dinst->dstate->memory.mpc,
138 dinst->dstate->memory.read_length);
139 }
140 }
141
142 /* opcode histogram */
143 #if VERBOSE == 1
144 void OpcodeHisto(const uint8_t byte1, struct NCValidatorState *vstate) {
145 vstate->opcodehisto[byte1] += 1;
146 }
147
148 static void InitOpcodeHisto(struct NCValidatorState *vstate) {
149 int i;
150 for (i = 0; i < 256; i += 1) vstate->opcodehisto[i] = 0;
151 }
152
153 static void PrintOpcodeHisto(struct NCValidatorState *vstate) {
154 int i;
155 int printed_in_this_row = 0;
156 NaClErrorReporter* reporter = vstate->dstate.error_reporter;
157 if (!VERBOSE) return;
158 (*reporter->printf)(reporter, "\nOpcode Histogram;\n");
159 for (i = 0; i < 256; ++i) {
160 if (0 != vstate->opcodehisto[i]) {
161 (*reporter->printf)(reporter, "%d\t0x%02x\t", vstate->opcodehisto[i], i);
162 ++printed_in_this_row;
163 if (printed_in_this_row > 3) {
164 printed_in_this_row = 0;
165 (*reporter->printf)(reporter, "\n");
166 }
167 }
168 }
169 if (0 != printed_in_this_row) {
170 (*reporter->printf)(reporter, "\n");
171 }
172 }
173 #else
174 #define OpcodeHisto(b, v)
175 #define InitOpcodeHisto(v)
176 #define PrintOpcodeHisto(f, v)
177 #endif /* VERBOSE == 1 */
178
179 /* statistics code */
180 static void NCStatsInst(struct NCValidatorState *vstate) {
181 vstate->stats.instructions += 1;
182 }
183
184 static void NCStatsCheckTarget(struct NCValidatorState *vstate) {
185 vstate->stats.checktarget += 1;
186 }
187
188 static void NCStatsTargetIndirect(struct NCValidatorState *vstate) {
189 vstate->stats.targetindirect += 1;
190 }
191
192 static void NCStatsSawFailure(struct NCValidatorState *vstate) {
193 vstate->stats.sawfailure = 1;
194 }
195
196 void NCStatsInternalError(struct NCValidatorState *vstate) {
197 vstate->stats.internalerrors += 1;
198 NCStatsSawFailure(vstate);
199 }
200
201 void NCStatsBadAlignment(struct NCValidatorState *vstate) {
202 vstate->stats.badalignment += 1;
203 NCStatsSawFailure(vstate);
204 }
205
206 static void NCStatsSegFault(struct NCValidatorState *vstate) {
207 vstate->stats.segfaults += 1;
208 NCStatsSawFailure(vstate);
209 }
210
211 static void NCStatsNewSegment(struct NCValidatorState *vstate) {
212 vstate->stats.segments += 1;
213 if (vstate->stats.segments > 1) {
214 vprint(vstate, (reporter, "error: multiple segments\n"));
215 NCStatsSawFailure(vstate);
216 }
217 }
218
219 void NCStatsBadTarget(struct NCValidatorState *vstate) {
220 vstate->stats.badtarget += 1;
221 NCStatsSawFailure(vstate);
222 }
223
224 static void NCStatsUnsafeIndirect(struct NCValidatorState *vstate) {
225 vstate->stats.unsafeindirect += 1;
226 NCStatsSawFailure(vstate);
227 }
228
229 static void NCStatsReturn(struct NCValidatorState *vstate) {
230 vstate->stats.returns += 1;
231 NCStatsUnsafeIndirect(vstate);
232 NCStatsSawFailure(vstate);
233 }
234
235 static void NCStatsIllegalInst(struct NCValidatorState *vstate) {
236 vstate->stats.illegalinst += 1;
237 NCStatsSawFailure(vstate);
238 }
239
240 static void NCStatsBadPrefix(struct NCValidatorState *vstate) {
241 vstate->stats.badprefix += 1;
242 vstate->stats.illegalinst += 1; /* a bad prefix is also an invalid inst */
243 NCStatsSawFailure(vstate);
244 }
245
246 static void NCStatsBadInstLength(struct NCValidatorState *vstate) {
247 vstate->stats.badinstlength += 1;
248 NCStatsSawFailure(vstate);
249 }
250
251 static void NCStatsInit(struct NCValidatorState *vstate) {
252 vstate->stats.instructions = 0;
253 vstate->stats.segments = 0;
254 vstate->stats.checktarget = 0;
255 vstate->stats.targetindirect = 0;
256 vstate->stats.badtarget = 0;
257 vstate->stats.unsafeindirect = 0;
258 vstate->stats.returns = 0;
259 vstate->stats.illegalinst = 0;
260 vstate->stats.badalignment = 0;
261 vstate->stats.internalerrors = 0;
262 vstate->stats.badinstlength = 0;
263 vstate->stats.badprefix = 0;
264 vstate->stats.didstubout = 0;
265 vstate->stats.sawfailure = 0;
266 InitOpcodeHisto(vstate);
267 }
268
269 void NCStatsPrint(struct NCValidatorState *vstate) {
270 NaClErrorReporter* reporter;
271 if (!VERBOSE || (vstate == NULL)) return;
272 reporter = vstate->dstate.error_reporter;
273 PrintOpcodeHisto(vstate);
274 (*reporter->printf)(reporter, "Analysis Summary:\n");
275 (*reporter->printf)(reporter, "%d Checked instructions\n",
276 vstate->stats.instructions);
277 (*reporter->printf)(reporter, "%d checked jump targets\n",
278 vstate->stats.checktarget);
279 (*reporter->printf)(
280 reporter, "%d calls/jumps need dynamic checking (%0.2f%%)\n",
281 vstate->stats.targetindirect,
282 vstate->stats.instructions ?
283 100.0 * vstate->stats.targetindirect/vstate->stats.instructions : 0);
284 if (vstate->stats.didstubout) {
285 (*reporter->printf)(reporter, "Some instructions were replaced with HLTs");
286 }
287 (*reporter->printf)(reporter, "\nProblems:\n");
288 (*reporter->printf)(reporter, "%d illegal instructions\n",
289 vstate->stats.illegalinst);
290 (*reporter->printf)(reporter,
291 "%d bad jump targets\n", vstate->stats.badtarget);
292 (*reporter->printf)(
293 reporter, "%d illegal unprotected indirect jumps (including ret)\n",
294 vstate->stats.unsafeindirect);
295 (*reporter->printf)(reporter, "%d instruction alignment defects\n",
296 vstate->stats.badalignment);
297 (*reporter->printf)(reporter, "%d segmentation errors\n",
298 vstate->stats.segfaults);
299 (*reporter->printf)(reporter, "%d bad prefix\n",
300 vstate->stats.badprefix);
301 (*reporter->printf)(reporter, "%d bad instruction length\n",
302 vstate->stats.badinstlength);
303 (*reporter->printf)(reporter, "%d internal errors\n",
304 vstate->stats.internalerrors);
305 }
306
307 /***********************************************************************/
308 /* jump target table */
309 const uint8_t nc_iadrmasks[8] = {0x01, 0x02, 0x04, 0x08,
310 0x10, 0x20, 0x40, 0x80};
311
312 /* forward declarations, needed for registration */
313 static Bool ValidateInst(const NCDecoderInst *dinst);
314 static Bool ValidateInstReplacement(NCDecoderStatePair* tthis,
315 NCDecoderInst *dinst_old,
316 NCDecoderInst *dinst_new);
317 static void NCJumpSummarize(struct NCValidatorState* vstate);
318
319 struct NCValidatorState *NCValidateInit(const NaClPcAddress vbase,
320 const NaClPcAddress codesize,
321 const int readonly_text,
322 const NaClCPUFeaturesX86 *features) {
323 struct NCValidatorState *vstate = NULL;
324 const int bundle_size = 32;
325
326 dprint(("NCValidateInit(%"NACL_PRIxNaClPcAddressAll
327 ", %"NACL_PRIxNaClMemorySizeAll", %08x)\n", vbase, codesize,
328 bundle_size));
329 do {
330 if (features == NULL)
331 break;
332 if ((vbase & (bundle_size - 1)) != 0)
333 break;
334 dprint(("ncv_init(%"NACL_PRIxNaClPcAddress", %"NACL_PRIxNaClMemorySize
335 ")\n", vbase, codesize));
336 vstate = (struct NCValidatorState *)calloc(1, sizeof(*vstate));
337 if (vstate == NULL)
338 break;
339 /* Record default error reporter here, since we don't construct
340 * the decoder state until the call to NCValidateSegment. This allows
341 * us to update the error reporter in the decoder state properly.
342 */
343 vstate->dstate.error_reporter = &kNCNullErrorReporter;
344 vstate->num_diagnostics = kMaxDiagnostics;
345 vstate->iadrbase = vbase;
346 vstate->codesize = codesize;
347 vstate->bundle_size = bundle_size;
348 vstate->bundle_mask = bundle_size - 1;
349 vstate->vttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1);
350 vstate->kttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1);
351 vstate->pattern_nonfirst_insts_table = NULL;
352 vstate->summarize_fn = NCJumpSummarize;
353 vstate->do_stub_out = 0;
354 vstate->readonly_text = readonly_text;
355 if (vstate->vttable == NULL || vstate->kttable == NULL)
356 break;
357 dprint((" allocated tables\n"));
358 NCStatsInit(vstate);
359 NaClCopyCPUFeaturesX86(&vstate->cpufeatures, features);
360 return vstate;
361 } while (0);
362 /* Failure. Clean up memory before returning. */
363 if (NULL != vstate) {
364 if (NULL != vstate->kttable)
365 free(vstate->kttable);
366 if (NULL != vstate->vttable)
367 free(vstate->vttable);
368 free(vstate);
369 }
370 return NULL;
371 }
372
373 void NCValidateSetStubOutMode(struct NCValidatorState *vstate,
374 int do_stub_out) {
375 vstate->do_stub_out = do_stub_out;
376 /* We also turn off error diagnostics, under the assumption
377 * you don't want them. (Note: if the user wants them,
378 * you can run ncval to get them)/
379 */
380 if (do_stub_out) {
381 NCValidateSetNumDiagnostics(vstate, 0);
382 }
383 }
384
385 void NCValidateSetNumDiagnostics(struct NCValidatorState* vstate,
386 int num_diagnostics) {
387 vstate->num_diagnostics = num_diagnostics;
388 }
389
390 void NCValidateSetErrorReporter(struct NCValidatorState* state,
391 NaClErrorReporter* error_reporter) {
392 NCDecoderStateSetErrorReporter(&state->dstate, error_reporter);
393 }
394
395 static INLINE void RememberInstructionBoundary(const NCDecoderInst *dinst,
396 struct NCValidatorState *vstate) {
397 /* The decoder should never pass us an out-of-bounds instruction. */
398 CHECK(dinst->inst_addr < vstate->codesize);
399 if (NCGetAdrTable(dinst->inst_addr, vstate->vttable)) {
400 vprint(vstate, (reporter,
401 "RememberIP: Saw inst at %"NACL_PRIxNaClPcAddressAll
402 " twice\n", NCPrintableInstructionAddress(dinst)));
403 NCStatsInternalError(vstate);
404 return;
405 }
406 NCStatsInst(vstate);
407 NCSetAdrTable(dinst->inst_addr, vstate->vttable);
408 }
409
410 static void RememberJumpTarget(const NCDecoderInst *dinst, int32_t jump_offset,
411 struct NCValidatorState *vstate) {
412 NaClPcAddress target = (dinst->inst_addr + dinst->inst.bytes.length
413 + jump_offset);
414
415 /* For testing only, this mode disables inter-instruction checks. */
416 if (NACL_FLAG_unsafe_single_inst32_mode) return;
417
418 if (target < vstate->codesize) {
419 NCSetAdrTable(target, vstate->kttable);
420 } else if ((target & vstate->bundle_mask) == 0) {
421 /* Allow bundle-aligned jumps. */
422 } else if (dinst->unchanged) {
423 /* If we are replacing this instruction during dynamic code modification
424 * and it has not changed, the jump target must be valid because the
425 * instruction has been previously validated. However, we may be only
426 * replacing a subsection of the code segment and therefore may not have
427 * information about instruction boundaries outside of the code being
428 * replaced. Therefore, we allow unaligned direct jumps outside of the code
429 * being validated if and only if the instruction is unchanged.
430 * If dynamic code replacement is not being performed, inst->unchanged
431 * should always be false.
432 */
433 } else {
434 ValidatePrintInstructionError(dinst, "JUMP TARGET out of range", vstate);
435 NCStatsBadTarget(vstate);
436 }
437 }
438
439 static void ForgetInstructionBoundary(const NCDecoderInst *dinst,
440 struct NCValidatorState *vstate) {
441 /* The decoder should never pass us an out-of-bounds instruction. */
442 CHECK(dinst->inst_addr < vstate->codesize);
443 NCClearAdrTable(dinst->inst_addr, vstate->vttable);
444 if (NULL != vstate->pattern_nonfirst_insts_table) {
445 NCSetAdrTable(dinst->inst_addr, vstate->pattern_nonfirst_insts_table);
446 }
447 }
448
449 int NCValidateFinish(struct NCValidatorState *vstate) {
450 if (vstate == NULL) {
451 dprint(("validator not initialized. Did you call ncvalidate_init()?\n"));
452 /* non-zero indicates failure */
453 return 1;
454 }
455
456 /* If we are stubbing out code, the following checks don't provide any
457 * usefull information, so quit early.
458 */
459 if (vstate->do_stub_out) return vstate->stats.sawfailure;
460
461 /* Double check that the base address matches the alignment constraint. */
462 if (vstate->iadrbase & vstate->bundle_mask) {
463 /* This should never happen because the alignment of iadrbase is */
464 /* checked in NCValidateInit(). */
465 ValidatePrintOffsetError(0, "Bad base address alignment", vstate);
466 NCStatsBadAlignment(vstate);
467 }
468
469 /* Apply summary analysis to collected data during pass over
470 * instructions.
471 */
472 (*(vstate->summarize_fn))(vstate);
473
474 /* Now that all the work is done, generate return code. */
475 /* Return zero if there are no problems. */
476 return (vstate->stats.sawfailure);
477 }
478
479 void NCValidateFreeState(struct NCValidatorState **vstate) {
480 CHECK(*vstate != NULL);
481 free((*vstate)->vttable);
482 free((*vstate)->kttable);
483 free((*vstate)->pattern_nonfirst_insts_table);
484 free(*vstate);
485 *vstate = NULL;
486 }
487
488 /* ValidateSFenceClFlush is called for the sfence/clflush opcode 0f ae /7 */
489 /* It returns 0 if the current instruction is implemented, and 1 if not. */
490 static int ValidateSFenceClFlush(const NCDecoderInst *dinst) {
491 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
492 uint8_t mrm = NCInstBytesByteInline(&dinst->inst_bytes, 2);
493
494 if (modrm_modInline(mrm) == 3) {
495 /* this is an sfence */
496 if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_FXSR))
497 return 0;
498 return 1;
499 } else {
500 /* this is an clflush */
501 if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_CLFLUSH))
502 return 0;
503 return 1;
504 }
505 }
506
507 static void ValidateCallAlignment(const NCDecoderInst *dinst) {
508 NaClPcAddress fallthru = dinst->inst_addr + dinst->inst.bytes.length;
509 struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
510 if (fallthru & vstate->bundle_mask) {
511 #if defined(ERROR_ON_CALL_BUNDLE_ALIGNMENT)
512 /* NOTE: Previously the validator recorded an error for call instructions
513 * that were not aligned against the end of a bundle, as these, while
514 * safe, are not correct with the current code generation idioms.
515 * This #if defined(ERROR_ON_CALL_BUNDLE_ALIGNMENT) was added to allow
516 * experimentation with different call/return idioms.
517 */
518 ValidatePrintInstructionError(dinst, "Bad call alignment", vstate);
519 /* This makes bad call alignment a fatal error. */
520 NCStatsBadAlignment(vstate);
521 #endif
522 }
523 }
524
525 static void ValidateJmp8(const NCDecoderInst *dinst) {
526 int8_t offset = NCInstBytesByteInline(&dinst->inst_bytes,
527 dinst->inst.prefixbytes+1);
528 struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
529 NCStatsCheckTarget(vstate);
530 RememberJumpTarget(dinst, offset, vstate);
531 }
532
533 static void ValidateJmpz(const NCDecoderInst *dinst) {
534 NCInstBytesPtr opcode;
535 uint8_t opcode0;
536 int32_t offset;
537 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
538 NCInstBytesPtrInitInc(&opcode, &dinst->inst_bytes,
539 dinst->inst.prefixbytes);
540 opcode0 = NCInstBytesByteInline(&opcode, 0);
541 NCStatsCheckTarget(vstate);
542 if (opcode0 == 0x0f) {
543 /* Multbyte opcode. Intruction is of form:
544 * 0F80 .. 0F8F: jCC $Jz
545 */
546 NCInstBytesPtr opcode_2;
547 NCInstBytesPtrInitInc(&opcode_2, &opcode, 2);
548 offset = NCInstBytesInt32(&opcode_2, dinst->inst.immbytes);
549 } else {
550 /* Single byte opcode. Must be one of:
551 * E8: call $Jz
552 * E9: jmp $Jx
553 */
554 NCInstBytesPtr opcode_1;
555 NCInstBytesPtrInitInc(&opcode_1, &opcode, 1);
556 offset = NCInstBytesInt32(&opcode_1, dinst->inst.immbytes);
557 /* as a courtesy, check call alignment correctness */
558 if (opcode0 == 0xe8) ValidateCallAlignment(dinst);
559 }
560 RememberJumpTarget(dinst, offset, vstate);
561 }
562
563 /*
564 * The NaCl five-byte safe indirect calling sequence looks like this:
565 * 83 e0 e0 and $0xe0,%eax
566 * ff d0 call *%eax
567 * The call may be replaced with a ff e0 jmp. Any register may
568 * be used, not just eax. The validator requires exactly this
569 * sequence.
570 * Note: The code above assumes 32-bit alignment. Change e0 as appropriate
571 * if a different alignment is used.
572 */
573 static void ValidateIndirect5(const NCDecoderInst *dinst) {
574 NCInstBytesPtr jmpopcode;
575 NCInstBytesPtr andopcode;
576 uint8_t mrm;
577 uint8_t targetreg;
578 const uint8_t kReg_ESP = 4;
579 NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
580
581 struct NCDecoderInst *andinst = PreviousInst(dinst, 1);
582 if ((andinst == NULL) || (andinst->inst.bytes.length != 3)) {
583 NCBadInstructionError(dinst, "Unsafe indirect jump");
584 NCStatsUnsafeIndirect(vstate);
585 return;
586 }
587 /* note: no prefixbytes allowed */
588 NCInstBytesPtrInitInc(&jmpopcode, &dinst->inst_bytes, 0);
589 /* note: no prefixbytes allowed */
590 NCInstBytesPtrInitInc(&andopcode, &andinst->inst_bytes, 0);
591 mrm = NCInstBytesByteInline(&jmpopcode, 1);
592 /* Note that the modrm_rm field holds the
593 * target addr the modrm_reg is the opcode.
594 */
595 targetreg = modrm_rmInline(mrm);
596 NCStatsCheckTarget(vstate);
597 NCStatsTargetIndirect(vstate);
598 do {
599 /* no prefix bytes allowed */
600 if (dinst->inst.prefixbytes != 0) break;
601 if (dinst->inst.prefixbytes != 0) break;
602 /* Check all the opcodes. */
603 /* In GROUP5, 2 => call, 4 => jmp */
604 if (NCInstBytesByteInline(&jmpopcode, 0) != 0xff) break;
605 if ((modrm_regInline(mrm) != 2) && (modrm_regInline(mrm) != 4)) break;
606 /* Issue 32: disallow unsafe call/jump indirection */
607 /* example: ff 12 call (*edx) */
608 /* Reported by defend.the.world on 11 Dec 2008 */
609 if (modrm_modInline(mrm) != 3) break;
610 if (targetreg == kReg_ESP) break;
611 if (NCInstBytesByteInline(&andopcode, 0) != 0x83) break;
612 /* check modrm bytes of or and and instructions */
613 if (NCInstBytesByteInline(&andopcode, 1) != (0xe0 | targetreg)) break;
614 /* check mask */
615 if (NCInstBytesByteInline(&andopcode, 2) !=
616 (0x0ff & ~vstate->bundle_mask)) break;
617 /* All checks look good. Make the sequence 'atomic.' */
618 ForgetInstructionBoundary(dinst, vstate);
619 /* as a courtesy, check call alignment correctness */
620 if (modrm_regInline(mrm) == 2) ValidateCallAlignment(dinst);
621 return;
622 } while (0);
623 NCBadInstructionError(dinst, "Unsafe indirect jump");
624 NCStatsUnsafeIndirect(vstate);
625 }
626
627 /* Checks if the set of prefixes are allowed for the instruction.
628 * By default, we only allow prefixes if they have been allowed
629 * by the bad prefix mask generated inside ncdecode_table.c.
630 * These masks are defined by the NaClInstType of the instruction.
631 * See ncdecode_table.c for more details on how these masks are set.
632 *
633 * Currently:
634 * Only 386, 386L, 386R, 386RE instruction allow Data 16
635 * (unless used as part of instruction selection in a multibyte instruction).
636 * Only 386, JMP8, and JMPZ allow segment registers prefixes.
637 * Only 386L and CMPXCHG8B allow the LOCK prefix.
638 * Only 386R and 386RE instructions allow the REP prefix.
639 * Only 386RE instructions allow the REPNE prefix.
640 *
641 * Note: The prefixmask does not include the prefix value (if any) used to
642 * select multiple byte instructions. Such prefixes have been moved to
643 * opcode_prefixmask, so that the selection (based on that prefix) has
644 * been recorded.
645 *
646 * In general, we do not allow multiple prefixes. Exceptions are as
647 * follows:
648 * 1 - Data 16 is allowed on lock instructions, so that 2 byte values
649 * can be locked.
650 * 2 - Multibyte instructions that are selected
651 * using prefix values Data 16, REP and REPNE, can only have
652 * one of these prefixes (Combinations of these three prefixes
653 * are not allowed for such multibyte instructions).
654 * 3 - Locks are only allowed on instructions with type 386L. The
655 * exception is inst cmpxch8b, which also can have a lock.
656 * 4 - The only two prefix byte combination allowed is Data 16 and Lock.
657 * 5 - Long nops that are hard coded can contain more than one prefix.
658 * See ncdecode.c for details (they don't use ValidatePrefixes).
659 */
660 static Bool ValidatePrefixes(const NCDecoderInst *dinst) {
661 if (dinst->inst.prefixbytes == 0) return TRUE;
662
663 if ((dinst->inst.prefixmask &
664 BadPrefixMask[dinst->opinfo->insttype]) != 0) {
665 return FALSE;
666 }
667
668 /* If a multibyte instruction is using a selection prefix, be
669 * sure that there is no conflict with other selection prefixes.
670 */
671 if ((dinst->inst.opcode_prefixmask != 0) &&
672 ((dinst->inst.prefixmask &
673 (kPrefixDATA16 | kPrefixREPNE | kPrefixREP)) != 0)) {
674 return FALSE;
675 }
676
677 /* Only allow a lock if it is a 386L instruction, or the special
678 * cmpxchg8b instruction.
679 */
680 if (dinst->inst.prefixmask & kPrefixLOCK) {
681 if ((dinst->opinfo->insttype != NACLi_386L) &&
682 (dinst->opinfo->insttype != NACLi_CMPXCHG8B)) {
683 return FALSE;
684 }
685 }
686
687 /* Only allow more than one prefix if two prefixes, and they are
688 * data 16 and lock.
689 */
690 if ((dinst->inst.prefixbytes > 1) &&
691 !((dinst->inst.prefixbytes == 2) &&
692 (dinst->inst.prefixmask == (kPrefixLOCK | kPrefixDATA16)) &&
693 /* Be sure data 16 (66) appears before lock (f0) prefix. */
694 (dinst->inst.lock_prefix_index == 1))) {
695 return FALSE;
696 }
697
698 return TRUE;
699 }
700
701 static const size_t kMaxValidInstLength = 11;
702
703 /* The modrm mod field is a two-bit value. Values 00, 01, and, 10
704 * define memory references. Value 11 defines register accesses instead
705 * of memory.
706 */
707 static const int kModRmModFieldDefinesRegisterRef = 0x3;
708
709 static Bool ValidateInst(const NCDecoderInst *dinst) {
710 NaClCPUFeaturesX86 *cpufeatures;
711 int squashme = 0;
712 NCValidatorState* vstate;
713 if (dinst == NULL) return TRUE;
714 vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
715
716 OpcodeHisto(NCInstBytesByteInline(&dinst->inst_bytes,
717 dinst->inst.prefixbytes),
718 vstate);
719 /* For testing only, this mode disables inter-instruction checks. */
720 if (!NACL_FLAG_unsafe_single_inst32_mode) {
721 RememberInstructionBoundary(dinst, vstate);
722 }
723
724 cpufeatures = &vstate->cpufeatures;
725
726 if (!ValidatePrefixes(dinst)) {
727 NCBadInstructionError(dinst, "Bad prefix usage");
728 NCStatsBadPrefix(vstate);
729 }
730
731 if ((dinst->opinfo->insttype != NACLi_NOP) &&
732 ((size_t) (dinst->inst.bytes.length - dinst->inst.prefixbytes)
733 > kMaxValidInstLength)) {
734 NCBadInstructionError(dinst, "Instruction too long");
735 NCStatsBadInstLength(vstate);
736 }
737
738 switch (dinst->opinfo->insttype) {
739 case NACLi_NOP:
740 case NACLi_386:
741 case NACLi_386L:
742 case NACLi_386R:
743 case NACLi_386RE:
744 break;
745 case NACLi_LAHF:
746 if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_TARGET_SUBARCH == 64)
747 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LAHF);
748 break;
749 case NACLi_JMP8:
750 ValidateJmp8(dinst);
751 break;
752 case NACLi_JMPZ:
753 ValidateJmpz(dinst);
754 break;
755 case NACLi_INDIRECT:
756 ValidateIndirect5(dinst);
757 break;
758 case NACLi_X87:
759 case NACLi_X87_FSINCOS:
760 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87);
761 break;
762 case NACLi_SFENCE_CLFLUSH:
763 squashme = ValidateSFenceClFlush(dinst);
764 break;
765 case NACLi_CMPXCHG8B:
766 /* Only allow if the modrm mod field accesses memory.
767 * This stops us from accepting f00f on multiple bytes.
768 * http://en.wikipedia.org/wiki/Pentium_F00F_bug
769 */
770 if (modrm_modInline(dinst->inst.mrm)
771 == kModRmModFieldDefinesRegisterRef) {
772 NCBadInstructionError(dinst, "Illegal instruction");
773 NCStatsIllegalInst(vstate);
774 }
775 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CX8);
776 break;
777 case NACLi_CMOV:
778 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV);
779 break;
780 case NACLi_FCMOV:
781 squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV) &&
782 NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87));
783 break;
784 case NACLi_RDTSC:
785 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_TSC);
786 break;
787 case NACLi_MMX:
788 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX);
789 break;
790 case NACLi_MMXSSE2:
791 /* Note: We accept these instructions if either MMX or SSE2 bits */
792 /* are set, in case MMX instructions go away someday... */
793 squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX) ||
794 NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2));
795 break;
796 case NACLi_SSE:
797 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE);
798 break;
799 case NACLi_SSE2:
800 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2);
801 break;
802 case NACLi_SSE3:
803 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE3);
804 break;
805 case NACLi_SSE4A:
806 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE4A);
807 break;
808 case NACLi_SSE41:
809 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE41);
810 break;
811 case NACLi_SSE42:
812 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE42);
813 break;
814 case NACLi_MOVBE:
815 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MOVBE);
816 break;
817 case NACLi_POPCNT:
818 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_POPCNT);
819 break;
820 case NACLi_LZCNT:
821 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LZCNT);
822 break;
823 case NACLi_SSSE3:
824 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSSE3);
825 break;
826 case NACLi_3DNOW:
827 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_3DNOW);
828 break;
829 case NACLi_E3DNOW:
830 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_E3DNOW);
831 break;
832 case NACLi_SSE2x:
833 /* This case requires CPUID checking code */
834 /* Note: DATA16 prefix required. The generated table
835 * for group 14 (which the only 2 SSE2x instructions are in),
836 * allows instructions with and without a 66 prefix. However,
837 * the SSE2x instructions psrldq and pslldq are only allowed
838 * with the 66 prefix. Hence, this code has been added to
839 * do this check.
840 */
841 if (!(dinst->inst.opcode_prefixmask & kPrefixDATA16)) {
842 NCBadInstructionError(dinst, "Bad prefix usage");
843 NCStatsBadPrefix(vstate);
844 }
845 squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2);
846 break;
847
848 case NACLi_RETURN:
849 NCBadInstructionError(dinst, "ret instruction (not allowed)");
850 NCStatsReturn(vstate);
851 /* ... and fall through to illegal instruction code */
852 case NACLi_EMMX:
853 /* EMMX needs to be supported someday but isn't ready yet. */
854 case NACLi_INVALID:
855 case NACLi_ILLEGAL:
856 case NACLi_SYSTEM:
857 case NACLi_RDMSR:
858 case NACLi_RDTSCP:
859 case NACLi_SYSCALL:
860 case NACLi_SYSENTER:
861 case NACLi_LONGMODE:
862 case NACLi_SVM:
863 case NACLi_OPINMRM:
864 case NACLi_3BYTE:
865 case NACLi_CMPXCHG16B: {
866 NCBadInstructionError(dinst, "Illegal instruction");
867 NCStatsIllegalInst(vstate);
868 break;
869 }
870 case NACLi_UNDEFINED: {
871 NCBadInstructionError(dinst, "Undefined instruction");
872 NCStatsIllegalInst(vstate);
873 NCStatsInternalError(vstate);
874 break;
875 }
876 default:
877 NCBadInstructionError(dinst, "Undefined instruction type");
878 NCStatsInternalError(vstate);
879 break;
880 }
881 if (squashme) {
882 if (vstate->readonly_text) {
883 NCBadInstructionError(dinst,
884 "Illegal instruction for fixed-feature CPU mode");
885 NCStatsIllegalInst(vstate);
886 } else {
887 NCStubOutMem(vstate, dinst->dstate->memory.mpc,
888 dinst->dstate->memory.read_length);
889 }
890 }
891 return TRUE;
892 }
893
894 Bool UnsafePartialValidateInst(const NCDecoderInst *dinst) {
895 NCValidatorState *vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
896 Bool result = FALSE;
897
898 NACL_FLAG_unsafe_single_inst32_mode = TRUE;
899 NCStatsInit(vstate);
900 if (ValidateInst(dinst)) {
901 result = vstate->stats.sawfailure == 0;
902 };
903 NACL_FLAG_unsafe_single_inst32_mode = FALSE;
904 return result;
905 }
906
907 /*
908 * Validate that two nacljmps are byte-for-byte identical. Note that
909 * one of the individual jumps must be validated in isolation with
910 * ValidateIndirect5() before this is called.
911 */
912 static void ValidateIndirect5Replacement(NCDecoderInst *dinst_old,
913 NCDecoderInst *dinst_new) {
914 do {
915 /* check that the and-guard is 3 bytes and bit-for-bit identical */
916 NCDecoderInst *andinst_old = PreviousInst(dinst_old, 1);
917 NCDecoderInst *andinst_new = PreviousInst(dinst_new, 1);
918 if ((andinst_old == NULL) || (andinst_old->inst.bytes.length != 3)) break;
919 if ((andinst_new == NULL) || (andinst_new->inst.bytes.length != 3)) break;
920 if (memcmp(andinst_old->inst.bytes.byte,
921 andinst_new->inst.bytes.byte, 3) != 0) break;
922
923 /* check that the indirect-jmp is 2 bytes and bit-for-bit identical */
924 if (dinst_old->inst.bytes.length != 2) break;
925 if (dinst_new->inst.bytes.length != 2) break;
926 if (memcmp(dinst_old->inst.bytes.byte,
927 dinst_new->inst.bytes.byte, 2) != 0) break;
928
929 return;
930 } while (0);
931 NCBadInstructionError(dinst_new,
932 "Replacement indirect jump must match original");
933 NCStatsUnsafeIndirect(NCVALIDATOR_STATE_DOWNCAST(dinst_new->dstate));
934 }
935
936 /*
937 * Check that mstate_new is a valid replacement instruction for mstate_old.
938 * Note that mstate_old was validated when it was inserted originally.
939 */
940 static Bool ValidateInstReplacement(NCDecoderStatePair* tthis,
941 NCDecoderInst *dinst_old,
942 NCDecoderInst *dinst_new) {
943 dinst_new->unchanged = memcmp(dinst_old->inst.bytes.byte,
944 dinst_new->inst.bytes.byte,
945 dinst_new->inst.bytes.length) == 0;
946 ValidateInst(dinst_new);
947
948 if (dinst_old->opinfo->insttype == NACLi_INDIRECT
949 || dinst_new->opinfo->insttype == NACLi_INDIRECT) {
950 /* Verify that nacljmps never change */
951 ValidateIndirect5Replacement(dinst_old, dinst_new);
952 }
953 return TRUE;
954 }
955
956 /* Create the decoder state for the validator state, using the
957 * given parameters.
958 */
959 static void NCValidateDStateInit(NCValidatorState *vstate,
960 uint8_t *mbase, NaClPcAddress vbase,
961 NaClMemorySize sz) {
962 NCDecoderState* dstate = &vstate->dstate;
963 /* Note: Based on the current API, we must grab the error reporter
964 * and reinstall it, after the call to NCValidateDStateInit, so that
965 * we don't replace it with the default error reporter.
966 */
967 NaClErrorReporter* reporter = dstate->error_reporter;
968 NCDecoderStateConstruct(dstate, mbase, vbase, sz,
969 vstate->inst_buffer, kNCValidatorInstBufferSize);
970 dstate->action_fn = ValidateInst;
971 dstate->new_segment_fn = (NCDecoderStateMethod) NCStatsNewSegment;
972 dstate->segmentation_error_fn = (NCDecoderStateMethod) NCStatsSegFault;
973 dstate->internal_error_fn = (NCDecoderStateMethod) NCStatsInternalError;
974 NCDecoderStateSetErrorReporter(dstate, reporter);
975 }
976
977 void NCValidateSegment(uint8_t *mbase, NaClPcAddress vbase, NaClMemorySize sz,
978 struct NCValidatorState *vstate) {
979 /* Sanity checks */
980 /* TODO(ncbray): remove redundant vbase/size args. */
981 if ((vbase & vstate->bundle_mask) != 0) {
982 ValidatePrintOffsetError(0, "Bad vbase alignment", vstate);
983 NCStatsSegFault(vstate);
984 return;
985 }
986 if (vbase != vstate->iadrbase) {
987 ValidatePrintOffsetError(0, "Mismatched vbase addresses", vstate);
988 NCStatsSegFault(vstate);
989 return;
990 }
991 if (sz != vstate->codesize) {
992 ValidatePrintOffsetError(0, "Mismatched code size", vstate);
993 NCStatsSegFault(vstate);
994 return;
995 }
996
997 sz = NCHaltTrimSize(mbase, sz, vstate->bundle_size);
998 vstate->codesize = sz;
999
1000 if (sz == 0) {
1001 ValidatePrintOffsetError(0, "Bad text segment (zero size)", vstate);
1002 NCStatsSegFault(vstate);
1003 return;
1004 }
1005 NCValidateDStateInit(vstate, mbase, vbase, sz);
1006 NCDecoderStateDecode(&vstate->dstate);
1007 NCDecoderStateDestruct(&vstate->dstate);
1008 }
1009
1010 int NCValidateSegmentPair(uint8_t *mbase_old, uint8_t *mbase_new,
1011 NaClPcAddress vbase, size_t sz,
1012 const NaClCPUFeaturesX86 *features) {
1013 /* TODO(karl): Refactor to use inheritance from NCDecoderStatePair? */
1014 NCDecoderStatePair pair;
1015 NCValidatorState* new_vstate;
1016 NCValidatorState* old_vstate;
1017
1018 int result = 0;
1019
1020 /* Verify that we actually have a segment to walk. */
1021 if (sz == 0) {
1022 printf("VALIDATOR: %"NACL_PRIxNaClPcAddress
1023 ": Bad text segment (zero size)\n", vbase);
1024 return 0;
1025 }
1026
1027 old_vstate = NCValidateInit(vbase, sz, FALSE, features);
1028 if (old_vstate != NULL) {
1029 NCValidateDStateInit(old_vstate, mbase_old, vbase, sz);
1030 new_vstate = NCValidateInit(vbase, sz, FALSE, features);
1031 if (new_vstate != NULL) {
1032 NCValidateDStateInit(new_vstate, mbase_new, vbase, sz);
1033
1034 NCDecoderStatePairConstruct(&pair,
1035 &old_vstate->dstate,
1036 &new_vstate->dstate,
1037 NULL); /* copy_func */
1038 pair.action_fn = ValidateInstReplacement;
1039 if (NCDecoderStatePairDecode(&pair)) {
1040 result = 1;
1041 } else {
1042 ValidatePrintOffsetError(0, "Replacement not applied!\n", new_vstate);
1043 }
1044 if (NCValidateFinish(new_vstate)) {
1045 /* Errors occurred during validation. */
1046 result = 0;
1047 }
1048 NCDecoderStatePairDestruct(&pair);
1049 NCDecoderStateDestruct(&new_vstate->dstate);
1050 NCValidateFreeState(&new_vstate);
1051 }
1052 NCDecoderStateDestruct(&old_vstate->dstate);
1053 NCValidateFreeState(&old_vstate);
1054 }
1055 return result;
1056 }
1057
1058 /* Walk the collected information on instruction boundaries and jump targets,
1059 * and verify that they are legal.
1060 */
1061 static void NCJumpSummarize(struct NCValidatorState* vstate) {
1062 uint32_t offset;
1063
1064 /* Verify that jumps are to the beginning of instructions, and that the
1065 * jumped to instruction is not in the middle of a native client pattern.
1066 */
1067 dprint(("CheckTargets: %"NACL_PRIxNaClPcAddress"-%"NACL_PRIxNaClPcAddress"\n",
1068 vstate->iadrbase, vstate->iadrbase+vstate->codesize));
1069 for (offset = 0; offset < vstate->codesize; offset += 1) {
1070 if (NCGetAdrTable(offset, vstate->kttable)) {
1071 NCStatsCheckTarget(vstate);
1072 if (!NCGetAdrTable(offset, vstate->vttable)) {
1073 ValidatePrintOffsetError(offset, "Bad jump target", vstate);
1074 NCStatsBadTarget(vstate);
1075 }
1076 }
1077 }
1078
1079 /* check basic block boundaries */
1080 for (offset = 0; offset < vstate->codesize; offset += vstate->bundle_size) {
1081 if (!NCGetAdrTable(offset, vstate->vttable)) {
1082 ValidatePrintOffsetError(offset, "Bad basic block alignment", vstate);
1083 NCStatsBadAlignment(vstate);
1084 }
1085 }
1086 }
OLDNEW
« no previous file with comments | « src/trusted/validator/x86/ncval_seg_sfi/ncvalidate.h ('k') | src/trusted/validator/x86/ncval_seg_sfi/ncvalidate_detailed.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698