/* 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"); } } }
/* Given the number of bytes for a literal constant, return the corresponding * expr node flags that represent the value of the parsed bytes. */ static NaClExpFlags NaClGetExprSizeFlagForBytes(uint8_t num_bytes) { switch (num_bytes) { case 1: return NACL_EFLAG(ExprSize8); break; case 2: return NACL_EFLAG(ExprSize16); break; case 4: return NACL_EFLAG(ExprSize32); case 8: return NACL_EFLAG(ExprSize64); default: return 0; } }
/* 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"); }
Bool NaClIsExpNegativeConstant(NaClExpVector* vector, int index) { NaClExp* node = &vector->node[index]; switch (node->kind) { case ExprConstant: if (node->flags & NACL_EFLAG(ExprUnsignedHex) || node->flags & NACL_EFLAG(ExprUnsignedInt)) { return FALSE; } else { /* Assume signed value. */ return NaClGetExprSignedValue(node) < 0; } break; default: break; } return FALSE; }
/* Append the given constant onto the given vector of expression * nodes. Returns the appended expression node. */ static INLINE NaClExp* NaClAppendConst(uint64_t value, NaClExpFlags flags, NaClExpVector* vector) { uint32_t val1; uint32_t val2; DEBUG( NaClLog(LOG_INFO, "Append constant %"NACL_PRIx64" : ", value); NaClPrintExpFlags(NaClLogGetGio(), flags); gprintf(NaClLogGetGio(), "\n")); NaClSplitExpConstant(value, &val1, &val2); if (val2 == 0) { return NaClAppendExp(ExprConstant, val1, flags, vector); } else { NaClExp* root = NaClAppendExp(ExprConstant64, 0, flags, vector); NaClAppendExp(ExprConstant, val1, NACL_EFLAG(ExprUnsignedHex), vector); NaClAppendExp(ExprConstant, val2, NACL_EFLAG(ExprUnsignedHex), vector); return root; } }
/* 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 true if there is a segment override in the segment address * node defined by vector[seg_addr_index]. * * Parameters: * vector - The node expression tree associated with the instruction. * seg_addr_index - The index to the segment address node to check. * seg_eflag - The expr flag that must be associated with the * segment address node to be considered for an override. * seg_reg - The expected (i.e. default) segment register * to be associated with the segment address. */ static Bool NaClHasSegmentOverride(NaClExpVector* vector, int seg_addr_index, NaClExpFlag seg_eflag, NaClOpKind seg_reg) { NaClExp* seg_node = &vector->node[seg_addr_index]; if (seg_node->flags & NACL_EFLAG(seg_eflag)) { int seg_index = seg_addr_index + 1; NaClExp* node = &vector->node[seg_index]; if ((ExprRegister == node->kind) && (seg_reg != NaClGetExpRegisterInline(node))) { return TRUE; } } return FALSE; }
/* Print out the given constant expression node to the given file. */ static void NaClPrintDisassembledConst( struct Gio* file, NaClInstState* state, NaClExp* node) { assert(node->kind == ExprConstant); if (node->flags & NACL_EFLAG(ExprJumpTarget)) { NaClPcAddress target = NaClInstStatePrintableAddress(state) + state->bytes.length + (NaClPcNumber) NaClGetExprSignedValue(node); gprintf(file, "0x%"NACL_PRIxNaClPcAddress, target); }else if (node->flags & NACL_EFLAG(ExprUnsignedHex)) { gprintf(file, "0x%"NACL_PRIx64, NaClGetExprUnsignedValue(node)); } else if (node->flags & NACL_EFLAG(ExprSignedHex)) { int64_t val = NaClGetExprSignedValue(node); if (val < 0) { val = -val; gprintf(file, "-0x%"NACL_PRIx64, val); } else { gprintf(file, "0x%"NACL_PRIx64, val); } } else if (node->flags & NACL_EFLAG(ExprUnsignedInt)) { gprintf(file, "%"NACL_PRIu64, NaClGetExprUnsignedValue(node)); } else { /* Assume ExprSignedInt. */ gprintf(file, "%"NACL_PRId64, NaClGetExprSignedValue(node)); } }
void NaClPrintExpFlags(struct Gio* file, NaClExpFlags flags) { if (flags == 0) { gprintf(file, "0"); } else { NaClExpFlag f; Bool is_first = TRUE; for (f = 0; f < NaClExpFlagEnumSize; f++) { if (flags & NACL_EFLAG(f)) { if (is_first) { is_first = FALSE; } else { gprintf(file, " | "); } gprintf(file, "%s", NaClExpFlagName(f)); } } } }
/* 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"); }
/* Returns the (NaClExpFlag) size of the given register. */ static NaClExpFlags NaClGetRegSize(NaClOpKind register_name) { switch (register_name) { case RegAL: case RegBL: case RegCL: case RegDL: case RegAH: case RegBH: case RegCH: case RegDH: case RegDIL: case RegSIL: case RegBPL: case RegSPL: case RegR8B: case RegR9B: case RegR10B: case RegR11B: case RegR12B: case RegR13B: case RegR14B: case RegR15B: return NACL_EFLAG(ExprSize8); case RegAX: case RegBX: case RegCX: case RegDX: case RegSI: case RegDI: case RegBP: case RegSP: case RegR8W: case RegR9W: case RegR10W: case RegR11W: case RegR12W: case RegR13W: case RegR14W: case RegR15W: return NACL_EFLAG(ExprSize16); case RegEAX: case RegEBX: case RegECX: case RegEDX: case RegESI: case RegEDI: case RegEBP: case RegESP: case RegR8D: case RegR9D: case RegR10D: case RegR11D: case RegR12D: case RegR13D: case RegR14D: case RegR15D: return NACL_EFLAG(ExprSize32); case RegCS: case RegDS: case RegSS: case RegES: case RegFS: case RegGS: return NACL_EFLAG(ExprSize16); case RegEIP: return NACL_EFLAG(ExprSize32); case RegRIP: return NACL_EFLAG(ExprSize64); case RegRAX: case RegRBX: case RegRCX: case RegRDX: case RegRSI: case RegRDI: case RegRBP: case RegRSP: case RegR8: case RegR9: case RegR10: case RegR11: case RegR12: case RegR13: case RegR14: case RegR15: return NACL_EFLAG(ExprSize64); default: return 0; } }
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"); } } } } } }
/* 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); } }