Commit message: x86: fast path checking for X86_insn_reg_intel()
CVE: CVE-2016-7151
CWE: CWE-125
Vulnerability description: Capstone 3.0.4 has an out-of-bounds vulnerability (SEGV caused by a read memory access) in X86_insn_reg_intel in arch/X86/X86Mapping.c.

Project: capstone

void X86_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
{
	int i = insn_find(insns, ARR_SIZE(insns), id, &h->insn_cache);
	if (i != 0) {
		insn->id = insns[i].mapid;

		if (h->detail) {
#ifndef CAPSTONE_DIET
			memcpy(insn->detail->regs_read, insns[i].regs_use, sizeof(insns[i].regs_use));
			insn->detail->regs_read_count = (uint8_t)count_positive(insns[i].regs_use);

			switch(id) {
				default:
					memcpy(insn->detail->regs_write, insns[i].regs_mod, sizeof(insns[i].regs_mod));
					insn->detail->regs_write_count = (uint8_t)count_positive(insns[i].regs_mod);
					break;
				case X86_RDTSC:
					if (h->mode == CS_MODE_64) {
						memcpy(insn->detail->regs_write, insns[i].regs_mod, sizeof(insns[i].regs_mod));
						insn->detail->regs_write_count = (uint8_t)count_positive(insns[i].regs_mod);
					} else {
						insn->detail->regs_write[0] = X86_REG_EAX;
						insn->detail->regs_write[1] = X86_REG_EDX;
						insn->detail->regs_write_count = 2;
					}
					break;
				case X86_RDTSCP:
					if (h->mode == CS_MODE_64) {
						memcpy(insn->detail->regs_write, insns[i].regs_mod, sizeof(insns[i].regs_mod));
						insn->detail->regs_write_count = (uint8_t)count_positive(insns[i].regs_mod);
					} else {
						insn->detail->regs_write[0] = X86_REG_EAX;
						insn->detail->regs_write[1] = X86_REG_ECX;
						insn->detail->regs_write[2] = X86_REG_EDX;
						insn->detail->regs_write_count = 3;
					}
					break;
			}

			switch(insn->id) {
				default:
					break;

				case X86_INS_LOOP:
				case X86_INS_LOOPE:
				case X86_INS_LOOPNE:
					switch(h->mode) {
						default: break;
						case CS_MODE_16:
								 insn->detail->regs_read[0] = X86_REG_CX;
								 insn->detail->regs_read_count = 1;
								 insn->detail->regs_write[0] = X86_REG_CX;
								 insn->detail->regs_write_count = 1;
								 break;
						case CS_MODE_32:
								 insn->detail->regs_read[0] = X86_REG_ECX;
								 insn->detail->regs_read_count = 1;
								 insn->detail->regs_write[0] = X86_REG_ECX;
								 insn->detail->regs_write_count = 1;
								 break;
						case CS_MODE_64:
								 insn->detail->regs_read[0] = X86_REG_RCX;
								 insn->detail->regs_read_count = 1;
								 insn->detail->regs_write[0] = X86_REG_RCX;
								 insn->detail->regs_write_count = 1;
								 break;
					}

					if (insn->id != X86_INS_LOOP) {
						insn->detail->regs_read[1] = X86_REG_EFLAGS;
						insn->detail->regs_read_count = 2;
					}

					break;

				case X86_INS_LODSB:
				case X86_INS_LODSD:
				case X86_INS_LODSQ:
				case X86_INS_LODSW:
					switch(h->mode) {
						default:
							break;
						case CS_MODE_16:
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_ESI, X86_REG_SI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_ESI, X86_REG_SI);
							break;
						case CS_MODE_64:
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_ESI, X86_REG_RSI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_ESI, X86_REG_RSI);
							break;
					}
					break;

				case X86_INS_SCASB:
				case X86_INS_SCASW:
				case X86_INS_SCASQ:
				case X86_INS_STOSB:
				case X86_INS_STOSD:
				case X86_INS_STOSQ:
				case X86_INS_STOSW:
					switch(h->mode) {
						default:
							break;
						case CS_MODE_16:
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_EDI, X86_REG_DI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_EDI, X86_REG_DI);
							break;
						case CS_MODE_64:
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_EDI, X86_REG_RDI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_EDI, X86_REG_RDI);
							break;
					}
					break;

				case X86_INS_CMPSB:
				case X86_INS_CMPSD:
				case X86_INS_CMPSQ:
				case X86_INS_CMPSW:
				case X86_INS_MOVSB:
				case X86_INS_MOVSW:
				case X86_INS_MOVSD:
				case X86_INS_MOVSQ:
					switch(h->mode) {
						default:
							break;
						case CS_MODE_16:
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_EDI, X86_REG_DI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_EDI, X86_REG_DI);
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_ESI, X86_REG_SI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_ESI, X86_REG_SI);
							break;
						case CS_MODE_64:
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_EDI, X86_REG_RDI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_EDI, X86_REG_RDI);
							arr_replace(insn->detail->regs_read, insn->detail->regs_read_count, X86_REG_ESI, X86_REG_RSI);
							arr_replace(insn->detail->regs_write, insn->detail->regs_write_count, X86_REG_ESI, X86_REG_RSI);
							break;
					}
					break;
			}

			memcpy(insn->detail->groups, insns[i].groups, sizeof(insns[i].groups));
			insn->detail->groups_count = (uint8_t)count_positive8(insns[i].groups);

			if (insns[i].branch || insns[i].indirect_branch) {
				insn->detail->groups[insn->detail->groups_count] = X86_GRP_JUMP;
				insn->detail->groups_count++;
			}

			switch (insns[i].id) {
				case X86_OUT8ir:
				case X86_OUT16ir:
				case X86_OUT32ir:
					if (insn->detail->x86.operands[0].imm == -78) {
						insn->detail->groups[insn->detail->groups_count] = X86_GRP_INT;
						insn->detail->groups_count++;
					}
					break;

				default:
					break;
			}
#endif
		}
	}
}

Project: capstone
File: X86Mapping.c

uint8_t *X86_get_op_access(cs_struct *h, unsigned int id, uint64_t *eflags)
{
	int i = insn_find(insns, ARR_SIZE(insns), id, &h->insn_cache);
	if (i != 0) {
		*eflags = insn_ops[i].eflags;
		return insn_ops[i].access;
	}

	return NULL;
}

Project: capstone
File: X86Mapping.c

const char *X86_group_name(csh handle, unsigned int id)
{
#ifndef CAPSTONE_DIET
	return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
#else
	return NULL;
#endif
}

Project: capstone

int X86_immediate_size(unsigned int id)
{
#if 0
	unsigned int i;

	for (i = 0; i < ARR_SIZE(x86_imm_size); i++) {
		if (id == x86_imm_size[i].id) {
			return x86_imm_size[i].size;
		}
	}
#endif

	unsigned int left, right, m;

	left = 0;
	right = ARR_SIZE(x86_imm_size) - 1;

	while(left <= right) {
		m = (left + right) / 2;
		if (id == x86_imm_size[m].id)
			return x86_imm_size[m].size;

		if (id < x86_imm_size[m].id)
			right = m - 1;
		else
			left = m + 1;
	}

	return 0;
}

Project: capstone
File: X86Mapping.c

const char *X86_insn_name(csh handle, unsigned int id)
{
#ifndef CAPSTONE_DIET
	if (id >= X86_INS_ENDING)
		return NULL;

	return insn_name_maps[id].name;
#else
	return NULL;
#endif
}

Project: capstone

x86_reg X86_insn_reg_att(unsigned int id, enum cs_ac_type *access)
{
	unsigned int i;

	for (i = 0; i < ARR_SIZE(insn_regs_att); i++) {
		if (insn_regs_att[i].insn == id) {
            if (access)
                *access = insn_regs_intel[i].access;
			return insn_regs_att[i].reg;
		}
	}

	return 0;
}

Project: capstone

bool X86_insn_reg_intel2(unsigned int id, x86_reg *reg1, enum cs_ac_type *access1, x86_reg *reg2, enum cs_ac_type *access2)
{
	unsigned int i;

	for (i = 0; i < ARR_SIZE(insn_regs_intel2); i++) {
		if (insn_regs_intel2[i].insn == id) {
			*reg1 = insn_regs_intel2[i].reg1;
			*reg2 = insn_regs_intel2[i].reg2;
			if (access1)
				*access1 = insn_regs_intel2[i].access1;
			if (access2)
				*access2 = insn_regs_intel2[i].access2;
			return true;
		}
	}

	return false;
}

Project: capstone

bool X86_lockrep(MCInst *MI, SStream *O)
{
	unsigned int opcode;
	bool res = false;

	switch(MI->x86_prefix[0]) {
		default:
			break;
		case 0xf0:
#ifndef CAPSTONE_DIET
			SStream_concat(O, "lock|");
#endif
			break;
		case 0xf2:	// repne
			opcode = MCInst_getOpcode(MI);
#ifndef CAPSTONE_DIET	// only care about memonic in standard (non-diet) mode
			if (valid_repne(MI->csh, opcode)) {
				SStream_concat(O, "repne|");
				add_cx(MI);
			} else {
				MI->x86_prefix[0] = 0;

#ifndef CAPSTONE_X86_REDUCE
				if (opcode == X86_MULPDrr) {
					MCInst_setOpcode(MI, X86_MULSDrr);
					SStream_concat(O, "mulsd\t");
					res = true;
				}
#endif
			}
#else	// diet mode -> only patch opcode in special cases
			if (!valid_repne(MI->csh, opcode)) {
				MI->x86_prefix[0] = 0;
			}
#ifndef CAPSTONE_X86_REDUCE
			if (opcode == X86_MULPDrr) {
				MCInst_setOpcode(MI, X86_MULSDrr);
			}
#endif
#endif
			break;

		case 0xf3:
			opcode = MCInst_getOpcode(MI);
#ifndef CAPSTONE_DIET	// only care about memonic in standard (non-diet) mode
			if (valid_rep(MI->csh, opcode)) {
				SStream_concat(O, "rep|");
				add_cx(MI);
			} else if (valid_repe(MI->csh, opcode)) {
				SStream_concat(O, "repe|");
				add_cx(MI);
			} else {
				MI->x86_prefix[0] = 0;

#ifndef CAPSTONE_X86_REDUCE
				if (opcode == X86_MULPDrr) {
					MCInst_setOpcode(MI, X86_MULSSrr);
					SStream_concat(O, "mulss\t");
					res = true;
				}
#endif
			}
#else	// diet mode -> only patch opcode in special cases
			if (!valid_rep(MI->csh, opcode) && !valid_repe(MI->csh, opcode)) {
				MI->x86_prefix[0] = 0;
			}
#ifndef CAPSTONE_X86_REDUCE
			if (opcode == X86_MULPDrr) {
				MCInst_setOpcode(MI, X86_MULSSrr);
			}
#endif
#endif
			break;
	}

	if (MI->csh->detail)
		memcpy(MI->flat_insn->detail->x86.prefix, MI->x86_prefix, ARR_SIZE(MI->x86_prefix));

	return res;
}

Project: capstone

void X86_reg_access(const cs_insn *insn,
		cs_regs regs_read, uint8_t *regs_read_count,
		cs_regs regs_write, uint8_t *regs_write_count)
{
	uint8_t i;
	uint8_t read_count, write_count;
	cs_x86 *x86 = &(insn->detail->x86);

	read_count = insn->detail->regs_read_count;
	write_count = insn->detail->regs_write_count;

	memcpy(regs_read, insn->detail->regs_read, read_count * sizeof(insn->detail->regs_read[0]));
	memcpy(regs_write, insn->detail->regs_write, write_count * sizeof(insn->detail->regs_write[0]));

	for (i = 0; i < x86->op_count; i++) {
		cs_x86_op *op = &(x86->operands[i]);
		switch((int)op->type) {
			case X86_OP_REG:
				if ((op->access & CS_AC_READ) && !arr_exist(regs_read, read_count, op->reg)) {
					regs_read[read_count] = op->reg;
					read_count++;
				}
				if ((op->access & CS_AC_WRITE) && !arr_exist(regs_write, write_count, op->reg)) {
					regs_write[write_count] = op->reg;
					write_count++;
				}
				break;
			case X86_OP_MEM:
				if ((op->mem.segment != X86_REG_INVALID)) {
					regs_read[read_count] = op->mem.segment;
					read_count++;
				}
				if ((op->mem.base != X86_REG_INVALID) && !arr_exist(regs_read, read_count, op->mem.base)) {
					regs_read[read_count] = op->mem.base;
					read_count++;
				}
				if ((op->mem.index != X86_REG_INVALID) && !arr_exist(regs_read, read_count, op->mem.index)) {
					regs_read[read_count] = op->mem.index;
					read_count++;
				}
			default:
				break;
		}
	}

	*regs_read_count = read_count;
	*regs_write_count = write_count;
}

Project: capstone
File: X86Mapping.c

const char *X86_reg_name(csh handle, unsigned int reg)
{
#ifndef CAPSTONE_DIET
	cs_struct *ud = (cs_struct *)handle;

	if (reg >= X86_REG_ENDING)
		return NULL;

	if (reg == X86_REG_EFLAGS) {
		if (ud->mode & CS_MODE_32)
			return "eflags";
		if (ud->mode & CS_MODE_64)
			return "rflags";
	}

	return reg_name_maps[reg].name;
#else
	return NULL;
#endif
}

Project: capstone
File: X86Mapping.c

static void add_cx(MCInst *MI)
{
	if (MI->csh->detail) {
		x86_reg cx;

		if (MI->csh->mode & CS_MODE_16)
			cx = X86_REG_CX;
		else if (MI->csh->mode & CS_MODE_32)
			cx = X86_REG_ECX;
		else	// 64-bit
			cx = X86_REG_RCX;

		MI->flat_insn->detail->regs_read[MI->flat_insn->detail->regs_read_count] = cx;
		MI->flat_insn->detail->regs_read_count++;

		MI->flat_insn->detail->regs_write[MI->flat_insn->detail->regs_write_count] = cx;
		MI->flat_insn->detail->regs_write_count++;
	}
}

Project: capstone
File: X86Mapping.c

static void arr_replace(uint16_t *arr, uint8_t max, x86_reg r1, x86_reg r2)
{
	uint8_t i;

	for(i = 0; i < max; i++) {
		if (arr[i] == r1) {
			arr[i] = r2;
			break;
		}
	}
}

Project: capstone

void op_addAvxBroadcast(MCInst *MI, x86_avx_bcast v)
{
	if (MI->csh->detail) {
		MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count - 1].avx_bcast = v;
	}
}

Project: capstone
File: X86Mapping.c

void op_addAvxRoundingMode(MCInst *MI, int v)
{
	if (MI->csh->detail) {
		MI->flat_insn->detail->x86.avx_rm = v;
	}
}

Project: capstone
File: X86Mapping.c

void op_addAvxSae(MCInst *MI)
{
	if (MI->csh->detail) {
		MI->flat_insn->detail->x86.avx_sae = true;
	}
}

Project: capstone

void op_addImm(MCInst *MI, int v)
{
	if (MI->csh->detail) {
		MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].type = X86_OP_IMM;
		MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].imm = v;
		if (MI->csh->syntax != CS_OPT_SYNTAX_ATT) {
			if (MI->flat_insn->detail->x86.op_count > 0)
				MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].size = MI->flat_insn->detail->x86.operands[0].size;
			else
				MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].size = MI->imm_size;
		} else
			MI->has_imm = true;
		MI->flat_insn->detail->x86.op_count++;
	}

	if (MI->op1_size == 0)
		MI->op1_size = MI->imm_size;
}

Project: capstone
File: X86Mapping.c

void op_addReg(MCInst *MI, int reg)
{
	if (MI->csh->detail) {
		MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].type = X86_OP_REG;
		MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].reg = reg;
		MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].size = MI->csh->regsize_map[reg];
		MI->flat_insn->detail->x86.op_count++;
	}

	if (MI->op1_size == 0)
		MI->op1_size = MI->csh->regsize_map[reg];
}

Project: capstone
File: X86Mapping.c

void op_addSseCC(MCInst *MI, int v)
{
	if (MI->csh->detail) {
		MI->flat_insn->detail->x86.sse_cc = v;
	}
}

Project: capstone
File: X86Mapping.c

static int regs_cmp(const void *a, const void *b)
{
	uint16_t l = ((struct insn_reg *)a)->insn;
	uint16_t r = ((struct insn_reg *)b)->insn;
 	return (l - r);
 }

Project: capstone

static bool valid_rep(cs_struct *h, unsigned int opcode)
{
	unsigned int id;
	int i = insn_find(insns, ARR_SIZE(insns), opcode, &h->insn_cache);
	if (i != 0) {
		id = insns[i].mapid;
		switch(id) {
			default:
				return false;

			case X86_INS_MOVSB:
			case X86_INS_MOVSW:
			case X86_INS_MOVSQ:

			case X86_INS_LODSB:
			case X86_INS_LODSW:
			case X86_INS_LODSQ:

			case X86_INS_STOSB:
			case X86_INS_STOSW:
			case X86_INS_STOSQ:

			case X86_INS_INSB:
			case X86_INS_INSW:
			case X86_INS_INSD:

			case X86_INS_OUTSB:
			case X86_INS_OUTSW:
			case X86_INS_OUTSD:
				return true;

			case X86_INS_MOVSD:
				if (opcode == X86_MOVSL) // REP MOVSD
					return true;
				return false;

			case X86_INS_LODSD:
				if (opcode == X86_LODSL) // REP LODSD
					return true;
				return false;

			case X86_INS_STOSD:
				if (opcode == X86_STOSL) // REP STOSD
					return true;
				return false;
		}
	}

	return false;
}

Project: capstone

static bool valid_repe(cs_struct *h, unsigned int opcode)
{
	unsigned int id;
	int i = insn_find(insns, ARR_SIZE(insns), opcode, &h->insn_cache);
	if (i != 0) {
		id = insns[i].mapid;
		switch(id) {
			default:
				return false;

			case X86_INS_CMPSB:
			case X86_INS_CMPSW:
			case X86_INS_CMPSQ:

			case X86_INS_SCASB:
			case X86_INS_SCASW:
			case X86_INS_SCASQ:
				return true;

			case X86_INS_CMPSD:
				if (opcode == X86_CMPSL) // REP CMPSD
					return true;
				return false;

			case X86_INS_SCASD:
				if (opcode == X86_SCASL) // REP SCASD
					return true;
				return false;
		}
	}

	return false;
}

Project: capstone

static bool valid_repne(cs_struct *h, unsigned int opcode)
{
	unsigned int id;
	int i = insn_find(insns, ARR_SIZE(insns), opcode, &h->insn_cache);
	if (i != 0) {
		id = insns[i].mapid;
		switch(id) {
			default:
				return false;

			case X86_INS_CMPSB:
			case X86_INS_CMPSW:
			case X86_INS_CMPSQ:

			case X86_INS_SCASB:
			case X86_INS_SCASW:
			case X86_INS_SCASQ:

			case X86_INS_MOVSB:
			case X86_INS_MOVSW:
			case X86_INS_MOVSD:
			case X86_INS_MOVSQ:

			case X86_INS_LODSB:
			case X86_INS_LODSW:
			case X86_INS_LODSD:
			case X86_INS_LODSQ:

			case X86_INS_STOSB:
			case X86_INS_STOSW:
			case X86_INS_STOSD:
			case X86_INS_STOSQ:

			case X86_INS_INSB:
			case X86_INS_INSW:
			case X86_INS_INSD:

			case X86_INS_OUTSB:
			case X86_INS_OUTSW:
			case X86_INS_OUTSD:

				return true;

			case X86_INS_CMPSD:
				if (opcode == X86_CMPSL) // REP CMPSD
					return true;
				return false;

			case X86_INS_SCASD:
				if (opcode == X86_SCASL) // REP SCASD
					return true;
				return false;
		}
	}

	return false;
}

Project: capstone
File: X86Mapping.c

x86_reg x86_map_segment(int r)
{
	return segment_map[r];
}

Project: capstone
File: X86Mapping.c

x86_reg x86_map_sib_base(int r)
{
	return sib_base_map[r];
}

Project: capstone
File: X86Mapping.c

x86_reg x86_map_sib_index(int r)
{
	return sib_index_map[r];
}