/* 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);
}