Example #1
0
/*
 * 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);
}
Example #2
0
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);
}
Example #3
0
static void ValidateJmp8(const NCDecoderInst *dinst) {
  int8_t offset = NCInstBytesByteInline(&dinst->inst_bytes,
                                        dinst->inst.prefixbytes+1);
  struct NCValidatorState* vstate = NCVALIDATOR_STATE_DOWNCAST(dinst->dstate);
  NCStatsCheckTarget(vstate);
  RememberJumpTarget(dinst, offset, vstate);
}
Example #4
0
int32_t NCInstBytesInt32(const NCInstBytesPtr* ptr, int num_bytes) {
  switch (num_bytes) {
    case 1:
      return (int8_t) NCInstBytesByteInline(ptr, 0);
    case 2:
      return (int16_t) (NCInstBytesByteInline(ptr, 0) +
                        (NCInstBytesByteInline(ptr, 1) << 8));
    case 3:
      /* Note: Handle special case of Iw, Ib in 32 bit validator. */
      return (int32_t) (NCInstBytesByteInline(ptr, 0) +
                        (NCInstBytesByteInline(ptr, 1) << 8) +
                        (NCInstBytesByteInline(ptr, 2) << 16));
    case 4:
      return (int32_t) (NCInstBytesByteInline(ptr, 0) +
                        (NCInstBytesByteInline(ptr, 1) << 8) +
                        (NCInstBytesByteInline(ptr, 2) << 16) +
                        (NCInstBytesByteInline(ptr, 3) << 24));
    default:
      CHECK(0); /* Fail -- should not happen. */
      return -1;
  }
}
Example #5
0
/* 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;
  }
}
Example #6
0
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;
}
Example #7
0
uint8_t NCInstBytesByte(const NCInstBytesPtr* ptr, int n) {
  return NCInstBytesByteInline(ptr, n);
}