/* 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; }
/* 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"); }
/* 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; }
/* 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; }
/* 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 name of the register defined by the indexed node in the * vector of nodes. */ NaClOpKind NaClGetExpVectorRegister(NaClExpVector* vector, int node) { return NaClGetExpRegisterInline(&vector->node[node]); }
/* Returns the register defined by the given node. */ NaClOpKind NaClGetExpRegister(NaClExp* node) { return NaClGetExpRegisterInline(node); }
static INLINE void NaClPrintDisassembledReg(struct Gio* file, NaClExp* node) { NaClPrintDisassembledRegKind(file, NaClGetExpRegisterInline(node)); }