/* Print out the register (from the list of register names), * that is referenced by the decoded instruction in the * decoder state. If is_gp_regs is true, the register names * correspond to general purpose register names. Otherwise, * they are some other set, such as MMX or XMM. */ static void RegMemPrint(const NCDecoderInst *dinst, const char* reg_names[], const uint8_t is_gp_regs, struct Gio* fp) { DEBUG( printf( "reg mem print: sib_offset = %d, " "disp_offset = %d, mrm.mod = %02x\n", NCSibOffset(dinst), (int) NCDispOffset(dinst), modrm_modInline(dinst->inst.mrm)) ); switch (modrm_modInline(dinst->inst.mrm)) { case 0: SegPrefixPrint(dinst, fp); if (NaClHasBit(dinst->inst.prefixmask, kPrefixADDR16)) { NaClIllegalOp(fp); } else { if (4 == modrm_rmInline(dinst->inst.mrm)) { SibPrint(dinst, fp); } else if (5 == modrm_rmInline(dinst->inst.mrm)) { gprintf(fp, "[0x%x]", DispValue32(dinst)); } else { gprintf(fp, "[%s]", gp_regs[modrm_rmInline(dinst->inst.mrm)]); } } break; case 1: SegPrefixPrint(dinst, fp); if (NaClHasBit(dinst->inst.prefixmask, kPrefixADDR16)) { NaClIllegalOp(fp); } else { gprintf(fp, "0x%x", DispValue32(dinst)); if (4 == modrm_rmInline(dinst->inst.mrm)) { SibPrint(dinst, fp); } else { gprintf(fp, "[%s]", gp_regs[modrm_rmInline(dinst->inst.mrm)]); } } break; case 2: SegPrefixPrint(dinst, fp); if (NaClHasBit(dinst->inst.prefixmask, kPrefixADDR16)) { NaClIllegalOp(fp); } else { gprintf(fp, "0x%x", DispValue32(dinst)); if (4 == modrm_rmInline(dinst->inst.mrm)) { SibPrint(dinst, fp); } else { gprintf(fp, "[%s]", gp_regs[modrm_rmInline(dinst->inst.mrm)]); } } break; case 3: if (is_gp_regs) { gprintf(fp, "%s", reg_names[state_modrm_reg(dinst)]); } else { gprintf(fp, "%s", reg_names[modrm_rmInline(dinst->inst.mrm)]); } break; } }
/* * The NaCl five-byte safe indirect calling sequence looks like this: * 83 e0 e0 and $0xe0,%eax * ff d0 call *%eax * The call may be replaced with a ff e0 jmp. Any register may * be used, not just eax. The validator requires exactly this * sequence. * Note: The code above assumes 32-bit alignment. Change e0 as appropriate * if a different alignment is used. */ static void ValidateIndirect5(const NCDecoderInst *dinst) { NCInstBytesPtr jmpopcode; NCInstBytesPtr andopcode; uint8_t mrm; uint8_t targetreg; const uint8_t kReg_ESP = 4; NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); struct NCDecoderInst *andinst = PreviousInst(dinst, 1); if ((andinst == NULL) || (andinst->inst.bytes.length != 3)) { NCBadInstructionError(dinst, "Unsafe indirect jump"); NCStatsUnsafeIndirect(vstate); return; } /* note: no prefixbytes allowed */ NCInstBytesPtrInitInc(&jmpopcode, &dinst->inst_bytes, 0); /* note: no prefixbytes allowed */ NCInstBytesPtrInitInc(&andopcode, &andinst->inst_bytes, 0); mrm = NCInstBytesByteInline(&jmpopcode, 1); /* Note that the modrm_rm field holds the * target addr the modrm_reg is the opcode. */ targetreg = modrm_rmInline(mrm); NCStatsCheckTarget(vstate); NCStatsTargetIndirect(vstate); do { /* no prefix bytes allowed */ if (dinst->inst.prefixbytes != 0) break; if (dinst->inst.prefixbytes != 0) break; /* Check all the opcodes. */ /* In GROUP5, 2 => call, 4 => jmp */ if (NCInstBytesByteInline(&jmpopcode, 0) != 0xff) break; if ((modrm_regInline(mrm) != 2) && (modrm_regInline(mrm) != 4)) break; /* Issue 32: disallow unsafe call/jump indirection */ /* example: ff 12 call (*edx) */ /* Reported by defend.the.world on 11 Dec 2008 */ if (modrm_modInline(mrm) != 3) break; if (targetreg == kReg_ESP) break; if (NCInstBytesByteInline(&andopcode, 0) != 0x83) break; /* check modrm bytes of or and and instructions */ if (NCInstBytesByteInline(&andopcode, 1) != (0xe0 | targetreg)) break; /* check mask */ if (NCInstBytesByteInline(&andopcode, 2) != (0x0ff & ~vstate->bundle_mask)) break; /* All checks look good. Make the sequence 'atomic.' */ ForgetInstructionBoundary(dinst, vstate); /* as a courtesy, check call alignment correctness */ if (modrm_regInline(mrm) == 2) ValidateCallAlignment(dinst); return; } while (0); NCBadInstructionError(dinst, "Unsafe indirect jump"); NCStatsUnsafeIndirect(vstate); }
/* It returns 0 if the current instruction is implemented, and 1 if not. */ static int ValidateSFenceClFlush(const NCDecoderInst *dinst) { NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); uint8_t mrm = NCInstBytesByteInline(&dinst->inst_bytes, 2); if (modrm_modInline(mrm) == 3) { /* this is an sfence */ if (NaClGetCPUFeature(&vstate->cpufeatures, NaClCPUFeature_FXSR)) return 0; return 1; } else { /* this is an clflush */ if (NaClGetCPUFeature(&vstate->cpufeatures, NaClCPUFeature_CLFLUSH)) return 0; return 1; } }
static Bool ValidateInst(const NCDecoderInst *dinst) { NaClCPUFeaturesX86 *cpufeatures; int squashme = 0; NCValidatorState* vstate; if (dinst == NULL) return TRUE; vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); OpcodeHisto(NCInstBytesByteInline(&dinst->inst_bytes, dinst->inst.prefixbytes), vstate); /* For testing only, this mode disables inter-instruction checks. */ if (!NACL_FLAG_unsafe_single_inst32_mode) { RememberInstructionBoundary(dinst, vstate); } cpufeatures = &(vstate->cpufeatures); if (!ValidatePrefixes(dinst)) { NCBadInstructionError(dinst, "Bad prefix usage"); NCStatsBadPrefix(vstate); } if ((dinst->opinfo->insttype != NACLi_NOP) && ((size_t) (dinst->inst.bytes.length - dinst->inst.prefixbytes) > kMaxValidInstLength)) { NCBadInstructionError(dinst, "Instruction too long"); NCStatsBadInstLength(vstate); } switch (dinst->opinfo->insttype) { case NACLi_NOP: case NACLi_386: case NACLi_386L: case NACLi_386R: case NACLi_386RE: break; case NACLi_JMP8: ValidateJmp8(dinst); break; case NACLi_JMPZ: ValidateJmpz(dinst); break; case NACLi_INDIRECT: ValidateIndirect5(dinst); break; case NACLi_X87: case NACLi_X87_FSINCOS: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_x87); break; case NACLi_SFENCE_CLFLUSH: squashme = ValidateSFenceClFlush(dinst); break; case NACLi_CMPXCHG8B: /* Only allow if the modrm mod field accesses memory. * This stops us from accepting f00f on multiple bytes. * http://en.wikipedia.org/wiki/Pentium_F00F_bug */ if (modrm_modInline(dinst->inst.mrm) == kModRmModFieldDefinesRegisterRef) { NCBadInstructionError(dinst, "Illegal instruction"); NCStatsIllegalInst(vstate); } squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_CX8); break; case NACLi_CMOV: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_CMOV); break; case NACLi_FCMOV: squashme = !(NaClGetCPUFeature(cpufeatures, NaClCPUFeature_CMOV) && NaClGetCPUFeature(cpufeatures, NaClCPUFeature_x87)); break; case NACLi_RDTSC: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_TSC); break; case NACLi_MMX: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_MMX); break; case NACLi_MMXSSE2: /* Note: We accept these instructions if either MMX or SSE2 bits */ /* are set, in case MMX instructions go away someday... */ squashme = !(NaClGetCPUFeature(cpufeatures, NaClCPUFeature_MMX) || NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE2)); break; case NACLi_SSE: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE); break; case NACLi_SSE2: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE2); break; case NACLi_SSE3: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE3); break; case NACLi_SSE4A: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE4A); break; case NACLi_SSE41: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE41); break; case NACLi_SSE42: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE42); break; case NACLi_MOVBE: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_MOVBE); break; case NACLi_POPCNT: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_POPCNT); break; case NACLi_LZCNT: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_LZCNT); break; case NACLi_SSSE3: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSSE3); break; case NACLi_3DNOW: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_3DNOW); break; case NACLi_E3DNOW: squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_E3DNOW); break; case NACLi_SSE2x: /* This case requires CPUID checking code */ /* Note: DATA16 prefix required. The generated table * for group 14 (which the only 2 SSE2x instructions are in), * allows instructions with and without a 66 prefix. However, * the SSE2x instructions psrldq and pslldq are only allowed * with the 66 prefix. Hence, this code has been added to * do this check. */ if (!(dinst->inst.opcode_prefixmask & kPrefixDATA16)) { NCBadInstructionError(dinst, "Bad prefix usage"); NCStatsBadPrefix(vstate); } squashme = !NaClGetCPUFeature(cpufeatures, NaClCPUFeature_SSE2); break; case NACLi_RETURN: NCBadInstructionError(dinst, "ret instruction (not allowed)"); NCStatsReturn(vstate); /* ... and fall through to illegal instruction code */ case NACLi_EMMX: /* EMMX needs to be supported someday but isn't ready yet. */ case NACLi_INVALID: case NACLi_ILLEGAL: case NACLi_SYSTEM: case NACLi_RDMSR: case NACLi_RDTSCP: case NACLi_SYSCALL: case NACLi_SYSENTER: case NACLi_LONGMODE: case NACLi_SVM: case NACLi_OPINMRM: case NACLi_3BYTE: case NACLi_CMPXCHG16B: { NCBadInstructionError(dinst, "Illegal instruction"); NCStatsIllegalInst(vstate); break; } case NACLi_UNDEFINED: { NCBadInstructionError(dinst, "Undefined instruction"); NCStatsIllegalInst(vstate); NCStatsInternalError(vstate); break; } default: NCBadInstructionError(dinst, "Undefined instruction type"); NCStatsInternalError(vstate); break; } if (squashme) { if (vstate->readonly_text) { NCBadInstructionError(dinst, "Illegal instruction for fixed-feature CPU mode"); NCStatsIllegalInst(vstate); } else { NCStubOutMem(vstate, dinst->dstate->memory.mpc, dinst->dstate->memory.read_length); } } return TRUE; }
/* Accessors for the ModRm byte. */ uint8_t modrm_mod(uint8_t modrm) { return modrm_modInline(modrm); }