static void ValidateJmpz(const NCDecoderInst *dinst) { NCInstBytesPtr opcode; uint8_t opcode0; int32_t offset; NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate); NCInstBytesPtrInitInc(&opcode, &dinst->inst_bytes, dinst->inst.prefixbytes); opcode0 = NCInstBytesByteInline(&opcode, 0); NCStatsCheckTarget(vstate); if (opcode0 == 0x0f) { /* Multbyte opcode. Intruction is of form: * 0F80 .. 0F8F: jCC $Jz */ NCInstBytesPtr opcode_2; NCInstBytesPtrInitInc(&opcode_2, &opcode, 2); offset = NCInstBytesInt32(&opcode_2, dinst->inst.immbytes); } else { /* Single byte opcode. Must be one of: * E8: call $Jz * E9: jmp $Jx */ NCInstBytesPtr opcode_1; NCInstBytesPtrInitInc(&opcode_1, &opcode, 1); offset = NCInstBytesInt32(&opcode_1, dinst->inst.immbytes); /* as a courtesy, check call alignment correctness */ if (opcode0 == 0xe8) ValidateCallAlignment(dinst); } RememberJumpTarget(dinst, offset, vstate); }
int64_t NCInstBytesInt64(const NCInstBytesPtr* ptr, int num_bytes) { switch (num_bytes) { case 1: case 2: case 3: /* Handle special case of Iw, Ib in 32 bit validator. */ case 4: return (int64_t) NCInstBytesInt32(ptr, num_bytes); case 6: { /* Handle special case of 48-bit pointers in 32 bit validator. */ NCInstBytesPtr ptr_plus_2; NCInstBytesPtrInitInc(&ptr_plus_2, ptr, 2); return ((int64_t) (NCInstBytesInt32(&ptr_plus_2, 2)) << 32) | ((int64_t) (NCInstBytesInt32(ptr, 4))); } case 8: { NCInstBytesPtr ptr_plus_4; NCInstBytesPtrInitInc(&ptr_plus_4, ptr, 4); return ((int64_t) (NCInstBytesInt32(&ptr_plus_4, 4)) << 32) | ((int64_t) (NCInstBytesInt32(ptr, 4))); } default: CHECK(0); /* Fail -- should not happen. */ return -1; } }
/* * 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); }
/* disassembler stuff */ static const char* DisFmt(const NCDecoderInst *dinst) { NCInstBytesPtr opbyte; uint8_t opbyte0; uint8_t opbyte1; uint8_t pm = dinst->inst.opcode_prefixmask; NCInstBytesPtrInitInc(&opbyte, &dinst->inst_bytes, NCOpcodeOffset(dinst)); opbyte0 = NCInstBytesByte(&opbyte, 0); if (dinst->opinfo->insttype == NACLi_X87 || dinst->opinfo->insttype == NACLi_X87_FSINCOS) { if (opbyte0 != kWAITOp) { return kDisasmX87Op[opbyte0 - kFirstX87Opcode][dinst->inst.mrm]; } } if (dinst->opinfo->insttype == NACLi_FCMOV) { return kDisasmX87Op[opbyte0 - kFirstX87Opcode][dinst->inst.mrm]; } if (dinst->opinfo->insttype == NACLi_NOP) return "nop"; if (opbyte0 != kTwoByteOpcodeByte1) return kDisasm1ByteOp[opbyte0]; opbyte1 = NCInstBytesByte(&opbyte, 1); if (opbyte1 == 0x0f) { return kDisasm0F0FOp[ NCInstBytesByte(&opbyte, dinst->inst.bytes.length - 1)]; } if (opbyte1 == 0x38) { return kDisasm0F38Op[NCInstBytesByte(&opbyte, 2)]; } if (opbyte1 == 0x3A) { return kDisasm0F3AOp[NCInstBytesByte(&opbyte, 2)]; } if (! (pm & (kPrefixDATA16 | kPrefixREPNE | kPrefixREP))) { return kDisasm0FXXOp[opbyte1]; } if (pm & kPrefixDATA16) return kDisasm660FXXOp[opbyte1]; if (pm & kPrefixREPNE) return kDisasmF20FXXOp[opbyte1]; if (pm & kPrefixREP) return kDisasmF30FXXOp[opbyte1]; /* no update; should be invalid */ return "internal error"; }
/* Returns the (sign extended) 32-bit integer displacement value. */ static int32_t DispValue32(const NCDecoderInst* dinst) { NCInstBytesPtr addr; NCInstBytesPtrInitInc(&addr, &dinst->inst_bytes, NCDispOffset(dinst)); return NCInstBytesInt32(&addr, dinst->inst.dispbytes); }
/* Returns the (sign extended) 64-bit integer immediate value. */ static int64_t ImmedValue64(const NCDecoderInst* dinst) { NCInstBytesPtr addr; NCInstBytesPtrInitInc(&addr, &dinst->inst_bytes, NCImmedOffset(dinst)); return NCInstBytesInt64(&addr, dinst->inst.immbytes); }