/* Inspect the parsed instruction to print out the opcode sequence matched. */ static void NaClInstPrintOpcodeSeq(struct Gio* gout, const NaClInstState* state) { size_t count = 0; if (state->num_opcode_bytes == 0) { /* Hard coded bytes sequence for instruction. */ gprintf(gout, " %s", kHardCodedMessage); count = strlen(kHardCodedMessage) + 2; } else { /* Modeled instruction. Pull out parsed opcode bytes from parsed * instruction. */ int i; gprintf(gout, " "); count = 1; /* Add prefix selector if applicable. */ if (state->opcode_prefix) { gprintf(gout, " %02x", state->opcode_prefix); count += 3; } /* Add opcode bytes. */ for (i = 0; i < state->num_opcode_bytes; ++i) { gprintf(gout, " %02x", state->bytes.byte[state->num_prefix_bytes + i]); count += 3; } if (state->inst->flags & NACL_IFLAG(OpcodeInModRm)) { gprintf(gout, " / %d", modrm_opcode(state->modrm)); count += 4; } else if (state->inst->flags & NACL_IFLAG(OpcodePlusR)) { gprintf(gout, " - r%d", NaClGetOpcodePlusR(state->inst->opcode_ext)); count += 5; } if (state->inst->flags & NACL_IFLAG(OpcodeInModRmRm)) { gprintf(gout, " / %d", modrm_rm(state->modrm)); count += 4; } /* Add opcode for 0f0f instructions, where the opcode is the last * byte of the instruction. */ if ((state->num_opcode_bytes >= 2) && (0 == (state->inst->flags & NACL_IFLAG(Opcode0F0F))) && (0x0F == state->bytes.byte[state->num_prefix_bytes]) && (0x0F == state->bytes.byte[state->num_prefix_bytes + 1])) { gprintf(gout, " %02x", state->bytes.byte[state->bytes.length - 1]); count += 3; } } while (count < 30) { gprintf(gout, " "); ++count; } }
/* * The NaCl five-byte safe indirect calling sequence looks like this: * 83 e0 f0 and $0xfffffff0,%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. * TODO(brad): validate or write the masks. */ static void ValidateIndirect5(const struct NCDecoderState *mstate) { uint8_t *jmpopcode; uint8_t *andopcode; uint8_t mrm; uint8_t targetreg; const uint8_t kReg_ESP = 4; struct NCDecoderState *andinst = PreviousInst(mstate, -1); assert(andinst != NULL); if (andinst->inst.length == 0) { BadInstructionError(mstate, "Unsafe indirect jump"); Stats_UnsafeIndirect(mstate->vstate); return; } jmpopcode = mstate->inst.maddr; /* note: no prefixbytes allowed */ andopcode = andinst->inst.maddr; /* note: no prefixbytes allowed */ mrm = jmpopcode[1]; targetreg = modrm_rm(mrm); /* Note that the modrm_rm field holds the */ /* target addr the modrm_reg is the opcode. */ Stats_CheckTarget(mstate->vstate); Stats_TargetIndirect(mstate->vstate); do { /* no prefix bytes allowed */ if (mstate->inst.prefixbytes != 0) break; if (andinst->inst.prefixbytes != 0) break; /* Check all the opcodes. */ /* In GROUP5, 2 => call, 4 => jmp */ if (jmpopcode[0] != 0xff) break; if ((modrm_reg(mrm) != 2) && (modrm_reg(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_mod(mrm) != 3) break; if (targetreg == kReg_ESP) break; if (andopcode[0] != 0x83) break; /* check modrm bytes of or and and instructions */ if (andopcode[1] != (0xe0 | targetreg)) break; /* check mask */ if (andopcode[2] != (0x0ff & ~mstate->vstate->alignmask)) break; /* All checks look good. Make the sequence 'atomic.' */ ForgetIP(mstate->inst.vaddr, mstate->vstate); /* as a courtesy, check call alignment correctness */ if (modrm_reg(mrm) == 2) ValidateCallAlignment(mstate); return; } while (0); BadInstructionError(mstate, "Unsafe indirect jump"); Stats_UnsafeIndirect(mstate->vstate); }