void NaClDisassembleSegment(uint8_t* mbase, NaClPcAddress vbase, NaClMemorySize size, NaClDisassembleFlags flags) { if (NaClHasBit(flags, NACL_DISASSEMBLE_FLAG(NaClDisassembleFull))) { if (NaClHasBit(flags, NACL_DISASSEMBLE_FLAG(NaClDisassembleValidatorDecoder))) { gprintf(NaClLogGetGio(), "Error: can't specify both full and validator disassembly,\n" " assuming full disassembly.\n"); } NaClDisassembleSegmentUsingTables(mbase, vbase, size, flags, kNaClDecoderTables); } else if (NaClHasBit (flags, NACL_DISASSEMBLE_FLAG(NaClDisassembleValidatorDecoder))) { if (64 == NACL_TARGET_SUBARCH) { NaClDisassembleSegmentUsingTables(mbase, vbase, size, flags, kNaClValDecoderTables); } else { NCDecodeSegment(mbase, vbase, size); } } else { gprintf(NaClLogGetGio(), "Error: No decoder tables specified, can't disassemble\n"); } }
/* Print out the register (from the list of register names), * that is referenced by the decoded instruction in the * decoder state. If is_gp_regs is true, the register names * correspond to general purpose register names. Otherwise, * they are some other set, such as MMX or XMM. */ static void RegMemPrint(const NCDecoderInst *dinst, const char* reg_names[], const uint8_t is_gp_regs, struct Gio* fp) { DEBUG( printf( "reg mem print: sib_offset = %d, " "disp_offset = %d, mrm.mod = %02x\n", NCSibOffset(dinst), (int) NCDispOffset(dinst), modrm_modInline(dinst->inst.mrm)) ); switch (modrm_modInline(dinst->inst.mrm)) { case 0: SegPrefixPrint(dinst, fp); if (NaClHasBit(dinst->inst.prefixmask, kPrefixADDR16)) { NaClIllegalOp(fp); } else { if (4 == modrm_rmInline(dinst->inst.mrm)) { SibPrint(dinst, fp); } else if (5 == modrm_rmInline(dinst->inst.mrm)) { gprintf(fp, "[0x%x]", DispValue32(dinst)); } else { gprintf(fp, "[%s]", gp_regs[modrm_rmInline(dinst->inst.mrm)]); } } break; case 1: SegPrefixPrint(dinst, fp); if (NaClHasBit(dinst->inst.prefixmask, kPrefixADDR16)) { NaClIllegalOp(fp); } else { gprintf(fp, "0x%x", DispValue32(dinst)); if (4 == modrm_rmInline(dinst->inst.mrm)) { SibPrint(dinst, fp); } else { gprintf(fp, "[%s]", gp_regs[modrm_rmInline(dinst->inst.mrm)]); } } break; case 2: SegPrefixPrint(dinst, fp); if (NaClHasBit(dinst->inst.prefixmask, kPrefixADDR16)) { NaClIllegalOp(fp); } else { gprintf(fp, "0x%x", DispValue32(dinst)); if (4 == modrm_rmInline(dinst->inst.mrm)) { SibPrint(dinst, fp); } else { gprintf(fp, "[%s]", gp_regs[modrm_rmInline(dinst->inst.mrm)]); } } break; case 3: if (is_gp_regs) { gprintf(fp, "%s", reg_names[state_modrm_reg(dinst)]); } else { gprintf(fp, "%s", reg_names[modrm_rmInline(dinst->inst.mrm)]); } break; } }
/* Given a jump statement, add the corresponding (explicit) jump value * to the set of actual jump targets. * Parameters: * vstate - The state of the validator. */ static void NaClAddExprJumpTarget(NaClValidatorState* vstate) { uint32_t i; NaClInstState* inst_state = vstate->cur_inst_state; NaClExpVector* vector = vstate->cur_inst_vector; DEBUG(NaClLog(LOG_INFO, "jump checking: "); NaClInstStateInstPrint(NaClLogGetGio(), inst_state)); for (i = 0; i < vector->number_expr_nodes; ++i) { NaClExp* node = &vector->node[i]; if (!NaClHasBit(node->flags, NACL_EFLAG(ExprJumpTarget))) continue; switch (node->kind) { case ExprRegister: if (64 == NACL_TARGET_SUBARCH) { NaClAddRegisterJumpIndirect64(vstate, node); } else { NaClAddRegisterJumpIndirect32(vstate, node); } break; case ExprConstant: /* Direct jump. */ NaClAddJumpToJumpSets(vstate, inst_state, (NaClPcNumber) NaClGetExprSignedValue(node)); break; default: NaClValidatorInstMessage( LOG_ERROR, vstate, inst_state, "Jump not native client compliant\n"); } } }
/* Checks if an indirect jump (in 32-bit mode) is native client compliant. * * Expects pattern: * and %REG, MASK * jmp %REG * * where the 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. * * Note: applies to all kinds of jumps and calls. * * Parameters: * state - The state of the validator. * reg - The register used in the jump instruction. */ static void NaClAddRegisterJumpIndirect32(NaClValidatorState* vstate, NaClExp* reg) { NaClOpKind jump_reg, and_reg; /* Do the following block exactly once. Use loop so that "break" can * be used for premature exit of block. */ do { /* Check that jump register is 32-bit. */ if (!NaClHasBit(reg->flags, NACL_EFLAG(ExprSize32))) break; jump_reg = NaClGetExpRegisterInline(reg); if (RegUnknown == jump_reg) break; DEBUG(NaClLog(LOG_INFO, "checking indirect jump: "); NaClInstStateInstPrint(NaClLogGetGio(), vstate->cur_inst_state); gprintf(NaClLogGetGio(), "jump_reg = %s\n", NaClOpKindName(jump_reg))); /* Check that sequence begins with an appropriate and instruction. */ and_reg = NaClGetAndMaskReg32(vstate, 1); if (jump_reg != and_reg) break; /* If reached, indirect jump is properly masked. */ DEBUG(NaClLog(LOG_INFO, "Protect register jump indirect\n")); NaClMarkInstructionJumpIllegal(vstate, vstate->cur_inst_state); return; } while(0); /* If reached, mask was not found. */ NaClValidatorInstMessage(LOG_ERROR, vstate, vstate->cur_inst_state, "Invalid indirect jump\n"); }
/* Disassemble the code segment, using the given decoder tables. * Note: The decoder tables specified in the flags argument will * be ignored. * * Parameters: * mbase - Memory region containing code segment. * vbase - PC address associated with first byte of memory region. * size - Number of bytes in memory region. * flags - Flags to use when decoding. */ static void NaClDisassembleSegmentUsingTables( uint8_t* mbase, NaClPcAddress vbase, NaClMemorySize size, NaClDisassembleFlags flags, const struct NaClDecodeTables* decoder_tables) { NaClSegment segment; NaClInstIter* iter; struct Gio* gout = NaClLogGetGio(); Bool print_internals = NaClHasBit(flags, NACL_DISASSEMBLE_FLAG(NaClDisassembleAddInternals)); NaClSegmentInitialize(mbase, vbase, size, &segment); iter = NaClInstIterCreate(decoder_tables, &segment); if (NULL == iter) { gprintf(gout, "Error: not enough memory\n"); } else { for (; NaClInstIterHasNext(iter); NaClInstIterAdvance(iter)) { NaClInstState* state = NaClInstIterGetState(iter); NaClInstStateInstPrint(gout, state); if (print_internals) { NaClInstPrintOpcodeSeq(gout, state); NaClInstPrint(gout, state->decoder_tables, NaClInstStateInst(state)); NaClExpVectorPrint(gout, state); } } NaClInstIterDestroy(iter); } }
/* 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)); }
/* Checks if an indirect jump (in 64-bit mode) is native client compliant. * * Expects pattern: * * and %REG32, MASK * add %REG64, %RBASE * jmp %REG64 * * where MASK is all 1/s except for the alignment mask bits, which must be zero. * * REG32 is the corresponding 32-bit register that whose value will get zero * extended by the AND operation into the corresponding 64-bit register REG64. * * It is assumed that opcode 0x83 is used for the AND operation, and hence, the * mask is a single byte. * * Note: applies to all kinds of jumps and calls. * * Parameters: * vstate - The state of the validator. * reg - The register used in the jump instruction. */ static void NaClAddRegisterJumpIndirect64(NaClValidatorState* vstate, NaClExp* reg) { NaClOpKind jump_reg, and_reg32, and_reg64; /* Do the following block exactly once. Use loop so that "break" can * be used for premature exit of block. */ do { /* Check that jump register is 64-bit. */ if (!NaClHasBit(reg->flags, NACL_EFLAG(ExprSize64))) break; jump_reg = NaClGetExpRegisterInline(reg); if (RegUnknown == jump_reg) break; DEBUG(NaClLog(LOG_INFO, "checking indirect jump: "); NaClInstStateInstPrint(NaClLogGetGio(), vstate->cur_inst_state); gprintf(NaClLogGetGio(), "jump_reg = %s\n", NaClOpKindName(jump_reg))); /* Check that sequence begins with an appropriate and instruction. */ and_reg32 = NaClGetAndMaskReg32(vstate, 2); if (RegUnknown == and_reg32) break; /* Get corresponding 64-bit register for 32-bit result of 'and', * and make sure it matches the jump register. */ and_reg64 = NaClGet64For32BitReg(and_reg32); if (and_reg64 != jump_reg) break; /* Check that the middle instruction is an appropriate add instruction. */ if (!NaClIsAddRbaseToReg64(vstate, 1, and_reg64)) break; /* If reached, indirect jump is properly masked. */ DEBUG(NaClLog(LOG_INFO, "Protect indirect jump instructions\n")); NaClMarkInstructionJumpIllegal( vstate, NaClInstIterGetLookbackStateInline(vstate->cur_iter, 1)); NaClMarkInstructionJumpIllegal(vstate, vstate->cur_inst_state); return; } while(0); /* If reached, mask was not found. */ NaClValidatorInstMessage(LOG_ERROR, vstate, vstate->cur_inst_state, "Invalid indirect jump\n"); }
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"); } } } } } }
/* Returns true if the disassemble flag is in the given flag set. */ Bool NaClContainsDisasembleFlag(NaClDisassembleFlags flags, NaClDisassembleFlag flag) { return NaClHasBit(flags, NACL_DISASSEMBLE_FLAG(flag)) ? TRUE : FALSE; }
uint8_t NaClRexB(uint8_t prefix) { return NaClHasBit(prefix, NaClRexBMask); }
/* 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); } }