size_t NaClOpRegName(NaClOpKind reg, char* buffer, size_t buffer_size) { const char* name = NaClOpKindName(reg); char* str; size_t index; /* Fail if no room to put register name. */ if (buffer_size == 0) return 0; buffer[0] = '\0'; /* To be safe, in case we exit prematurely. */ /* Get name for register. */ name = NaClOpKindName(reg); if (NULL == name) return 0; /* Strip off 'Reg' prefix from register name, if it exists. */ str = strstr(name, NACLOP_REG_PREFIX); if (str != name) return 0; str += strlen(NACLOP_REG_PREFIX); /* Copy the name, converting characters to lower case. */ for (index = 0; (index + 1) < buffer_size; ++index) { char ch = tolower(str[index]); if ('\0' == ch) break; buffer[index] = tolower(str[index]); } /* Be sure to add null character at end. */ buffer[index] = '\0'; return index; }
/* Print out the opcode operand in a simplified (i.e. more human readable) * form. */ void NaClOpPrint(struct Gio* f, const NaClOp* operand) { gprintf(f, "%s", NaClOpKindName(operand->kind)); if (operand->flags) { size_t i; for (i = strlen(NaClOpKindName(operand->kind)); i < 24; ++i) { gprintf(f, " "); } NaClOpFlagsPrint(f, operand->flags); } gprintf(f, "\n"); }
/* Appends the given kind of register onto the vector of expression nodes. * Returns the appended register. */ static INLINE NaClExp* NaClAppendReg(NaClOpKind r, NaClExpVector* vector) { NaClExp* node; DEBUG(NaClLog(LOG_INFO, "append register %s\n", NaClOpKindName(r))); node = NaClAppendExp(ExprRegister, r, NaClGetRegSize(r), vector); DEBUG(NaClExpVectorPrint(NaClLogGetGio(), vector)); return node; }
/* 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"); }
/* 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"); }