OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 /* | |
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 } | |
OLD | NEW |