/* Returns true if the 64-bit register reg64 set by an instruction of the form
 *
 *    add %reg64 %RBASE
 *
 * The instruction checked is the "distance" instruction from the current
 * instruction being looked at by the specified iterator.
 */
static Bool NaClIsAddRbaseToReg64(NaClValidatorState* vstate,
                                  size_t distance,
                                  NaClOpKind reg64) {
  NaClInstState* state;
  const NaClInst* inst;
  int op_1, op_2;
  NaClExpVector* nodes;
  NaClExp* node;
  NaClOpKind reg;
  NaClInstIter* iter = vstate->cur_iter;

  /* Get the corresponding instruction. */
  if (!NaClInstIterHasLookbackStateInline(iter, distance)) return FALSE;
  state = NaClInstIterGetLookbackStateInline(iter, distance);
  inst = NaClInstStateInst(state);
  if (InstAdd != inst->name) return FALSE;
  DEBUG(NaClLog(LOG_INFO, "inst(%d): add rbase: ", (int) distance);
        NaClInstStateInstPrint(NaClLogGetGio(), state));

  /* Extract the values of the two operands for the and. */
  if (!NaClExtractBinaryOperandIndices(state, &op_1, &op_2)) return FALSE;

  /* Extract the destination register of the and. */
  nodes = NaClInstStateExpVector(state);
  node = &nodes->node[op_1];
  if (ExprRegister != node->kind) return FALSE;

  /* Check that destination register matches wanted register. */
  reg = NaClGetExpRegisterInline(node);
  if (reg != reg64) return FALSE;

  /* Check that source register is the base register. */
  return NaClGetExpVectorRegister(nodes, op_2) == vstate->base_register;
}
Beispiel #2
0
/* Print out the given expression node to the given file.
 * Returns the index of the node following the given indexed
 * expression.
 */
static int NaClPrintDisassembledExp(struct Gio* file,
                                    NaClInstState* state,
                                    uint32_t index) {
  NaClExp* node;
  NaClExpVector* vector = NaClInstStateExpVector(state);
  assert(index < vector->number_expr_nodes);
  node = &vector->node[index];
  switch (node->kind) {
    default:
      gprintf(file, "undefined");
      return index + 1;
    case ExprRegister:
      NaClPrintDisassembledReg(file, node);
      return index + 1;
    case OperandReference:
      return NaClPrintDisassembledExp(file, state, index + 1);
    case ExprConstant:
      NaClPrintDisassembledConst(file, state, node);
      return index + 1;
    case ExprSegmentAddress:
      return NaClPrintDisassembledSegmentAddr(file, state, index);
    case ExprMemOffset:
      return NaClPrintDisassembledMemOffset(file, state, index);
    case ExprNaClIllegal:
      gprintf(file, "*NaClIllegal*");
      return index + 1;
  }
}
Beispiel #3
0
/* Print out the given (segment address) expression node to the
 * given file. Returns the index of the node following the
 * given (indexed) segment address.
 */
static int NaClPrintDisassembledSegmentAddr(struct Gio* file,
                                            NaClInstState* state,
                                            int index) {
  int memory_address;
  NaClExpVector* vector = NaClInstStateExpVector(state);
  /* If segment register is default. If so, do not print. */
  if (IsSegmentAddressDsRegPair(state, index) ||
      IsSegmentAddressEsRegPair(state, index)) {
    /* Segment register matches default.  Don't print. */
  } else {
    /* Print the segment register associated with the segment address. */
    NaClPrintDisassembledExp(file, state, index + 1);
    gprintf(file, ":");
  }
  memory_address = NaClGetExpKidIndex(vector, index, 1);
  if (vector->node[memory_address].kind == ExprRegister) {
    /* Special case segment address, where the register corresponds to
     * a memory address. Print out the register in '[]' brackets to
     * communicate that it is a memory reference.
     */
    int result;
    gprintf(file, "[");
    result = NaClPrintDisassembledExp(file, state, memory_address);
    gprintf(file, "]");
    return result;
  } else {
    /* print out memory address associated with segment address. */
    return NaClPrintDisassembledExp(file, state, memory_address);
  }
}
Beispiel #4
0
void NaClExpVectorPrint(struct Gio* file, NaClInstState* state) {
  uint32_t i;
  NaClExpVector* vector = NaClInstStateExpVector(state);
  gprintf(file, "NaClExpVector[%d] = {\n", vector->number_expr_nodes);
  for (i = 0; i < vector->number_expr_nodes; i++) {
    NaClExp* node = &vector->node[i];
    gprintf(file, "  { %s[%d] , ",
            NaClExpKindName(node->kind),
            NaClExpKindRank(node->kind));
    switch (node->kind) {
      case ExprRegister:
        NaClPrintDisassembledReg(file, node);
        break;
      case ExprConstant:
        NaClPrintDisassembledConst(file, state, node);
        break;
      default:
        gprintf(file, "%"NACL_PRIu64, NaClGetExprUnsignedValue(node));
        break;
    }
    gprintf(file, ", ");
    NaClPrintExpFlags(file, node->flags);
    gprintf(file, " },\n");
  }
  gprintf(file, "};\n");
}
Beispiel #5
0
/* Retrurns true if the segment register of the index segment address is ES,
 * and ES has been marked (by the instruction) as the default register
 * for the segment address.
 */
static Bool IsSegmentAddressEsRegPair(NaClInstState* state,
                                      int index) {
  NaClExpVector* vector = NaClInstStateExpVector(state);
  NaClExp* segment_address = &vector->node[index];
  NaClExp* segment_register =
      &vector->node[NaClGetExpKidIndex(vector, index, 0)];
  return NaClHasBit(segment_address->flags, NACL_EFLAG(ExprESrCase)) &&
      (segment_register->kind == ExprRegister) &&
      (RegES == NaClGetExpRegisterInline(segment_register));
}
/* Returns the 32-bit register for instructions of the form
 *
 *    and %reg32, MASK
 *
 * where MASK is all 1/s except for the alignment mask bits, which must be zero.
 *
 * It is assumed that opcode 0x83 is used for the AND operation, and hence, the
 * mask is a single byte.
 *
 * Returns RegUnknown if the instruction doesn't match the form listed above.
 */
static NaClOpKind NaClGetAndMaskReg32(NaClValidatorState* vstate,
                                      size_t distance) {
  NaClInstState* state;
  const NaClInst* inst;
  int op_1, op_2;
  NaClExpVector* nodes;
  NaClExp* node;
  uint8_t mask;
  NaClOpKind reg32;
  NaClInstIter* iter = vstate->cur_iter;

  /* Get the corresponding and instruction. */
  if (!NaClInstIterHasLookbackStateInline(iter, distance)) return RegUnknown;
  state = NaClInstIterGetLookbackStateInline(iter, distance);
  inst = NaClInstStateInst(state);
  if ((InstAnd != inst->name) ||
      (state->num_opcode_bytes == 0) ||
      (0x83 != state->bytes.byte[state->num_prefix_bytes])) return RegUnknown;
  DEBUG(NaClLog(LOG_INFO, "inst(%d): and mask: ", (int) distance);
        NaClInstStateInstPrint(NaClLogGetGio(), state));

  /* Extract the values of the two operands for the and. */
  if (!NaClExtractBinaryOperandIndices(state, &op_1, &op_2)) return RegUnknown;

  /* Extract the destination register of the and. */
  nodes = NaClInstStateExpVector(state);
  node = &nodes->node[op_1];
  if (ExprRegister != node->kind) return RegUnknown;

  reg32 = NaClGetExpRegisterInline(node);
  DEBUG(NaClLog(LOG_INFO, "and mask reg = %s\n", NaClOpKindName(reg32)));

  /* Check that the mask is ok. */
  mask = NaClGetJumpMask(vstate);
  DEBUG(NaClLog(LOG_INFO, "mask = %"NACL_PRIx8"\n", mask));

  assert(0xf0 == mask || 0xe0 == mask); /* alignment must be either 16 or 32. */
  node = &nodes->node[op_2];
  /* Technically the operand is a signed value, but "mask" has not been sign
   * extended, so treat the value as an unsigned byte.
   */
  if (ExprConstant != node->kind || mask != NaClGetExprUnsignedValue(node))
    return RegUnknown;
  DEBUG(NaClLog(LOG_INFO, "is mask constant\n"));

  return reg32;
}
Bool NaClInstValidates(uint8_t* mbase,
                       uint8_t size,
                       NaClPcAddress vbase,
                       NaClInstStruct* inst) {
  NaClSegment segment;
  NaClValidatorState* state;
  Bool validates = FALSE;
  NaClCPUFeaturesX86 cpu_features;

  NaClGetCurrentCPUFeaturesX86((NaClCPUFeatures *) &cpu_features);
  NACL_FLAGS_unsafe_single_inst_mode = TRUE;
  state = NaClValidatorStateCreate(vbase, (NaClMemorySize) size, RegR15, FALSE,
                                   &cpu_features);
  do {
    NaClSegmentInitialize(mbase, vbase, (NaClMemorySize) size, &segment);
    NaClBaseRegisterMemoryInitialize(state);
    state->cur_iter = NaClInstIterCreate(kNaClDecoderTables, &segment);
    if (NULL == state->cur_iter) break;
    state->cur_inst_state = NaClInstIterGetState(state->cur_iter);
    state->cur_inst = NaClInstStateInst(state->cur_inst_state);
    state->cur_inst_vector = NaClInstStateExpVector(state->cur_inst_state);
    NaClValidateInstructionLegal(state);
    NaClBaseRegisterValidator(state);
    /* induce call to NaClMaybeReportPreviousBad() */
    NaClBaseRegisterSummarize(state);
    NaClMemoryReferenceValidator(state);
    NaClJumpValidator(state);
    validates = NaClValidatesOk(state);
    NaClInstIterDestroy(state->cur_iter);
    state->cur_iter = NULL;
    state->cur_inst_state = NULL;
    state->cur_inst = NULL;
    state->cur_inst_vector = NULL;
  } while(0);
  NaClValidatorStateDestroy(state);
  /* Strictly speaking this shouldn't be necessary, as the mode */
  /* should only be used from tests. Disabling it here as a     */
  /* defensive tactic. */
  NACL_FLAGS_unsafe_single_inst_mode = FALSE;
  return validates;
}
static Bool NaClExtractBinaryOperandIndices(
    NaClInstState* inst,
    int* op_1,
    int* op_2) {
  uint32_t index;
  NaClExpVector* nodes = NaClInstStateExpVector(inst);
  *op_1 = -1;
  *op_2 = -1;

  for (index = 0; index < nodes->number_expr_nodes; ++index) {
    if (OperandReference == nodes->node[index].kind) {
      if (-1 == *op_1) {
        *op_1 = index + 1;
      } else {
        *op_2 = index + 1;
        return TRUE;
      }
    }
  }
  return FALSE;
}
Beispiel #9
0
/* Print out the given (memory offset) expression node to the given file.
 * Returns the index of the node following the given (indexed) memory offset.
 */
static int NaClPrintDisassembledMemOffset(struct Gio* file,
                                      NaClInstState *state,
                                      int index) {
  NaClExpVector* vector = NaClInstStateExpVector(state);
  int r1_index = index + 1;
  int r2_index = r1_index + NaClExpWidth(vector, r1_index);
  int scale_index = r2_index + NaClExpWidth(vector, r2_index);
  int disp_index = scale_index + NaClExpWidth(vector, scale_index);
  NaClOpKind r1 = NaClGetExpVectorRegister(vector, r1_index);
  NaClOpKind r2 = NaClGetExpVectorRegister(vector, r2_index);
  uint64_t scale = NaClGetExprUnsignedValue(&vector->node[scale_index]);
  int64_t disp = NaClGetExprSignedValue(&vector->node[disp_index]);
  assert(ExprMemOffset == vector->node[index].kind);
  gprintf(file,"[");
  if (r1 != RegUnknown) {
    NaClPrintDisassembledRegKind(file, r1);
  }
  if (r2 != RegUnknown) {
    if (r1 != RegUnknown) {
      gprintf(file, "+");
    }
    NaClPrintDisassembledRegKind(file, r2);
    gprintf(file, "*%d", (uint32_t) scale);
  }
  if (disp != 0) {
    if ((r1 != RegUnknown || r2 != RegUnknown) &&
        !NaClIsExpNegativeConstant(vector, disp_index)) {
      gprintf(file, "+");
    }
    /* Recurse to handle print using format flags. */
    NaClPrintDisassembledExp(file, state, disp_index);
  } else if (r1 == RegUnknown && r2 == RegUnknown) {
    /* be sure to generate case: [0x0]. */
    NaClPrintDisassembledExp(file, state, disp_index);
  }
  gprintf(file, "]");
  return disp_index + NaClExpWidth(vector, disp_index);
}
static void NaClInstLayoutCheck(NaClValidatorState* vstate) {
  NaClPcAddress start;
  NaClPcAddress end;
  NaClPcAddress i;
  if (NULL == vstate->cur_inst_state) return;

  DEBUG(NaClLog(LOG_INFO, "Jump layout check: ");
        NaClInstStateInstPrint(NaClLogGetGio(), vstate->cur_inst_state));

  /* Check basic block boundaries. */
  start = vstate->cur_inst_state->inst_addr;

  /* Check that if first instruction in a basic block, it isn't in the
   * middle of a pattern.
   */
  if ((0 == (start & vstate->bundle_mask)) &&
      NaClAddressSetContains(vstate->jump_sets.removed_targets,
                             start, vstate)) {
    NaClValidatorInstMessage(
        LOG_ERROR, vstate, vstate->cur_inst_state,
        "Instruction begins basic block, but in middle of nacl pattern\n");
  }

  /* Check that instruction doesn't cross block boundaries. */
  end = (NaClPcAddress) (start + vstate->cur_inst_state->bytes.length);
  for (i = start + 1; i < end; ++i) {
    if (0 == (i & vstate->bundle_mask)) {
      NaClValidatorInstMessage(
          LOG_ERROR, vstate, vstate->cur_inst_state,
          "Instruction crosses basic block alignment\n");
    }
  }

  /* Check jump targets. */
  if (NaClHasBit(vstate->cur_inst_state->inst->flags,
                 NACL_IFLAG(JumpInstruction) | NACL_IFLAG(ConditionalJump))) {
    uint32_t i;
    NaClExpVector* vector = NaClInstStateExpVector(vstate->cur_inst_state);
    for (i = 0; i < vector->number_expr_nodes; ++i) {
      NaClExp* node = &vector->node[i];
      if (NaClHasBit(node->flags, NACL_EFLAG(ExprJumpTarget))
          && node->kind == ExprConstant) {
        /* Explicit jump value. Check if legal! */
        NaClPcAddress target = end +
            (NaClPcNumber) NaClGetExprSignedValue(node);
        /* Don't report targets that are out of range. They should have
         * been reported in the first pass!
         */
        if (NaClCheckAddressRange(target, vstate)) {
          if (NaClAddressSetContains(vstate->jump_sets.possible_targets,
                                     target, vstate)) {
            if (NaClAddressSetContains(vstate->jump_sets.removed_targets,
                                       target, vstate)) {
              NaClValidatorInstMessage(
                  LOG_ERROR, vstate, vstate->cur_inst_state,
                  "Jumps into middle of nacl pattern\n");
            }
          } else {
            NaClValidatorInstMessage(
                LOG_ERROR, vstate, vstate->cur_inst_state,
                "Doesn't jump to instruction address\n");
          }
        }
      }
    }
  }
}
Beispiel #11
0
/* Print the given instruction opcode of the give state, to the
 * given file.
 */
static void NaClPrintDisassembled(struct Gio* file,
                                  NaClInstState* state,
                                  const NaClInst* inst) {
  uint32_t tree_index = 0;
  Bool is_first = TRUE;
  Bool not_printed_prefix_segment = TRUE;
  NaClExp* node;
  NaClExpVector* vector = NaClInstStateExpVector(state);

  /* Print the name of the instruction. */
  if (NaClHasBit(inst->flags, NACL_IFLAG(PartialInstruction))) {
    /* Instruction has been simplified. Print out corresponding
     * hints to the reader, so that they know that the instruction
     * has been simplified.
     */
    gprintf(file, "[P] ");
    NaClPrintLower(file, (char*) NaClMnemonicName(inst->name));
    if (NaClHasBit(inst->flags, NACL_IFLAG(NaClIllegal))) {
      gprintf(file, "(illegal)");
    }
  } else {
    NaClPrintLower(file, (char*) NaClMnemonicName(inst->name));
  }

  /* Use the generated expression tree to print out (non-implicit) operands
   * of the instruction.
   */
  while (tree_index < vector->number_expr_nodes) {
    node = &vector->node[tree_index];
    if (node->kind != OperandReference ||
        (NACL_EMPTY_EFLAGS == (node->flags & NACL_EFLAG(ExprImplicit)))) {
      if (is_first) {
        gprintf(file, " ");
        is_first = FALSE;
      } else {
        gprintf(file, ", ");
      }
      NaClPrintDisassembledExp(file, state, tree_index);

      /* If this is a partial instruction, add set/use information
       * so that that it is more clear what was matched.
       */
      if (NaClHasBit(inst->flags, NACL_IFLAG(PartialInstruction)) &&
          node->kind == OperandReference) {
        const NaClOp* op =
            NaClGetInstOperandInline(state->decoder_tables,
                                     inst,
                                     (uint8_t) NaClGetExprUnsignedValue(node));
        if (NaClHasBit(op->flags, (NACL_OPFLAG(OpSet) |
                                   NACL_OPFLAG(OpUse) |
                                   NACL_OPFLAG(OperandZeroExtends_v)))) {
          gprintf(file, " (");
          NaClPrintAddOperandFlag(file, op, OpSet, "s");
          NaClPrintAddOperandFlag(file, op, OpUse, "u");
          NaClPrintAddOperandFlag(file, op, OperandZeroExtends_v, "z");
          gprintf(file, ")");
        }
      }
    } else if (not_printed_prefix_segment &&
               (OperandReference == node->kind) &&
               (node->flags & NACL_EFLAG(ExprImplicit))) {
      /* Print out segment override of implicit segment address, if
       * applicable.
       */
      if (OperandReference == node->kind) {
        int seg_addr_index = tree_index + 1;
        if (ExprSegmentAddress == vector->node[seg_addr_index].kind) {
          if (NaClHasSegmentOverride(vector, seg_addr_index,
                                     ExprDSrCase, RegDS)) {
            NaClPrintSegmentOverride(file, &is_first, state, vector,
                                     seg_addr_index);
          } else if (NaClHasSegmentOverride(vector, seg_addr_index,
                                            ExprESrCase, RegES)) {
            NaClPrintSegmentOverride(file, &is_first, state, vector,
                                     seg_addr_index);
          }
        }
      }
    }
    /* Skip over expression to next expresssion. */
    tree_index += NaClExpWidth(vector, tree_index);
  }
}