Commit message: Fix #12242 - Crash in x86.nz assembler (#12266)
CVE: CVE-2018-19843
CWE: CWE-125
Vulnerability description: opmov in libr/asm/p/asm_x86_nz.c in radare2 before 3.1.0 allows attackers to cause a denial of service (buffer over-read) via crafted x86 assembly data, as demonstrated by rasm2.

Project: radare2

static int opinc(RAsm *a, ut8 *data, const Opcode *op) {
	if (op->operands[1].type) {
		eprintf ("Error: Invalid operands\n");
		return -1;
	}
	int l = 0;
	int size = op->operands[0].type & ALL_SIZE;
	if (op->operands[0].explicit_size) {
		size = op->operands[0].dest_size;
	}

	if (size & OT_WORD) {
		data[l++] = 0x66;
	}

	int rex = 1 << 6;
	bool use_rex = false;
	if (size & OT_QWORD) {			//W field
		use_rex = true;
		rex |= 1 << 3;
	}
	if (op->operands[0].extended) {		//B field
		use_rex = true;
		rex |= 1;
	}

	int opcode;
	if (size & OT_BYTE) {
		opcode = 0xfe;
	} else {
		opcode = 0xff;
	}

	if (!(op->operands[0].type & OT_MEMORY)) {
		if (use_rex) {
			data[l++] = rex;
		}
		if (a->bits > 32 || size & OT_BYTE) {
			data[l++] = opcode;
		}
		if (a->bits == 32 && size & (OT_DWORD | OT_WORD)) {
			data[l++] = 0x40 | op->operands[0].reg;
		} else {
			data[l++] = 0xc0 | op->operands[0].reg;
		}
		return l;
	}

	bool rip_rel = op->operands[0].regs[0] == X86R_RIP;
	int offset = op->operands[0].offset * op->operands[0].offset_sign;
	int modrm = 0;
	int mod;
	int reg = 0;
	int rm;
	bool use_sib = false;
	int sib;
	if (offset == 0) {
		mod = 0;
	} else if (offset < 128 && offset > -129) {
		mod = 1;
	} else {
		mod = 2;
	}

	if (op->operands[0].regs[0] & OT_WORD) {
		if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_SI) {
			rm = B0000;
		} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == X86R_DI) {
			rm = B0001;
		} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_SI) {
			rm = B0010;
		} else if (op->operands[0].regs[0] == X86R_BP && op->operands[0].regs[1] == X86R_DI) {
			rm = B0011;
		} else if (op->operands[0].regs[0] == X86R_SI && op->operands[0].regs[1] == -1) {
			rm = B0100;
		} else if (op->operands[0].regs[0] == X86R_DI && op->operands[0].regs[1] == -1) {
			rm = B0101;
		} else if (op->operands[0].regs[0] == X86R_BX && op->operands[0].regs[1] == -1) {
			rm = B0111;
		} else {
			return -1;
		}
		modrm = (mod << 6) | (reg << 3) | rm;
	} else {
		if (op->operands[0].extended) {
			rm = op->operands[0].reg;
		} else {
			rm = op->operands[0].regs[0];
		}
		if (rm == 5 && mod == 0) {
			mod = 1;
		}

		int index = op->operands[0].regs[1];
		int scale = getsib(op->operands[0].scale[1]);
		if (index != -1) {
			use_sib = true;
			sib = (scale << 6) | (index << 3) | rm;
		} else if (rm == 4) {
			use_sib = true;
			sib = 0x24;
		}
		if (use_sib) {
			rm = B0100;
		}
		if (rip_rel) {
			modrm = (B0000 << 6) | (reg << 3) | B0101;
			sib = (scale << 6) | (B0100 << 3) | B0101;
		} else {
			modrm = (mod << 6) | (reg << 3) | rm;
		}
	}

	if (use_rex) {
		data[l++] = rex;
	}
	data[l++] = opcode;
	data[l++] = modrm;
	if (use_sib) {
		data[l++] = sib;
	}
	if (mod == 1) {
		data[l++] = offset;
	} else if (op->operands[0].regs[0] & OT_WORD && mod == 2) {
		data[l++] = offset;
		data[l++] = offset >> 8;
	} else if (mod == 2 || rip_rel) {
		data[l++] = offset;
		data[l++] = offset >> 8;
		data[l++] = offset >> 16;
		data[l++] = offset >> 24;
	}

	return l;
}

Project: radare2

static int oprep(RAsm *a, ut8 *data, const Opcode *op) {
	int l = 0;
	LookupTable *lt_ptr;
	int retval;
	if (!strcmp (op->mnemonic, "rep") ||
	    !strcmp (op->mnemonic, "repe") ||
	    !strcmp (op->mnemonic, "repz")) {
		data[l++] = 0xf3;
	} else if (!strcmp (op->mnemonic, "repne") ||
	           !strcmp (op->mnemonic, "repnz")) {
		data[l++] = 0xf2;
	}
	Opcode instr = {0};
	parseOpcode (a, op->operands[0].rep_op, &instr);

	for (lt_ptr = oplookup; strcmp (lt_ptr->mnemonic, "null"); lt_ptr++) {
		if (!r_str_casecmp (instr.mnemonic, lt_ptr->mnemonic)) {
			if (lt_ptr->opcode > 0) {
				if (lt_ptr->only_x32 && a->bits == 64) {
					return -1;
				}
				ut8 *ptr = (ut8 *)&lt_ptr->opcode;
				int i = 0;
				for (; i < lt_ptr->size; i++) {
					data[i + l] = ptr[lt_ptr->size - (i + 1)];
				}
				free (instr.mnemonic);
				return l + lt_ptr->size;
			} else {
				if (lt_ptr->opdo) {
					data += l;
					if (instr.has_bnd) {
						data[l] = 0xf2;
						data++;
					}
					retval = lt_ptr->opdo (a, data, &instr);
					if (instr.has_bnd) {
						retval++;
					}
					return l + retval;
				}
				break;
			}
		}
	}
	free (instr.mnemonic);
	return -1;
}

Project: radare2

static int parseOperand(RAsm *a, const char *str, Operand *op, bool isrepop) {
	size_t pos, nextpos = 0;
	x86newTokenType last_type;
	int size_token = 1;
	bool explicit_size = false;
	int reg_index = 0;
	op->type = 0;
	while (size_token) {
		pos = nextpos;
		last_type = getToken (str, &pos, &nextpos);

		if (!r_str_ncasecmp (str + pos, "ptr", 3)) {
			continue;
		} else if (!r_str_ncasecmp (str + pos, "byte", 4)) {
			op->type |= OT_MEMORY | OT_BYTE;
			op->dest_size = OT_BYTE;
			explicit_size = true;
		} else if (!r_str_ncasecmp (str + pos, "word", 4)) {
			op->type |= OT_MEMORY | OT_WORD;
			op->dest_size = OT_WORD;
			explicit_size = true;
		} else if (!r_str_ncasecmp (str + pos, "dword", 5)) {
			op->type |= OT_MEMORY | OT_DWORD;
			op->dest_size = OT_DWORD;
			explicit_size = true;
		} else if (!r_str_ncasecmp (str + pos, "qword", 5)) {
			op->type |= OT_MEMORY | OT_QWORD;
			op->dest_size = OT_QWORD;
			explicit_size = true;
		} else if (!r_str_ncasecmp (str + pos, "oword", 5)) {
			op->type |= OT_MEMORY | OT_OWORD;
			op->dest_size = OT_OWORD;
			explicit_size = true;
		} else if (!r_str_ncasecmp (str + pos, "tbyte", 5)) {
			op->type |= OT_MEMORY | OT_TBYTE;
			op->dest_size = OT_TBYTE;
			explicit_size = true;
		} else { // the current token doesn't denote a size
			size_token = 0;
		}
	}

	if (str[pos] == '[') {
		if (!op->type) {
			op->type = OT_MEMORY;
		}
		op->offset = op->scale[0] = op->scale[1] = 0;

		ut64 temp = 1;
		Register reg = X86R_UNDEFINED;
		bool first_reg = true;
		while (str[pos] != ']') {
			if (pos > nextpos) {
				break;
			}
			pos = nextpos;
			if (!str[pos]) {
				break;
			}
			last_type = getToken (str, &pos, &nextpos);

			if (last_type == TT_SPECIAL) {
				if (str[pos] == '+' || str[pos] == '-' || str[pos] == ']') {
					if (reg != X86R_UNDEFINED) {
						op->regs[reg_index] = reg;
						op->scale[reg_index] = temp;
						++reg_index;
					} else {
						op->offset += temp;
						op->regs[reg_index] = X86R_UNDEFINED;
					}

					temp = 1;
					reg = X86R_UNDEFINED;
				} else if (str[pos] == '*') {

				}
			}
			else if (last_type == TT_WORD) {
				ut32 reg_type = 0;

				if (reg != X86R_UNDEFINED) {
					op->type = 0;	// Make the result invalid
				}

				nextpos = pos;
				reg = parseReg (a, str, &nextpos, &reg_type);

				if (first_reg) {
					op->extended = false;
					if (reg > 8) {
						op->extended = true;
						op->reg = reg - 9;
					}
					first_reg = false;
				} else if (reg > 8) {
					op->reg = reg - 9;
				}
				if (reg_type & OT_REGTYPE & OT_SEGMENTREG) {
					op->reg = reg;
					op->type = reg_type;
					parse_segment_offset (a, str, &nextpos, op, reg_index);
					return nextpos;
				}

				if (!explicit_size) {
					op->type |= reg_type;
				}
				op->reg_size = reg_type;
				op->explicit_size = explicit_size;

				if (!(reg_type & OT_GPREG)) {
					op->type = 0;	// Make the result invalid
				}
			}
			else {
				char *p = strchr (str, '+');
				op->offset_sign = 1;
				if (!p) {
					p = strchr (str, '-');
					if (p) {
						op->offset_sign = -1;
					}
				}
				char * plus = strchr (str, '+');
				char * minus = strchr (str, '-');
				char * closeB = strchr (str, ']');
				if (plus && minus && plus < closeB && minus < closeB) {
					op->offset_sign = -1;
				}
				char *tmp;
				tmp = malloc (strlen (str + pos) + 1);
				strcpy (tmp, str + pos);
				strtok (tmp, "+-");
				st64 read = getnum (a, tmp);
				free (tmp);
				temp *= read;
			}
		}
	} else if (last_type == TT_WORD) {   // register
		nextpos = pos;
		RFlagItem *flag;

		if (isrepop) {
			op->is_good_flag = false;
			strncpy (op->rep_op, str, MAX_REPOP_LENGTH - 1);
			op->rep_op[MAX_REPOP_LENGTH - 1] = '\0';
			return nextpos;
		}

		op->reg = parseReg (a, str, &nextpos, &op->type);

		op->extended = false;
		if (op->reg > 8) {
			op->extended = true;
			op->reg -= 9;
		}
		if (op->type & OT_REGTYPE & OT_SEGMENTREG) {
			parse_segment_offset (a, str, &nextpos, op, reg_index);
			return nextpos;
		}
		if (op->reg == X86R_UNDEFINED) {
			op->is_good_flag = false;
			if (a->num && a->num->value == 0) {
				return nextpos;
			}
			op->type = OT_CONSTANT;
			RCore *core = a->num? (RCore *)(a->num->userptr): NULL;
			if (core && (flag = r_flag_get (core->flags, str))) {
				op->is_good_flag = true;
			}

			char *p = strchr (str, '-');
			if (p) {
				op->sign = -1;
				str = ++p;
			}
			op->immediate = getnum (a, str);
		} else if (op->reg < X86R_UNDEFINED) {
			strncpy (op->rep_op, str, MAX_REPOP_LENGTH - 1);
			op->rep_op[MAX_REPOP_LENGTH - 1] = '\0';
		}
	} else {                             // immediate
		op->type = OT_CONSTANT;
		op->sign = 1;
		char *p = strchr (str, '-');
		if (p) {
			op->sign = -1;
			str = ++p;
		}
		op->immediate = getnum (a, str);
	}

	return nextpos;
}