Validator: print message for unsafe indirects
[nativeclient.git] / ncv / ncvalidate.c
blob6d98c50ab40f9fe58430e5a4ba8c8bc1b2d8dd6d
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * ncvalidate.c
34 * Validate x86 instructions for Native Client
37 #include "native_client/include/portability.h"
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <assert.h>
43 #include "ncdecode.h"
44 #include "ncvalidate_internaltypes.h"
45 #include "nacl_cpuid.h"
46 #include "native_client/service_runtime/sel_ldr.h"
48 /* debugging stuff */
49 #define DEBUGGING 0
50 #if DEBUGGING
51 #define dprint(args) printf args
52 #else
53 #define dprint(args)
54 #endif /* DEBUGGING */
56 /* TODO verbosity needs to be controllable via commandline flags */
57 #define VERBOSE 1
58 #if VERBOSE
59 #define vprint(args) printf args
60 #else
61 #define vprint(args)
62 #endif /* VERBOSE */
64 /* opcode histogram */
65 #if VERBOSE == 1
66 static void OpcodeHisto(const uint8_t byte1, struct NCValidatorState *vstate) {
67 vstate->opcodehisto[byte1] += 1;
70 static void InitOpcodeHisto(struct NCValidatorState *vstate) {
71 int i;
72 for (i = 0; i < 256; i += 1) vstate->opcodehisto[i] = 0;
75 static void PrintOpcodeHisto(FILE *f, struct NCValidatorState *vstate) {
76 int i;
77 if (!VERBOSE) return;
78 fprintf(f, "\nOpcode Histogram;\n");
79 for (i = 0; i < 256; i += 4) {
80 if (vstate->opcodehisto[i] != 0) {
81 fprintf(f, "%d\t0x%02x\t%d\t0x%02x\t%d\t0x%02x\t%d\t0x%02x\n",
82 vstate->opcodehisto[i], i, vstate->opcodehisto[i+1], i+1,
83 vstate->opcodehisto[i+2], i+2, vstate->opcodehisto[i+3], i+3);
87 #else
88 #define OpcodeHisto(b, v)
89 #define InitOpcodeHisto(v)
90 #define PrintOpcodeHisto(f, v)
91 #endif /* VERBOSE == 1 */
93 /* statistics code */
94 static void Stats_Inst(struct NCValidatorState *vstate) {
95 vstate->stats.instructions += 1;
98 static void Stats_CheckTarget(struct NCValidatorState *vstate) {
99 vstate->stats.checktarget += 1;
102 static void Stats_TargetIndirect(struct NCValidatorState *vstate) {
103 vstate->stats.targetindirect += 1;
106 static void Stats_SawFailure(struct NCValidatorState *vstate) {
107 vstate->stats.sawfailure = 1;
110 static void Stats_InternalError(struct NCValidatorState *vstate) {
111 vstate->stats.internalerrors += 1;
112 Stats_SawFailure(vstate);
115 static void Stats_BadCPU(struct NCValidatorState *vstate) {
116 vstate->stats.badcpu += 1;
117 Stats_SawFailure(vstate);
120 static void Stats_BadAlignment(struct NCValidatorState *vstate) {
121 vstate->stats.badalignment += 1;
122 Stats_SawFailure(vstate);
125 static void Stats_SegFault(struct NCValidatorState *vstate) {
126 vstate->stats.segfaults += 1;
127 Stats_SawFailure(vstate);
130 static void Stats_NewSegment(struct NCValidatorState *vstate) {
131 vstate->stats.segments += 1;
132 if (vstate->stats.segments > 1) {
133 vprint(("error: multiple segments\n"));
134 Stats_SawFailure(vstate);
138 static void Stats_BadTarget(struct NCValidatorState *vstate) {
139 vstate->stats.badtarget += 1;
140 Stats_SawFailure(vstate);
143 static void Stats_UnsafeIndirect(struct NCValidatorState *vstate) {
144 vstate->stats.unsafeindirect += 1;
145 Stats_SawFailure(vstate);
148 static void Stats_MissingFullStop(struct NCValidatorState *vstate) {
149 vstate->stats.missingfullstop += 1;
150 Stats_SawFailure(vstate);
153 static void Stats_Return(struct NCValidatorState *vstate) {
154 vstate->stats.returns += 1;
155 Stats_UnsafeIndirect(vstate);
156 Stats_SawFailure(vstate);
159 static void Stats_IllegalInst(struct NCValidatorState *vstate) {
160 vstate->stats.illegalinst += 1;
161 Stats_SawFailure(vstate);
164 static void Stats_BadPrefix(struct NCValidatorState *vstate) {
165 vstate->stats.badprefix += 1;
166 Stats_SawFailure(vstate);
169 static void Stats_BadInstLength(struct NCValidatorState *vstate) {
170 vstate->stats.badinstlength += 1;
171 Stats_SawFailure(vstate);
174 static void Stats_Init(struct NCValidatorState *vstate) {
175 static int inited = 0;
176 if (inited) return;
178 vstate->stats.instructions = 0;
179 vstate->stats.segments = 0;
180 vstate->stats.checktarget = 0;
181 vstate->stats.targetindirect = 0;
182 vstate->stats.badtarget = 0;
183 vstate->stats.unsafeindirect = 0;
184 vstate->stats.returns = 0;
185 vstate->stats.illegalinst = 0;
186 vstate->stats.badalignment = 0;
187 vstate->stats.internalerrors = 0;
188 vstate->stats.badcpu = 0;
189 vstate->stats.missingfullstop = 0;
190 vstate->stats.badinstlength = 0;
191 vstate->stats.badprefix = 0;
192 vstate->stats.sawfailure = 0;
193 InitOpcodeHisto(vstate);
194 inited = 1;
197 void Stats_Print(FILE *f, struct NCValidatorState *vstate) {
198 if (!VERBOSE) return;
199 if (vstate == NULL) {
200 fprintf(f, "Analysis Summary: invalid module or internal failure\n");
201 return;
203 PrintOpcodeHisto(f, vstate);
204 fprintf(f, "Analysis Summary:\n");
205 fprintf(f, "%d Checked instructions\n", vstate->stats.instructions);
206 fprintf(f, "%d checked jump targets\n", vstate->stats.checktarget);
207 fprintf(f, "%d calls/jumps need dynamic checking (%0.2f%%)\n",
208 vstate->stats.targetindirect,
209 vstate->stats.instructions ?
210 100.0 * vstate->stats.targetindirect/vstate->stats.instructions : 0);
211 fprintf(f, "\nProblems:\n");
212 fprintf(f, "%d illegal instructions\n", vstate->stats.illegalinst);
213 fprintf(f, "%d bad jump targets\n", vstate->stats.badtarget);
214 fprintf(f, "%d illegal unprotected indirect jumps (including ret)\n",
215 vstate->stats.unsafeindirect);
216 fprintf(f, "%d instruction alignment defects\n",
217 vstate->stats.badalignment);
218 fprintf(f, "%d segmentation errors\n",
219 vstate->stats.segfaults);
220 fprintf(f, "%d bad prefix\n",
221 vstate->stats.badprefix);
222 fprintf(f, "%d bad instruction length\n",
223 vstate->stats.badinstlength);
224 fprintf(f, "%d missing full stop\n",
225 vstate->stats.missingfullstop);
226 fprintf(f, "%d internal errors\n",
227 vstate->stats.internalerrors);
228 fprintf(f, "%d bad cpu\n",
229 vstate->stats.badcpu);
232 /***********************************************************************/
233 /* jump target table */
234 static const uint8_t iadrmasks[8] = {0x01, 0x02, 0x04, 0x08,
235 0x10, 0x20, 0x40, 0x80};
236 #define IATOffset(__IA) ((__IA) >> 3)
237 #define IATMask(__IA) (iadrmasks[(__IA) & 0x7])
238 #define SetAdrTable(__IOFF, __TABLE) \
239 (__TABLE)[IATOffset(__IOFF)] |= IATMask(__IOFF)
240 #define ClearAdrTable(__IOFF, __TABLE) \
241 (__TABLE)[IATOffset(__IOFF)] &= ~(IATMask(__IOFF))
242 #define GetAdrTable(__IOFF, __TABLE) \
243 ((__TABLE)[IATOffset(__IOFF)] & IATMask(__IOFF))
245 /* forward declaration, for registration */
246 void ValidateInst(const struct NCDecoderState *mstate);
248 /* In general we are quite paranoid about what prefixes can */
249 /* be used and where. For one-byte opcodes, prefixes are */
250 /* restricted based on the NACLi_ type and the masks in */
251 /* BadPrefixMask. For two-byte opcodes, any */
252 /* prefix can be used, but they function more to define the */
253 /* opcode as opposed to modify it; hence there are separate */
254 /* tables in ncdecodetab.h for the four allowed prefix bytes. */
255 static uint32_t BadPrefixMask[kNaClInstTypeRange];
256 static void InitBadPrefixMask() {
257 int i;
259 for (i = 0; i < kNaClInstTypeRange; i++) {
260 BadPrefixMask[i] = 0xffffffff; /* all prefixes are bad */
262 BadPrefixMask[NACLi_386] = ~(kPrefixDATA16 | kPrefixSEGGS);
263 BadPrefixMask[NACLi_386L] = ~(kPrefixDATA16 | kPrefixSEGGS | kPrefixLOCK);
264 BadPrefixMask[NACLi_386R] = ~(kPrefixDATA16 | kPrefixREP);
265 BadPrefixMask[NACLi_386RE] = ~(kPrefixDATA16 | kPrefixREP | kPrefixREPNE);
266 BadPrefixMask[NACLi_JMP8] = ~(kPrefixSEGCS | kPrefixSEGDS);
267 BadPrefixMask[NACLi_JMPZ] = ~(kPrefixSEGCS | kPrefixSEGDS);
268 BadPrefixMask[NACLi_CMPXCHG8B] = ~kPrefixLOCK;
272 * NCValidateInit: Initialize NaCl validator internal state
273 * Parameters:
274 * vbase: base virtual address for code segment
275 * vlimit: size in bytes of code segment
276 * alignment: 16 or 32, specifying alignment
277 * Returns:
278 * an initialized struct NCValidatorState * if everything is okay,
279 * else NULL
281 struct NCValidatorState *NCValidateInit(const uint32_t vbase,
282 const uint32_t vlimit,
283 const uint8_t alignment) {
284 uint32_t alignbase = vbase & (~alignment);
285 struct NCValidatorState *vstate;
287 dprint(("NCValidateInit(%08x, %08x, %08x)\n", vbase, vlimit, alignment));
288 InitBadPrefixMask();
289 do {
290 if (vlimit <= vbase) break;
291 if (alignment != 16 && alignment != 32) break;
292 dprint(("ncv_init(%x, %x)\n", vbase, vlimit));
293 vstate = (struct NCValidatorState *)calloc(1, sizeof(*vstate));
294 if (vstate == NULL) break;
295 vstate->iadrbase = alignbase;
296 vstate->iadrlimit = vlimit;
297 vstate->alignment = alignment;
298 vstate->alignmask = alignment-1;
299 vstate->vttable = (uint8_t *)calloc(IATOffset(vlimit - alignbase) + 1, 1);
300 vstate->kttable = (uint8_t *)calloc(IATOffset(vlimit - alignbase) + 1, 1);
301 if (vstate->vttable == NULL || vstate->kttable == NULL) break;
302 dprint((" allocated tables\n"));
303 Stats_Init(vstate);
304 NCDecodeRegisterCallbacks(ValidateInst, Stats_NewSegment,
305 Stats_SegFault, Stats_InternalError);
306 return vstate;
307 } while (0);
308 /* failure */
309 return NULL;
312 static void RememberIP(const uint32_t ip, struct NCValidatorState *vstate) {
313 uint32_t ioffset = ip - vstate->iadrbase;
314 if (ip < vstate->iadrbase || ip >= vstate->iadrlimit) {
315 vprint(("VALIDATOR: JUMP TARGET %08x out of range in RememberIP\n", ip));
316 Stats_BadTarget(vstate);
317 return;
319 if (GetAdrTable(ioffset, vstate->vttable)) {
320 vprint(("RememberIP: Saw inst at %08x twice\n", ip));
321 Stats_InternalError(vstate);
322 return;
324 Stats_Inst(vstate);
325 SetAdrTable(ioffset, vstate->vttable);
328 static void RememberTP(const uint32_t src, uint32_t target,
329 struct NCValidatorState *vstate) {
330 uint32_t ioffset = target - vstate->iadrbase;
332 do {
333 if (target < vstate->iadrlimit) {
334 if (target >= vstate->iadrbase) break;
336 * the trampolines need to be aligned 0mod32 regardless of the
337 * program's elf flags. This allows the same library to be used
338 * with both 16 and 32 byte aligned clients.
340 if (target >= vstate->iadrbase - NACL_TRAMPOLINE_SIZE
341 && ((target & (NACL_INSTR_BLOCK_SIZE-1)) == 0)) {
343 * TODO: once we fully support 16/32 alignment, remove this
344 * in favor of however we communicate the fixed block size.
346 /* this is an aligned target in the trampoline area; ignore */
347 /* vprint(("ignoring target %08x in trampoline\n", target)); */
348 return;
351 vprint(("VALIDATOR: %08x: JUMP TARGET %08x out of range\n", src, target));
352 Stats_BadTarget(vstate);
353 return;
354 } while (0);
355 SetAdrTable(ioffset, vstate->kttable);
358 static void ForgetIP(const uint32_t ip,
359 struct NCValidatorState *vstate) {
360 uint32_t ioffset = ip - vstate->iadrbase;
361 if (ip < vstate->iadrbase || ip >= vstate->iadrlimit) {
362 vprint(("VALIDATOR: JUMP TARGET %08x out of range in ForgetIP\n", ip));
363 Stats_BadTarget(vstate);
364 return;
366 ClearAdrTable(ioffset, vstate->vttable);
369 int NCValidateFinish(struct NCValidatorState *vstate) {
370 uint32_t offset;
371 if (vstate == NULL) {
372 vprint(("validator not initialized. Did you call ncvalidate_init()?\n"));
373 /* non-zero indicates failure */
374 return 1;
376 dprint(("CheckTargets: %x-%x\n", vstate->iadrbase, vstate->iadrlimit));
377 for (offset = 0;
378 offset < vstate->iadrlimit - vstate->iadrbase;
379 offset += 1) {
380 if (GetAdrTable(offset, vstate->kttable)) {
381 /* printf("CheckTarget %x\n", offset + iadrbase); */
382 Stats_CheckTarget(vstate);
383 if (!GetAdrTable(offset, vstate->vttable)) {
384 Stats_BadTarget(vstate);
385 vprint(("VALIDATOR: Bad jump target at %x\n", vstate->iadrbase + offset));
389 /* check basic block boundaries */
390 if (vstate->iadrbase & vstate->alignmask) {
391 vprint(("VALIDATOR: Bad base address alignment 0x%08x\n", vstate->iadrbase));
392 Stats_BadAlignment(vstate);
394 for (offset = 0; offset < vstate->iadrlimit - vstate->iadrbase;
395 offset += vstate->alignment) {
396 if (!GetAdrTable(offset, vstate->vttable)) {
397 vprint(("VALIDATOR: Bad basic block alignment at %08x\n",
398 vstate->iadrbase + offset));
399 Stats_BadAlignment(vstate);
402 fflush(stdout);
404 /* Now that all the work is done, generate return code. */
405 /* Return zero if there are no problems. */
406 return (vstate->stats.sawfailure);
409 void NCValidateFreeState(struct NCValidatorState **vstate) {
410 if (*vstate == NULL) return;
411 free((*vstate)->vttable);
412 free((*vstate)->kttable);
413 free(*vstate);
414 *vstate = NULL;
417 static void ValidateCallAlignment(const struct NCDecoderState *mstate) {
418 uint32_t fallthru = mstate->inst.vaddr + mstate->inst.length;
419 if (fallthru & mstate->vstate->alignmask) {
420 printf("VALIDATOR: bad call alignment at %x\n", mstate->inst.vaddr);
421 /* This makes bad call alignment a fatal error. */
422 Stats_BadAlignment(mstate->vstate);
426 static void ValidateJmp8(const struct NCDecoderState *mstate) {
427 uint8_t opcode = (uint8_t)mstate->inst.maddr[mstate->inst.prefixbytes];
428 int8_t offset = (int8_t)mstate->inst.maddr[mstate->inst.prefixbytes+1];
429 uint32_t target = mstate->inst.vaddr + mstate->inst.length + offset;
430 Stats_CheckTarget(mstate->vstate);
431 if ((opcode & 0xf0) == 0x70 || opcode == 0xeb ||
432 opcode == 0xe0 || opcode == 0xe1 || opcode == 0xe2 || opcode == 0xe3) {
433 RememberTP(mstate->inst.vaddr, target, mstate->vstate);
434 } else {
435 /* If this ever happens, it's probably a decoder bug. */
436 vprint(("ERROR: JMP8 %x: %x\n", mstate->inst.vaddr, opcode));
437 Stats_InternalError(mstate->vstate);
441 static void ValidateJmpz(const struct NCDecoderState *mstate) {
442 uint8_t *opcode = mstate->inst.maddr + mstate->inst.prefixbytes;
443 int32_t offset;
444 uint32_t target;
445 Stats_CheckTarget(mstate->vstate);
446 if (*opcode == 0xe8 || *opcode == 0xe9) {
447 offset = *(int32_t *)&opcode[1];
448 target = mstate->inst.vaddr + mstate->inst.length + offset;
449 RememberTP(mstate->inst.vaddr, target, mstate->vstate);
450 /* as a courtesy, check call alignment correctness */
451 if (*opcode == 0xe8) ValidateCallAlignment(mstate);
452 } else if (*opcode == 0x0f) {
453 if ((opcode[1] & 0xf0) == 0x80) {
454 offset = *(int32_t *)&opcode[2];
455 target = mstate->inst.vaddr + mstate->inst.length + offset;
456 RememberTP(mstate->inst.vaddr, target, mstate->vstate);
458 } else {
459 /* If this ever happens, it's probably a decoder bug. */
460 vprint(("ERROR: JMPZ %x: %x %x\n",
461 mstate->inst.vaddr, opcode[0], opcode[1]));
462 Stats_InternalError(mstate->vstate);
467 * The NaCl 14-byte safe indirect calling sequence looks like this:
468 * 81 e0 ff ff ff ff and $0xffffffff,%eax
469 * 81 c8 00 00 00 00 or $0x0,%eax
470 * ff d0 call *%eax
471 * The call may be replaced with a ff e0 jmp. Any register may
472 * be used, not just eax. The validator requires exactly this
473 * sequence.
474 * TODO: validate or write the masks.
476 #ifdef NACLJMP14
477 NOTE: This routine has not been kept up to date, and should be updated
478 with respect to the five-byte version (below) if we need to use it
479 again someday.
480 static void ValidateIndirect14(const struct NCDecoderState *mstate) {
481 uint8_t *jmpopcode;
482 uint8_t *oropcode;
483 uint8_t *andopcode;
484 uint8_t mrm;
485 uint8_t reg;
487 struct NCDecoderState *andinst = PreviousInst(mstate, -2);
488 struct NCDecoderState *orinst = PreviousInst(mstate, -1);
489 assert(andinst != NULL);
490 assert(orinst != NULL);
491 if ((andinst->inst.length == 0) || (orinst->inst.length == 0)) {
492 Stats_UnsafeIndirect(mstate->vstate);
493 return;
495 /* no prefix bytes allowed; checked below */
496 jmpopcode = mstate->inst.maddr;
497 oropcode = orinst->inst.maddr;
498 andopcode = andinst->inst.maddr;
499 mrm = jmpopcode[1];
500 reg = modrm_rm(mrm);
502 Stats_CheckTarget(mstate->vstate);
503 Stats_TargetIndirect(mstate->vstate);
504 do {
505 /* check no prefix bytes */
506 if (mstate->inst.prefixbytes != 0) break;
507 if (andinst->inst.prefixbytes != 0) break;
508 if (orinst->inst.prefixbytes != 0) break;
509 /* check all the opcodes. */
510 if (jmpopcode[0] != 0xff) break;
511 if ((modrm_reg(mrm) != 2) && (modrm_reg(mrm) != 4)) break;
512 /* Issue 32: disallow unsafe call/jump indirection */
513 /* example: ff 12 call (*edx) */
514 /* Reported by defend.the.world on 11 Dec 2008 */
515 if (modrm_mod(mrm) != 3) break;
516 if (oropcode[0] != 0x81) break;
517 if (andopcode[0] != 0x81) break;
518 /* check modrm bytes of or and and instructions */
519 if (andopcode[1] != (0xe0 | reg)) break;
520 if (oropcode[1] != (0xc8 | reg)) break;
521 /* check mask */
522 assert(0); /* not implemented yet, so don't use this routine! */
523 /* All checks look good. Make the sequence 'atomic.' */
524 ForgetIP(orinst->inst.vaddr, mstate->vstate);
525 ForgetIP(mstate->inst.vaddr, mstate->vstate);
526 return;
527 } while (0);
528 Stats_UnsafeIndirect(mstate->vstate);
530 #endif
533 * The NaCl five-byte safe indirect calling sequence looks like this:
534 * 83 e0 f0 and $0xfffffff0,%eax
535 * ff d0 call *%eax
536 * The call may be replaced with a ff e0 jmp. Any register may
537 * be used, not just eax. The validator requires exactly this
538 * sequence.
539 * TODO: validate or write the masks.
541 static void ValidateIndirect5(const struct NCDecoderState *mstate) {
542 uint8_t *jmpopcode;
543 uint8_t *andopcode;
544 uint8_t mrm;
545 uint8_t targetreg;
546 const uint8_t kReg_ESP = 4;
548 struct NCDecoderState *andinst = PreviousInst(mstate, -1);
549 assert(andinst != NULL);
550 if (andinst->inst.length == 0) {
551 Stats_UnsafeIndirect(mstate->vstate);
552 return;
554 jmpopcode = mstate->inst.maddr; /* note: no prefixbytes allowed */
555 andopcode = andinst->inst.maddr; /* note: no prefixbytes allowed */
556 mrm = jmpopcode[1];
557 targetreg = modrm_rm(mrm); /* Note that the modrm_rm field holds the */
558 /* target addr the modrm_reg is the opcode. */
560 Stats_CheckTarget(mstate->vstate);
561 Stats_TargetIndirect(mstate->vstate);
562 do {
563 /* no prefix bytes allowed */
564 if (mstate->inst.prefixbytes != 0) break;
565 if (andinst->inst.prefixbytes != 0) break;
566 /* Check all the opcodes. */
567 /* In GROUP5, 2 => call, 4 => jmp */
568 if (jmpopcode[0] != 0xff) break;
569 if ((modrm_reg(mrm) != 2) && (modrm_reg(mrm) != 4)) break;
570 /* Issue 32: disallow unsafe call/jump indirection */
571 /* example: ff 12 call (*edx) */
572 /* Reported by defend.the.world on 11 Dec 2008 */
573 if (modrm_mod(mrm) != 3) break;
574 if (targetreg == kReg_ESP) break;
575 if (andopcode[0] != 0x83) break;
576 /* check modrm bytes of or and and instructions */
577 if (andopcode[1] != (0xe0 | targetreg)) break;
578 /* check mask */
579 if (andopcode[2] != (0x0ff & ~mstate->vstate->alignmask)) break;
580 /* All checks look good. Make the sequence 'atomic.' */
581 ForgetIP(mstate->inst.vaddr, mstate->vstate);
582 /* as a courtesy, check call alignment correctness */
583 if (modrm_reg(mrm) == 2) ValidateCallAlignment(mstate);
584 return;
585 } while (0);
586 Stats_UnsafeIndirect(mstate->vstate);
587 vprint(("VALIDATOR: %08x: unsafe indirect\n", mstate->inst.vaddr));
590 /* It appears to me that none of my favorite test programs use more */
591 /* than a single prefix byte on an instruction. It would be nice if */
592 /* we could make this a requirement. */
593 static const size_t kMaxValidInstLength = 11;
594 static const uint8_t kNaClFullStop = 0xf4; /* x86 HALT opcode */
596 void ValidateInst(const struct NCDecoderState *mstate) {
597 const int kMaxValPrefixBytes = 2;
598 CPUFeatures *cpufeatures;
599 int squashme = 0;
601 /* dprint(("ValidateInst(%x, %x) at %x\n",
602 (uint32_t)mstate, (uint32_t)mstate->vstate, mstate->inst.vaddr)); */
603 if (mstate == NULL) return;
604 if (mstate->vstate == NULL) return;
605 OpcodeHisto(mstate->inst.maddr[mstate->inst.prefixbytes],
606 mstate->vstate);
607 RememberIP(mstate->inst.vaddr, mstate->vstate);
608 cpufeatures = &(mstate->vstate->cpufeatures);
609 do {
610 if (mstate->inst.prefixbytes == 0) break;
611 if (mstate->inst.prefixbytes <= kMaxValPrefixBytes) {
612 if (mstate->inst.hasopbyte2) {
613 if (mstate->inst.prefixmask & kPrefixLOCK) {
614 /* For two byte opcodes, check for use of the lock prefix. */
615 if (mstate->opinfo->insttype == NACLi_386L) break;
616 if (mstate->opinfo->insttype == NACLi_CMPXCHG8B) break;
617 } else {
618 /* Other prefixes checks are encoded in the two-byte tables. */
619 break;
621 } else {
622 /* one byte opcode */
623 if ((mstate->inst.prefixmask &
624 BadPrefixMask[mstate->opinfo->insttype]) == 0) break;
627 vprint(("VALIDATOR: BadPrefix at %08x, length %i\n", mstate->inst.vaddr,
628 mstate->inst.prefixbytes));
629 Stats_BadPrefix(mstate->vstate);
630 } while (0);
631 if ((size_t) (mstate->inst.length - mstate->inst.prefixbytes)
632 > kMaxValidInstLength) {
633 Stats_BadInstLength(mstate->vstate);
635 switch (mstate->opinfo->insttype) {
636 case NACLi_386:
637 case NACLi_386L:
638 case NACLi_386R:
639 case NACLi_386RE:
640 break;
641 case NACLi_JMP8:
642 ValidateJmp8(mstate);
643 break;
644 case NACLi_JMPZ:
645 ValidateJmpz(mstate);
646 break;
647 case NACLi_INDIRECT:
648 ValidateIndirect5(mstate);
649 break;
650 case NACLi_X87:
651 squashme = (!cpufeatures->f_x87);
652 break;
653 case NACLi_CFLUSH:
654 squashme = (!cpufeatures->f_CFLUSH);
655 break;
656 case NACLi_CMPXCHG8B:
657 squashme = (!cpufeatures->f_CX8);
658 break;
659 case NACLi_CMOV:
660 squashme = (!cpufeatures->f_CMOV);
661 break;
662 case NACLi_FCMOV:
663 squashme = (!(cpufeatures->f_CMOV && cpufeatures->f_x87));
664 break;
665 case NACLi_RDTSC:
666 squashme = (!cpufeatures->f_TSC);
667 break;
668 case NACLi_MMX:
669 squashme = (!cpufeatures->f_MMX);
670 break;
671 case NACLi_MMXSSE2:
672 /* Note: We accept these instructions if either MMX or SSE2 bits */
673 /* are set, in case MMX instructions go away someday... */
674 squashme = (!(cpufeatures->f_MMX || cpufeatures->f_SSE2));
675 break;
676 case NACLi_SSE:
677 squashme = (!cpufeatures->f_SSE);
678 break;
679 case NACLi_SSE2:
680 squashme = (!cpufeatures->f_SSE2);
681 break;
682 case NACLi_SSE3:
683 squashme = (!cpufeatures->f_SSE3);
684 break;
685 case NACLi_SSE4A:
686 squashme = (!cpufeatures->f_SSE4A);
687 break;
688 case NACLi_POPCNT:
689 squashme = (!cpufeatures->f_POPCNT);
690 break;
691 case NACLi_LZCNT:
692 squashme = (!cpufeatures->f_LZCNT);
693 break;
694 case NACLi_SSSE3:
695 squashme = (!cpufeatures->f_SSSE3);
696 break;
697 case NACLi_3DNOW:
698 squashme = (!cpufeatures->f_3DNOW);
699 break;
700 case NACLi_E3DNOW:
701 squashme = (!cpufeatures->f_E3DNOW);
702 break;
703 case NACLi_SSE2x:
704 /* This case requires CPUID checking code */
705 /* DATA16 prefix required */
706 if (!(mstate->inst.prefixmask & kPrefixDATA16)) {
707 Stats_BadPrefix(mstate->vstate);
709 squashme = (!cpufeatures->f_SSE2);
710 break;
712 case NACLi_RETURN:
713 Stats_Return(mstate->vstate);
714 /* ... and fall through to illegal instruction code */
715 case NACLi_EMMX:
716 case NACLi_SSE41:
717 case NACLi_SSE42:
718 /* EMMX, SSE41 and SSE42 need to be supported someday but */
719 /* aren't ready yet */
720 case NACLi_INVALID:
721 case NACLi_ILLEGAL:
722 case NACLi_SYSTEM:
723 case NACLi_RDMSR:
724 case NACLi_RDTSCP:
725 case NACLi_SYSCALL:
726 case NACLi_SYSENTER:
727 case NACLi_LONGMODE:
728 case NACLi_SVM:
729 case NACLi_OPINMRM:
730 case NACLi_3BYTE:
731 case NACLi_CMPXCHG16B:
733 uint8_t *opcode = mstate->inst.maddr + mstate->inst.prefixbytes;
734 Stats_IllegalInst(mstate->vstate);
735 if (VERBOSE) {
736 printf("VALIDATOR: illegal inst at %x (%02x)\n", mstate->inst.vaddr, *opcode);
738 break;
740 case NACLi_UNDEFINED:
742 uint8_t *opcode = mstate->inst.maddr + mstate->inst.prefixbytes;
743 Stats_IllegalInst(mstate->vstate);
744 Stats_InternalError(mstate->vstate);
745 if (VERBOSE) {
746 printf("VALIDATOR: undefined inst at %x (%02x)\n", mstate->inst.vaddr, *opcode);
748 break;
750 /* Note that by omitting a default: case the compiler checks */
751 /* that all cases are covered. */
753 /* Disable this for now because we are mmapping code read-only. */
754 #if 0
755 if (squashme) memset(mstate->inst.maddr, kNaClFullStop, mstate->inst.length);
756 #endif
759 void NCValidateSegment(uint8_t *mbase, uint32_t vbase, size_t sz,
760 struct NCValidatorState *vstate) {
761 if (sz == 0) {
762 Stats_MissingFullStop(vstate);
763 Stats_SegFault(vstate);
764 return;
766 GetCPUFeatures(&(vstate->cpufeatures));
767 /* The name of the flag is misleading; f_386 requires not just */
768 /* 386 instructions but also the CPUID instruction is supported. */
769 if (!vstate->cpufeatures.f_386) {
770 Stats_BadCPU(vstate);
771 return;
773 #if (0)
774 /* TODO: enable this check */
775 if (!vstate->cpufeatures.f_whitelisted) {
776 Stats_BadCPU(vstate);
777 return;
779 #endif
781 NCDecodeSegment(mbase, vbase, sz-1, vstate);
782 if (mbase[sz-1] != kNaClFullStop) {
783 Stats_MissingFullStop(vstate);