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_internaltypes.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/ncbadprefixmask_64.h"
32 : #else
33 : #include "native_client/src/trusted/validator/x86/ncval_seg_sfi/gen/ncbadprefixmask_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 0 : return kMaxDiagnostics;
80 : }
81 :
82 569 : void NCValidatorSetMaxDiagnostics(int new_value) {
83 569 : kMaxDiagnostics = new_value;
84 569 : }
85 :
86 0 : int NCValidatorDidStubOut(struct NCValidatorState *vstate) {
87 0 : return vstate->stats.didstubout;
88 : }
89 :
90 : /* This function is intended to only be called by ValidatePrintInstructionError
91 : * and ValidatePrintOffsetError.
92 : */
93 0 : static void ValidatePrintError(const NaClPcAddress addr,
94 0 : const char *msg,
95 0 : struct NCValidatorState *vstate) {
96 0 : if (vstate->num_diagnostics != 0) {
97 0 : NaClErrorReporter* reporter = vstate->dstate.error_reporter;
98 0 : (*reporter->printf)(
99 : reporter,
100 : "VALIDATOR: %"NACL_PRIxNaClPcAddress": %s\n", addr, msg);
101 0 : --(vstate->num_diagnostics);
102 0 : if (vstate->num_diagnostics == 0) {
103 0 : (*reporter->printf)(
104 : reporter,
105 : "VALIDATOR: Error limit reached, turning off diagnostics!\n");
106 0 : }
107 0 : }
108 0 : }
109 :
110 0 : static void ValidatePrintInstructionError(const struct NCDecoderInst *dinst,
111 0 : const char *msg,
112 0 : struct NCValidatorState *vstate) {
113 0 : ValidatePrintError(NCPrintableInstructionAddress(dinst), msg, vstate);
114 0 : }
115 :
116 0 : static void ValidatePrintOffsetError(const NaClPcAddress addr,
117 0 : const char *msg,
118 0 : struct NCValidatorState *vstate) {
119 0 : ValidatePrintError(vstate->iadrbase + addr, msg, vstate);
120 0 : }
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 0 : static void NCStubOutMem(struct NCValidatorState *vstate, void *ptr,
127 0 : size_t num) {
128 0 : vstate->stats.didstubout = 1;
129 0 : memset(ptr, kNaClFullStop, num);
130 0 : }
131 :
132 0 : void NCBadInstructionError(const struct NCDecoderInst *dinst,
133 0 : const char *msg) {
134 0 : NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
135 0 : ValidatePrintInstructionError(dinst, msg, vstate);
136 0 : if (vstate->do_stub_out) {
137 0 : NCStubOutMem(vstate, dinst->dstate->memory.mpc,
138 : dinst->dstate->memory.read_length);
139 0 : }
140 0 : }
141 :
142 : /* opcode histogram */
143 : #if VERBOSE == 1
144 0 : void OpcodeHisto(const uint8_t byte1, struct NCValidatorState *vstate) {
145 0 : vstate->opcodehisto[byte1] += 1;
146 0 : }
147 :
148 0 : static void InitOpcodeHisto(struct NCValidatorState *vstate) {
149 0 : int i;
150 0 : for (i = 0; i < 256; i += 1) vstate->opcodehisto[i] = 0;
151 0 : }
152 :
153 0 : static void PrintOpcodeHisto(struct NCValidatorState *vstate) {
154 0 : int i;
155 0 : int printed_in_this_row = 0;
156 0 : NaClErrorReporter* reporter = vstate->dstate.error_reporter;
157 : if (!VERBOSE) return;
158 0 : (*reporter->printf)(reporter, "\nOpcode Histogram;\n");
159 0 : for (i = 0; i < 256; ++i) {
160 0 : if (0 != vstate->opcodehisto[i]) {
161 0 : (*reporter->printf)(reporter, "%d\t0x%02x\t", vstate->opcodehisto[i], i);
162 0 : ++printed_in_this_row;
163 0 : if (printed_in_this_row > 3) {
164 0 : printed_in_this_row = 0;
165 0 : (*reporter->printf)(reporter, "\n");
166 0 : }
167 0 : }
168 0 : }
169 0 : if (0 != printed_in_this_row) {
170 0 : (*reporter->printf)(reporter, "\n");
171 0 : }
172 0 : }
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 0 : static void NCStatsInst(struct NCValidatorState *vstate) {
181 0 : vstate->stats.instructions += 1;
182 0 : }
183 :
184 0 : static void NCStatsCheckTarget(struct NCValidatorState *vstate) {
185 0 : vstate->stats.checktarget += 1;
186 0 : }
187 :
188 0 : static void NCStatsTargetIndirect(struct NCValidatorState *vstate) {
189 0 : vstate->stats.targetindirect += 1;
190 0 : }
191 :
192 0 : static void NCStatsSawFailure(struct NCValidatorState *vstate) {
193 0 : vstate->stats.sawfailure = 1;
194 0 : }
195 :
196 0 : void NCStatsInternalError(struct NCValidatorState *vstate) {
197 0 : vstate->stats.internalerrors += 1;
198 0 : NCStatsSawFailure(vstate);
199 0 : }
200 :
201 0 : void NCStatsBadAlignment(struct NCValidatorState *vstate) {
202 0 : vstate->stats.badalignment += 1;
203 0 : NCStatsSawFailure(vstate);
204 0 : }
205 :
206 0 : static void NCStatsSegFault(struct NCValidatorState *vstate) {
207 0 : vstate->stats.segfaults += 1;
208 0 : NCStatsSawFailure(vstate);
209 0 : }
210 :
211 0 : static void NCStatsNewSegment(struct NCValidatorState *vstate) {
212 0 : vstate->stats.segments += 1;
213 0 : if (vstate->stats.segments > 1) {
214 0 : vprint(vstate, (reporter, "error: multiple segments\n"));
215 0 : NCStatsSawFailure(vstate);
216 0 : }
217 0 : }
218 :
219 0 : void NCStatsBadTarget(struct NCValidatorState *vstate) {
220 0 : vstate->stats.badtarget += 1;
221 0 : NCStatsSawFailure(vstate);
222 0 : }
223 :
224 0 : static void NCStatsUnsafeIndirect(struct NCValidatorState *vstate) {
225 0 : vstate->stats.unsafeindirect += 1;
226 0 : NCStatsSawFailure(vstate);
227 0 : }
228 :
229 0 : static void NCStatsReturn(struct NCValidatorState *vstate) {
230 0 : vstate->stats.returns += 1;
231 0 : NCStatsUnsafeIndirect(vstate);
232 0 : NCStatsSawFailure(vstate);
233 0 : }
234 :
235 0 : static void NCStatsIllegalInst(struct NCValidatorState *vstate) {
236 0 : vstate->stats.illegalinst += 1;
237 0 : NCStatsSawFailure(vstate);
238 0 : }
239 :
240 0 : static void NCStatsBadPrefix(struct NCValidatorState *vstate) {
241 0 : vstate->stats.badprefix += 1;
242 0 : vstate->stats.illegalinst += 1; /* a bad prefix is also an invalid inst */
243 0 : NCStatsSawFailure(vstate);
244 0 : }
245 :
246 0 : static void NCStatsBadInstLength(struct NCValidatorState *vstate) {
247 0 : vstate->stats.badinstlength += 1;
248 0 : NCStatsSawFailure(vstate);
249 0 : }
250 :
251 0 : static void NCStatsInit(struct NCValidatorState *vstate) {
252 0 : vstate->stats.instructions = 0;
253 0 : vstate->stats.segments = 0;
254 0 : vstate->stats.checktarget = 0;
255 0 : vstate->stats.targetindirect = 0;
256 0 : vstate->stats.badtarget = 0;
257 0 : vstate->stats.unsafeindirect = 0;
258 0 : vstate->stats.returns = 0;
259 0 : vstate->stats.illegalinst = 0;
260 0 : vstate->stats.badalignment = 0;
261 0 : vstate->stats.internalerrors = 0;
262 0 : vstate->stats.badinstlength = 0;
263 0 : vstate->stats.badprefix = 0;
264 0 : vstate->stats.didstubout = 0;
265 0 : vstate->stats.sawfailure = 0;
266 0 : InitOpcodeHisto(vstate);
267 0 : }
268 :
269 0 : void NCStatsPrint(struct NCValidatorState *vstate) {
270 0 : NaClErrorReporter* reporter;
271 0 : if (!VERBOSE || (vstate == NULL)) return;
272 0 : reporter = vstate->dstate.error_reporter;
273 0 : PrintOpcodeHisto(vstate);
274 0 : (*reporter->printf)(reporter, "Analysis Summary:\n");
275 0 : (*reporter->printf)(reporter, "%d Checked instructions\n",
276 : vstate->stats.instructions);
277 0 : (*reporter->printf)(reporter, "%d checked jump targets\n",
278 : vstate->stats.checktarget);
279 0 : (*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 0 : if (vstate->stats.didstubout) {
285 0 : (*reporter->printf)(reporter, "Some instructions were replaced with HLTs");
286 0 : }
287 0 : (*reporter->printf)(reporter, "\nProblems:\n");
288 0 : (*reporter->printf)(reporter, "%d illegal instructions\n",
289 : vstate->stats.illegalinst);
290 0 : (*reporter->printf)(reporter,
291 : "%d bad jump targets\n", vstate->stats.badtarget);
292 0 : (*reporter->printf)(
293 : reporter, "%d illegal unprotected indirect jumps (including ret)\n",
294 : vstate->stats.unsafeindirect);
295 0 : (*reporter->printf)(reporter, "%d instruction alignment defects\n",
296 : vstate->stats.badalignment);
297 0 : (*reporter->printf)(reporter, "%d segmentation errors\n",
298 : vstate->stats.segfaults);
299 0 : (*reporter->printf)(reporter, "%d bad prefix\n",
300 : vstate->stats.badprefix);
301 0 : (*reporter->printf)(reporter, "%d bad instruction length\n",
302 : vstate->stats.badinstlength);
303 0 : (*reporter->printf)(reporter, "%d internal errors\n",
304 : vstate->stats.internalerrors);
305 0 : }
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 0 : struct NCValidatorState *NCValidateInit(const NaClPcAddress vbase,
320 0 : const NaClPcAddress codesize,
321 0 : const int readonly_text,
322 0 : const NaClCPUFeaturesX86 *features) {
323 0 : struct NCValidatorState *vstate = NULL;
324 0 : const int bundle_size = 32;
325 :
326 0 : dprint(("NCValidateInit(%"NACL_PRIxNaClPcAddressAll
327 : ", %"NACL_PRIxNaClMemorySizeAll", %08x)\n", vbase, codesize,
328 : bundle_size));
329 0 : do {
330 0 : if (features == NULL)
331 0 : break;
332 0 : if ((vbase & (bundle_size - 1)) != 0)
333 0 : break;
334 0 : dprint(("ncv_init(%"NACL_PRIxNaClPcAddress", %"NACL_PRIxNaClMemorySize
335 : ")\n", vbase, codesize));
336 0 : vstate = (struct NCValidatorState *)calloc(1, sizeof(*vstate));
337 0 : if (vstate == NULL)
338 0 : 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 0 : vstate->dstate.error_reporter = &kNCNullErrorReporter;
344 0 : vstate->num_diagnostics = kMaxDiagnostics;
345 0 : vstate->iadrbase = vbase;
346 0 : vstate->codesize = codesize;
347 0 : vstate->bundle_size = bundle_size;
348 0 : vstate->bundle_mask = bundle_size - 1;
349 0 : vstate->vttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1);
350 0 : vstate->kttable = (uint8_t *)calloc(NCIATOffset(codesize) + 1, 1);
351 0 : vstate->pattern_nonfirst_insts_table = NULL;
352 0 : vstate->summarize_fn = NCJumpSummarize;
353 0 : vstate->do_stub_out = 0;
354 0 : vstate->readonly_text = readonly_text;
355 0 : if (vstate->vttable == NULL || vstate->kttable == NULL)
356 0 : break;
357 0 : dprint((" allocated tables\n"));
358 0 : NCStatsInit(vstate);
359 0 : NaClCopyCPUFeaturesX86(&vstate->cpufeatures, features);
360 0 : return vstate;
361 : } while (0);
362 : /* Failure. Clean up memory before returning. */
363 0 : if (NULL != vstate) {
364 0 : if (NULL != vstate->kttable)
365 0 : free(vstate->kttable);
366 0 : if (NULL != vstate->vttable)
367 0 : free(vstate->vttable);
368 0 : free(vstate);
369 0 : }
370 0 : return NULL;
371 0 : }
372 :
373 0 : void NCValidateSetStubOutMode(struct NCValidatorState *vstate,
374 0 : int do_stub_out) {
375 0 : 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 0 : if (do_stub_out) {
381 0 : NCValidateSetNumDiagnostics(vstate, 0);
382 0 : }
383 0 : }
384 :
385 0 : void NCValidateSetNumDiagnostics(struct NCValidatorState* vstate,
386 0 : int num_diagnostics) {
387 0 : vstate->num_diagnostics = num_diagnostics;
388 0 : }
389 :
390 0 : void NCValidateSetErrorReporter(struct NCValidatorState* state,
391 0 : NaClErrorReporter* error_reporter) {
392 0 : NCDecoderStateSetErrorReporter(&state->dstate, error_reporter);
393 0 : }
394 :
395 0 : static INLINE void RememberInstructionBoundary(const NCDecoderInst *dinst,
396 0 : struct NCValidatorState *vstate) {
397 : /* The decoder should never pass us an out-of-bounds instruction. */
398 0 : CHECK(dinst->inst_addr < vstate->codesize);
399 0 : if (NCGetAdrTable(dinst->inst_addr, vstate->vttable)) {
400 0 : vprint(vstate, (reporter,
401 : "RememberIP: Saw inst at %"NACL_PRIxNaClPcAddressAll
402 : " twice\n", NCPrintableInstructionAddress(dinst)));
403 0 : NCStatsInternalError(vstate);
404 0 : return;
405 : }
406 0 : NCStatsInst(vstate);
407 0 : NCSetAdrTable(dinst->inst_addr, vstate->vttable);
408 0 : }
409 :
410 0 : static void RememberJumpTarget(const NCDecoderInst *dinst, int32_t jump_offset,
411 0 : struct NCValidatorState *vstate) {
412 0 : NaClPcAddress target = (dinst->inst_addr + dinst->inst.bytes.length
413 : + jump_offset);
414 :
415 : /* For testing only, this mode disables inter-instruction checks. */
416 0 : if (NACL_FLAG_unsafe_single_inst32_mode) return;
417 :
418 0 : if (target < vstate->codesize) {
419 0 : NCSetAdrTable(target, vstate->kttable);
420 0 : } else if ((target & vstate->bundle_mask) == 0) {
421 : /* Allow bundle-aligned jumps. */
422 0 : } 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 0 : } else {
434 0 : ValidatePrintInstructionError(dinst, "JUMP TARGET out of range", vstate);
435 0 : NCStatsBadTarget(vstate);
436 : }
437 0 : }
438 :
439 0 : static void ForgetInstructionBoundary(const NCDecoderInst *dinst,
440 0 : struct NCValidatorState *vstate) {
441 : /* The decoder should never pass us an out-of-bounds instruction. */
442 0 : CHECK(dinst->inst_addr < vstate->codesize);
443 0 : NCClearAdrTable(dinst->inst_addr, vstate->vttable);
444 0 : if (NULL != vstate->pattern_nonfirst_insts_table) {
445 0 : NCSetAdrTable(dinst->inst_addr, vstate->pattern_nonfirst_insts_table);
446 0 : }
447 0 : }
448 :
449 0 : int NCValidateFinish(struct NCValidatorState *vstate) {
450 0 : if (vstate == NULL) {
451 0 : dprint(("validator not initialized. Did you call ncvalidate_init()?\n"));
452 : /* non-zero indicates failure */
453 0 : 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 0 : if (vstate->do_stub_out) return vstate->stats.sawfailure;
460 :
461 : /* Double check that the base address matches the alignment constraint. */
462 0 : if (vstate->iadrbase & vstate->bundle_mask) {
463 : /* This should never happen because the alignment of iadrbase is */
464 : /* checked in NCValidateInit(). */
465 0 : ValidatePrintOffsetError(0, "Bad base address alignment", vstate);
466 0 : NCStatsBadAlignment(vstate);
467 0 : }
468 :
469 : /* Apply summary analysis to collected data during pass over
470 : * instructions.
471 : */
472 0 : (*(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 0 : return (vstate->stats.sawfailure);
477 0 : }
478 :
479 0 : void NCValidateFreeState(struct NCValidatorState **vstate) {
480 0 : CHECK(*vstate != NULL);
481 0 : free((*vstate)->vttable);
482 0 : free((*vstate)->kttable);
483 0 : free((*vstate)->pattern_nonfirst_insts_table);
484 0 : free(*vstate);
485 0 : *vstate = NULL;
486 0 : }
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 0 : static int ValidateSFenceClFlush(const NCDecoderInst *dinst) {
491 0 : NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
492 0 : uint8_t mrm = NCInstBytesByteInline(&dinst->inst_bytes, 2);
493 :
494 0 : if (modrm_modInline(mrm) == 3) {
495 : /* this is an sfence */
496 0 : if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_FXSR))
497 0 : return 0;
498 0 : return 1;
499 : } else {
500 : /* this is an clflush */
501 0 : if (NaClGetCPUFeatureX86(&vstate->cpufeatures, NaClCPUFeatureX86_CLFLUSH))
502 0 : return 0;
503 0 : return 1;
504 : }
505 0 : }
506 :
507 0 : static void ValidateCallAlignment(const NCDecoderInst *dinst) {
508 0 : NaClPcAddress fallthru = dinst->inst_addr + dinst->inst.bytes.length;
509 0 : struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
510 0 : 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 0 : }
523 0 : }
524 :
525 0 : static void ValidateJmp8(const NCDecoderInst *dinst) {
526 0 : int8_t offset = NCInstBytesByteInline(&dinst->inst_bytes,
527 : dinst->inst.prefixbytes+1);
528 0 : struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
529 0 : NCStatsCheckTarget(vstate);
530 0 : RememberJumpTarget(dinst, offset, vstate);
531 0 : }
532 :
533 0 : static void ValidateJmpz(const NCDecoderInst *dinst) {
534 0 : NCInstBytesPtr opcode;
535 0 : uint8_t opcode0;
536 0 : int32_t offset;
537 0 : NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
538 0 : NCInstBytesPtrInitInc(&opcode, &dinst->inst_bytes,
539 : dinst->inst.prefixbytes);
540 0 : opcode0 = NCInstBytesByteInline(&opcode, 0);
541 0 : NCStatsCheckTarget(vstate);
542 0 : if (opcode0 == 0x0f) {
543 : /* Multbyte opcode. Intruction is of form:
544 : * 0F80 .. 0F8F: jCC $Jz
545 : */
546 0 : NCInstBytesPtr opcode_2;
547 0 : NCInstBytesPtrInitInc(&opcode_2, &opcode, 2);
548 0 : offset = NCInstBytesInt32(&opcode_2, dinst->inst.immbytes);
549 0 : } else {
550 : /* Single byte opcode. Must be one of:
551 : * E8: call $Jz
552 : * E9: jmp $Jx
553 : */
554 0 : NCInstBytesPtr opcode_1;
555 0 : NCInstBytesPtrInitInc(&opcode_1, &opcode, 1);
556 0 : offset = NCInstBytesInt32(&opcode_1, dinst->inst.immbytes);
557 : /* as a courtesy, check call alignment correctness */
558 0 : if (opcode0 == 0xe8) ValidateCallAlignment(dinst);
559 : }
560 0 : RememberJumpTarget(dinst, offset, vstate);
561 0 : }
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 0 : static void ValidateIndirect5(const NCDecoderInst *dinst) {
574 0 : NCInstBytesPtr jmpopcode;
575 0 : NCInstBytesPtr andopcode;
576 0 : uint8_t mrm;
577 0 : uint8_t targetreg;
578 0 : const uint8_t kReg_ESP = 4;
579 0 : NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
580 :
581 0 : struct NCDecoderInst *andinst = PreviousInst(dinst, 1);
582 0 : if ((andinst == NULL) || (andinst->inst.bytes.length != 3)) {
583 0 : NCBadInstructionError(dinst, "Unsafe indirect jump");
584 0 : NCStatsUnsafeIndirect(vstate);
585 0 : return;
586 : }
587 : /* note: no prefixbytes allowed */
588 0 : NCInstBytesPtrInitInc(&jmpopcode, &dinst->inst_bytes, 0);
589 : /* note: no prefixbytes allowed */
590 0 : NCInstBytesPtrInitInc(&andopcode, &andinst->inst_bytes, 0);
591 0 : mrm = NCInstBytesByteInline(&jmpopcode, 1);
592 : /* Note that the modrm_rm field holds the
593 : * target addr the modrm_reg is the opcode.
594 : */
595 0 : targetreg = modrm_rmInline(mrm);
596 0 : NCStatsCheckTarget(vstate);
597 0 : NCStatsTargetIndirect(vstate);
598 0 : do {
599 : /* no prefix bytes allowed */
600 0 : if (dinst->inst.prefixbytes != 0) break;
601 0 : if (dinst->inst.prefixbytes != 0) break;
602 : /* Check all the opcodes. */
603 : /* In GROUP5, 2 => call, 4 => jmp */
604 0 : if (NCInstBytesByteInline(&jmpopcode, 0) != 0xff) break;
605 0 : 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 0 : if (modrm_modInline(mrm) != 3) break;
610 0 : if (targetreg == kReg_ESP) break;
611 0 : if (NCInstBytesByteInline(&andopcode, 0) != 0x83) break;
612 : /* check modrm bytes of or and and instructions */
613 0 : if (NCInstBytesByteInline(&andopcode, 1) != (0xe0 | targetreg)) break;
614 : /* check mask */
615 0 : if (NCInstBytesByteInline(&andopcode, 2) !=
616 0 : (0x0ff & ~vstate->bundle_mask)) break;
617 : /* All checks look good. Make the sequence 'atomic.' */
618 0 : ForgetInstructionBoundary(dinst, vstate);
619 : /* as a courtesy, check call alignment correctness */
620 0 : if (modrm_regInline(mrm) == 2) ValidateCallAlignment(dinst);
621 0 : return;
622 : } while (0);
623 0 : NCBadInstructionError(dinst, "Unsafe indirect jump");
624 0 : NCStatsUnsafeIndirect(vstate);
625 0 : }
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 0 : static Bool ValidatePrefixes(const NCDecoderInst *dinst) {
661 0 : if (dinst->inst.prefixbytes == 0) return TRUE;
662 :
663 0 : if ((dinst->inst.prefixmask &
664 : BadPrefixMask[dinst->opinfo->insttype]) != 0) {
665 0 : 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 0 : if ((dinst->inst.opcode_prefixmask != 0) &&
672 : ((dinst->inst.prefixmask &
673 : (kPrefixDATA16 | kPrefixREPNE | kPrefixREP)) != 0)) {
674 0 : return FALSE;
675 : }
676 :
677 : /* Only allow a lock if it is a 386L instruction, or the special
678 : * cmpxchg8b instruction.
679 : */
680 0 : if (dinst->inst.prefixmask & kPrefixLOCK) {
681 0 : if ((dinst->opinfo->insttype != NACLi_386L) &&
682 : (dinst->opinfo->insttype != NACLi_CMPXCHG8B)) {
683 0 : return FALSE;
684 : }
685 0 : }
686 :
687 : /* Only allow more than one prefix if two prefixes, and they are
688 : * data 16 and lock.
689 : */
690 0 : 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 0 : return FALSE;
696 : }
697 :
698 0 : return TRUE;
699 0 : }
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 0 : static Bool ValidateInst(const NCDecoderInst *dinst) {
710 0 : NaClCPUFeaturesX86 *cpufeatures;
711 0 : int squashme = 0;
712 0 : NCValidatorState* vstate;
713 0 : if (dinst == NULL) return TRUE;
714 0 : vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
715 :
716 0 : OpcodeHisto(NCInstBytesByteInline(&dinst->inst_bytes,
717 : dinst->inst.prefixbytes),
718 : vstate);
719 : /* For testing only, this mode disables inter-instruction checks. */
720 0 : if (!NACL_FLAG_unsafe_single_inst32_mode) {
721 0 : RememberInstructionBoundary(dinst, vstate);
722 0 : }
723 :
724 0 : cpufeatures = &vstate->cpufeatures;
725 :
726 0 : if (!ValidatePrefixes(dinst)) {
727 0 : NCBadInstructionError(dinst, "Bad prefix usage");
728 0 : NCStatsBadPrefix(vstate);
729 0 : }
730 :
731 0 : if ((dinst->opinfo->insttype != NACLi_NOP) &&
732 : ((size_t) (dinst->inst.bytes.length - dinst->inst.prefixbytes)
733 : > kMaxValidInstLength)) {
734 0 : NCBadInstructionError(dinst, "Instruction too long");
735 0 : NCStatsBadInstLength(vstate);
736 0 : }
737 :
738 0 : 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 0 : break;
745 : case NACLi_LAHF:
746 : if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_TARGET_SUBARCH == 64)
747 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LAHF);
748 0 : break;
749 : case NACLi_JMP8:
750 0 : ValidateJmp8(dinst);
751 0 : break;
752 : case NACLi_JMPZ:
753 0 : ValidateJmpz(dinst);
754 0 : break;
755 : case NACLi_INDIRECT:
756 0 : ValidateIndirect5(dinst);
757 0 : break;
758 : case NACLi_X87:
759 : case NACLi_X87_FSINCOS:
760 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87);
761 0 : break;
762 : case NACLi_SFENCE_CLFLUSH:
763 0 : squashme = ValidateSFenceClFlush(dinst);
764 0 : 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 0 : if (modrm_modInline(dinst->inst.mrm)
771 : == kModRmModFieldDefinesRegisterRef) {
772 0 : NCBadInstructionError(dinst, "Illegal instruction");
773 0 : NCStatsIllegalInst(vstate);
774 0 : }
775 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CX8);
776 0 : break;
777 : case NACLi_CMOV:
778 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV);
779 0 : break;
780 : case NACLi_FCMOV:
781 0 : squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_CMOV) &&
782 0 : NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_x87));
783 0 : break;
784 : case NACLi_RDTSC:
785 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_TSC);
786 0 : break;
787 : case NACLi_MMX:
788 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX);
789 0 : 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 0 : squashme = !(NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MMX) ||
794 0 : NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2));
795 0 : break;
796 : case NACLi_SSE:
797 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE);
798 0 : break;
799 : case NACLi_SSE2:
800 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2);
801 0 : break;
802 : case NACLi_SSE3:
803 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE3);
804 0 : break;
805 : case NACLi_SSE4A:
806 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE4A);
807 0 : break;
808 : case NACLi_SSE41:
809 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE41);
810 0 : break;
811 : case NACLi_SSE42:
812 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE42);
813 0 : break;
814 : case NACLi_MOVBE:
815 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_MOVBE);
816 0 : break;
817 : case NACLi_POPCNT:
818 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_POPCNT);
819 0 : break;
820 : case NACLi_LZCNT:
821 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_LZCNT);
822 0 : break;
823 : case NACLi_SSSE3:
824 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSSE3);
825 0 : break;
826 : case NACLi_3DNOW:
827 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_3DNOW);
828 0 : break;
829 : case NACLi_E3DNOW:
830 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_E3DNOW);
831 0 : 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 0 : if (!(dinst->inst.opcode_prefixmask & kPrefixDATA16)) {
842 0 : NCBadInstructionError(dinst, "Bad prefix usage");
843 0 : NCStatsBadPrefix(vstate);
844 0 : }
845 0 : squashme = !NaClGetCPUFeatureX86(cpufeatures, NaClCPUFeatureX86_SSE2);
846 0 : break;
847 :
848 : case NACLi_RETURN:
849 0 : NCBadInstructionError(dinst, "ret instruction (not allowed)");
850 0 : 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 0 : NCBadInstructionError(dinst, "Illegal instruction");
867 0 : NCStatsIllegalInst(vstate);
868 0 : break;
869 : }
870 : case NACLi_UNDEFINED: {
871 0 : NCBadInstructionError(dinst, "Undefined instruction");
872 0 : NCStatsIllegalInst(vstate);
873 0 : NCStatsInternalError(vstate);
874 0 : break;
875 : }
876 : default:
877 0 : NCBadInstructionError(dinst, "Undefined instruction type");
878 0 : NCStatsInternalError(vstate);
879 0 : break;
880 : }
881 0 : if (squashme) {
882 0 : if (vstate->readonly_text) {
883 0 : NCBadInstructionError(dinst,
884 : "Illegal instruction for fixed-feature CPU mode");
885 0 : NCStatsIllegalInst(vstate);
886 0 : } else {
887 0 : NCStubOutMem(vstate, dinst->dstate->memory.mpc,
888 : dinst->dstate->memory.read_length);
889 : }
890 0 : }
891 0 : return TRUE;
892 0 : }
893 :
894 0 : Bool UnsafePartialValidateInst(const NCDecoderInst *dinst) {
895 0 : NCValidatorState *vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
896 0 : Bool result = FALSE;
897 :
898 0 : NACL_FLAG_unsafe_single_inst32_mode = TRUE;
899 0 : NCStatsInit(vstate);
900 0 : if (ValidateInst(dinst)) {
901 0 : result = vstate->stats.sawfailure == 0;
902 0 : };
903 0 : NACL_FLAG_unsafe_single_inst32_mode = FALSE;
904 0 : 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 0 : static void ValidateIndirect5Replacement(NCDecoderInst *dinst_old,
913 0 : NCDecoderInst *dinst_new) {
914 0 : do {
915 : /* check that the and-guard is 3 bytes and bit-for-bit identical */
916 0 : NCDecoderInst *andinst_old = PreviousInst(dinst_old, 1);
917 0 : NCDecoderInst *andinst_new = PreviousInst(dinst_new, 1);
918 0 : if ((andinst_old == NULL) || (andinst_old->inst.bytes.length != 3)) break;
919 0 : if ((andinst_new == NULL) || (andinst_new->inst.bytes.length != 3)) break;
920 0 : if (memcmp(andinst_old->inst.bytes.byte,
921 0 : andinst_new->inst.bytes.byte, 3) != 0) break;
922 :
923 : /* check that the indirect-jmp is 2 bytes and bit-for-bit identical */
924 0 : if (dinst_old->inst.bytes.length != 2) break;
925 0 : if (dinst_new->inst.bytes.length != 2) break;
926 0 : if (memcmp(dinst_old->inst.bytes.byte,
927 0 : dinst_new->inst.bytes.byte, 2) != 0) break;
928 :
929 0 : return;
930 : } while (0);
931 0 : NCBadInstructionError(dinst_new,
932 : "Replacement indirect jump must match original");
933 0 : NCStatsUnsafeIndirect(NCVALIDATOR_STATE_DOWNCAST(dinst_new->dstate));
934 0 : }
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 0 : static Bool ValidateInstReplacement(NCDecoderStatePair* tthis,
941 0 : NCDecoderInst *dinst_old,
942 0 : NCDecoderInst *dinst_new) {
943 0 : dinst_new->unchanged = memcmp(dinst_old->inst.bytes.byte,
944 : dinst_new->inst.bytes.byte,
945 : dinst_new->inst.bytes.length) == 0;
946 0 : ValidateInst(dinst_new);
947 :
948 0 : if (dinst_old->opinfo->insttype == NACLi_INDIRECT
949 : || dinst_new->opinfo->insttype == NACLi_INDIRECT) {
950 : /* Verify that nacljmps never change */
951 0 : ValidateIndirect5Replacement(dinst_old, dinst_new);
952 0 : }
953 0 : return TRUE;
954 : }
955 :
956 : /* Create the decoder state for the validator state, using the
957 : * given parameters.
958 : */
959 0 : static void NCValidateDStateInit(NCValidatorState *vstate,
960 0 : uint8_t *mbase, NaClPcAddress vbase,
961 0 : NaClMemorySize sz) {
962 0 : 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 0 : NaClErrorReporter* reporter = dstate->error_reporter;
968 0 : NCDecoderStateConstruct(dstate, mbase, vbase, sz,
969 : vstate->inst_buffer, kNCValidatorInstBufferSize);
970 0 : dstate->action_fn = ValidateInst;
971 0 : dstate->new_segment_fn = (NCDecoderStateMethod) NCStatsNewSegment;
972 0 : dstate->segmentation_error_fn = (NCDecoderStateMethod) NCStatsSegFault;
973 0 : dstate->internal_error_fn = (NCDecoderStateMethod) NCStatsInternalError;
974 0 : NCDecoderStateSetErrorReporter(dstate, reporter);
975 0 : }
976 :
977 0 : void NCValidateSegment(uint8_t *mbase, NaClPcAddress vbase, NaClMemorySize sz,
978 0 : struct NCValidatorState *vstate) {
979 : /* Sanity checks */
980 : /* TODO(ncbray): remove redundant vbase/size args. */
981 0 : if ((vbase & vstate->bundle_mask) != 0) {
982 0 : ValidatePrintOffsetError(0, "Bad vbase alignment", vstate);
983 0 : NCStatsSegFault(vstate);
984 0 : return;
985 : }
986 0 : if (vbase != vstate->iadrbase) {
987 0 : ValidatePrintOffsetError(0, "Mismatched vbase addresses", vstate);
988 0 : NCStatsSegFault(vstate);
989 0 : return;
990 : }
991 0 : if (sz != vstate->codesize) {
992 0 : ValidatePrintOffsetError(0, "Mismatched code size", vstate);
993 0 : NCStatsSegFault(vstate);
994 0 : return;
995 : }
996 :
997 0 : sz = NCHaltTrimSize(mbase, sz, vstate->bundle_size);
998 0 : vstate->codesize = sz;
999 :
1000 0 : if (sz == 0) {
1001 0 : ValidatePrintOffsetError(0, "Bad text segment (zero size)", vstate);
1002 0 : NCStatsSegFault(vstate);
1003 0 : return;
1004 : }
1005 0 : NCValidateDStateInit(vstate, mbase, vbase, sz);
1006 0 : NCDecoderStateDecode(&vstate->dstate);
1007 0 : NCDecoderStateDestruct(&vstate->dstate);
1008 0 : }
1009 :
1010 0 : int NCValidateSegmentPair(uint8_t *mbase_old, uint8_t *mbase_new,
1011 0 : NaClPcAddress vbase, size_t sz,
1012 0 : const NaClCPUFeaturesX86 *features) {
1013 : /* TODO(karl): Refactor to use inheritance from NCDecoderStatePair? */
1014 0 : NCDecoderStatePair pair;
1015 0 : NCValidatorState* new_vstate;
1016 0 : NCValidatorState* old_vstate;
1017 :
1018 0 : int result = 0;
1019 :
1020 : /* Verify that we actually have a segment to walk. */
1021 0 : if (sz == 0) {
1022 0 : printf("VALIDATOR: %"NACL_PRIxNaClPcAddress
1023 : ": Bad text segment (zero size)\n", vbase);
1024 0 : return 0;
1025 : }
1026 :
1027 0 : old_vstate = NCValidateInit(vbase, sz, FALSE, features);
1028 0 : if (old_vstate != NULL) {
1029 0 : NCValidateDStateInit(old_vstate, mbase_old, vbase, sz);
1030 0 : new_vstate = NCValidateInit(vbase, sz, FALSE, features);
1031 0 : if (new_vstate != NULL) {
1032 0 : NCValidateDStateInit(new_vstate, mbase_new, vbase, sz);
1033 :
1034 0 : NCDecoderStatePairConstruct(&pair,
1035 : &old_vstate->dstate,
1036 : &new_vstate->dstate,
1037 : NULL); /* copy_func */
1038 0 : pair.action_fn = ValidateInstReplacement;
1039 0 : if (NCDecoderStatePairDecode(&pair)) {
1040 0 : result = 1;
1041 0 : } else {
1042 0 : ValidatePrintOffsetError(0, "Replacement not applied!\n", new_vstate);
1043 : }
1044 0 : if (NCValidateFinish(new_vstate)) {
1045 : /* Errors occurred during validation. */
1046 0 : result = 0;
1047 0 : }
1048 0 : NCDecoderStatePairDestruct(&pair);
1049 0 : NCDecoderStateDestruct(&new_vstate->dstate);
1050 0 : NCValidateFreeState(&new_vstate);
1051 0 : }
1052 0 : NCDecoderStateDestruct(&old_vstate->dstate);
1053 0 : NCValidateFreeState(&old_vstate);
1054 0 : }
1055 0 : return result;
1056 0 : }
1057 :
1058 : /* Walk the collected information on instruction boundaries and jump targets,
1059 : * and verify that they are legal.
1060 : */
1061 0 : static void NCJumpSummarize(struct NCValidatorState* vstate) {
1062 0 : 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 0 : dprint(("CheckTargets: %"NACL_PRIxNaClPcAddress"-%"NACL_PRIxNaClPcAddress"\n",
1068 : vstate->iadrbase, vstate->iadrbase+vstate->codesize));
1069 0 : for (offset = 0; offset < vstate->codesize; offset += 1) {
1070 0 : if (NCGetAdrTable(offset, vstate->kttable)) {
1071 0 : NCStatsCheckTarget(vstate);
1072 0 : if (!NCGetAdrTable(offset, vstate->vttable)) {
1073 0 : ValidatePrintOffsetError(offset, "Bad jump target", vstate);
1074 0 : NCStatsBadTarget(vstate);
1075 0 : }
1076 0 : }
1077 0 : }
1078 :
1079 : /* check basic block boundaries */
1080 0 : for (offset = 0; offset < vstate->codesize; offset += vstate->bundle_size) {
1081 0 : if (!NCGetAdrTable(offset, vstate->vttable)) {
1082 0 : ValidatePrintOffsetError(offset, "Bad basic block alignment", vstate);
1083 0 : NCStatsBadAlignment(vstate);
1084 0 : }
1085 0 : }
1086 0 : }
|