Commit message: Fix #10294 - crash in r2_hoobr__6502_op
CVE: CVE-2018-12322
CWE: CWE-125
Vulnerability description: There is a heap out of bounds read in radare2 2.6.0 in _6502_op() in libr/anal/p/anal_6502.c via a crafted iNES ROM binary file.

Project: radare2

static void _6502_anal_esil_ccall(RAnalOp *op, ut8 data0)
{
	char *flag;
	switch(data0) {
	case 0x10: // bpl $ffff
		flag = "N,!";
		break;
	case 0x30: // bmi $ffff
		flag = "N";
		break;
	case 0x50: // bvc $ffff
		flag = "V,!";
		break;
	case 0x70: // bvs $ffff
		flag = "V";
		break;
	case 0x90: // bcc $ffff
		flag = "C,!";
		break;
	case 0xb0: // bcs $ffff
		flag = "C";
		break;
	case 0xd0: // bne $ffff
		flag = "Z,!";
		break;
	case 0xf0: // beq $ffff
		flag = "Z";
		break;
	default:
		flag = "unk";
		break;
	}
	r_strbuf_setf (&op->esil, "%s,?{,0x%04x,pc,=,}", flag, (op->jump & 0xffff));
}

Project: radare2
File: anal_6502.c

static void _6502_anal_esil_flags(RAnalOp *op, ut8 data0) {
	int enabled=0;
	char flag ='u';
	switch(data0) {
	case 0x78: // sei
		enabled = 1;
		flag = 'I';
		break;
	case 0x58: // cli
		enabled = 0;
		flag = 'I';
		break;
	case 0x38: // sec
		enabled = 1;
		flag = 'C';
		break;
	case 0x18: // clc
		enabled = 0;
		flag = 'C';
		break;
	case 0xf8: // sed
		enabled = 1;
		flag = 'D';
		break;
	case 0xd8: // cld
		enabled = 0;
		flag = 'D';
		break;
	case 0xb8: // clv
		enabled = 0;
		flag = 'V';
		break;
		break;
	}
	r_strbuf_setf (&op->esil, "%d,%c,=", enabled, flag);
}

Project: radare2

static void _6502_anal_esil_get_addr_pattern1(RAnalOp *op, const ut8* data, char* addrbuf, int addrsize) {
	switch(data[0] & 0x1f) { // 0x1f = b00111111
	case 0x09: // op #$ff
		op->cycles = 2;
		snprintf (addrbuf, addrsize,"0x%02x", data[1]);
		break;
	case 0x05: // op $ff
		op->cycles = 3;
		snprintf (addrbuf, addrsize,"0x%02x", data[1]);
		break;
	case 0x15: // op $ff,x
		op->cycles = 4;
		snprintf (addrbuf, addrsize,"x,0x%02x,+", data[1]);
		break;
	case 0x0d: // op $ffff
		op->cycles = 4;
		snprintf (addrbuf, addrsize,"0x%04x", data[1] | data[2] << 8);
		break;
	case 0x1d: // op $ffff,x
		op->cycles = 4;
		snprintf (addrbuf, addrsize,"x,0x%04x,+", data[1] | data[2] << 8);
		break;
	case 0x19: // op $ffff,y
		op->cycles = 4;
		snprintf (addrbuf, addrsize,"y,0x%04x,+", data[1] | data[2] << 8);
		break;
	case 0x01: // op ($ff,x)
		op->cycles = 6;
		snprintf (addrbuf, addrsize,"x,0x%02x,+,[2]", data[1]);
		break;
	case 0x11: // op ($ff),y
		op->cycles = 5;
		snprintf (addrbuf, addrsize,"y,0x%02x,[2],+", data[1]);
		break;
	}
}

Project: radare2

static void _6502_anal_esil_get_addr_pattern2(RAnalOp *op, const ut8* data, char* addrbuf, int addrsize, char reg) {
	switch(data[0] & 0x1f) { // 0x1f = b00111111
	case 0x02: // op #$ff
		op->cycles = 2;
		snprintf (addrbuf, addrsize, "0x%02x", data[1]);
		break;
	case 0x0a: //op a
		op->cycles = 2;
		snprintf (addrbuf, addrsize, "a");
		break;
	case 0x06: // op $ff
		op->cycles = 5;
		snprintf (addrbuf, addrsize, "0x%02x", data[1]);
		break;
	case 0x16: // op $ff,x
		op->cycles = 6;
		snprintf (addrbuf, addrsize, "%c,0x%02x,+", reg, data[1]);
		break;
	case 0x0e: // op $ffff
		op->cycles = 6;
		snprintf (addrbuf, addrsize, "0x%04x", data[1] | data[2] << 8);
		break;
	case 0x1e: // op $ffff,x
		op->cycles = 7;
		snprintf (addrbuf, addrsize, "%c,0x%04x,+", reg, data[1] | data[2] << 8);
		break;
	}
}

Project: radare2

static void _6502_anal_esil_get_addr_pattern3(RAnalOp *op, const ut8* data, char* addrbuf, int addrsize, char reg) {
	switch (data[0] & 0x1f) { // 0x1f = b00111111
	case 0x00: // op #$ff
		op->cycles = 2;
		snprintf (addrbuf, addrsize, "0x%02x", data[1]);
		break;
	case 0x08: //op a
		op->cycles = 2;
		snprintf (addrbuf, addrsize, "a");
		break;
	case 0x04: // op $ff
		op->cycles = 5;
		snprintf (addrbuf, addrsize, "0x%02x", data[1]);
		break;
	case 0x14: // op $ff,x
		op->cycles = 6;
		snprintf (addrbuf, addrsize, "%c,0x%02x,+", reg, data[1]);
		break;
	case 0x0c: // op $ffff
		op->cycles = 6;
		snprintf (addrbuf, addrsize, "0x%04x", data[1] | data[2] << 8);
		break;
	case 0x1c: // op $ffff,x
		op->cycles = 7;
		snprintf (addrbuf, addrsize, "%c,0x%04x,+", reg, data[1] | data[2] << 8);
		break;
	}
}

Project: radare2
File: anal_6502.c

static void _6502_anal_esil_inc_reg(RAnalOp *op, ut8 data0, char* sign)
{
	char* reg = NULL;

	switch(data0) {
	case 0xe8: // inx
	case 0xca: // dex
		reg = "x";
		break;
	case 0xc8: // iny
	case 0x88: // dey
		reg = "y";
		break;
	}
	r_strbuf_setf (&op->esil, "%s,%s%s=", reg, sign, sign);
	_6502_anal_update_flags (op, _6502_FLAGS_NZ);
}

Project: radare2

static void _6502_anal_esil_pop(RAnalOp *op, ut8 data0) {
	char *reg = (data0==0x28) ? "flags" : "a";
	r_strbuf_setf (&op->esil, "sp,++=,sp,0x100,+,[1],%s,=", reg);

	if (data0==0x68) _6502_anal_update_flags (op, _6502_FLAGS_NZ);
}

Project: radare2

static void _6502_anal_esil_push(RAnalOp *op, ut8 data0) {
	char *reg = (data0==0x08) ? "flags" : "a";
	r_strbuf_setf (&op->esil, "%s,sp,0x100,+,=[1],sp,--=", reg);
}

Project: radare2
File: anal_6502.c

static void _6502_anal_update_flags(RAnalOp *op, int flags) {
	/* FIXME: $b9 instead of $b8 to prevent the bug triggered by: A = 0 - 0xff - 1 */
	if (flags & _6502_FLAGS_B) r_strbuf_append (&op->esil, ",$b9,C,=");
	if (flags & _6502_FLAGS_C) r_strbuf_append (&op->esil, ",$c7,C,=");
	if (flags & _6502_FLAGS_Z) r_strbuf_append (&op->esil, ",$z,Z,=");
	if (flags & _6502_FLAGS_N) r_strbuf_append (&op->esil, ",$s,N,=");
}

Project: radare2
File: anal_6502.c

static int esil_6502_fini (RAnalEsil *esil) {
	return true;
}

Project: radare2
File: anal_6502.c

static int esil_6502_init (RAnalEsil *esil) {
	if (esil->anal && esil->anal->reg) {		//initial values
		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "pc", -1), 0x0000);
		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "sp", -1), 0xff);
		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "a", -1), 0x00);
		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "x", -1), 0x00);
		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "y", -1), 0x00);
		r_reg_set_value (esil->anal->reg, r_reg_get (esil->anal->reg, "flags", -1), 0x00);
	}
	return true;
}

Project: radare2

static int set_reg_profile(RAnal *anal) {
	char *p =
		"=PC	pc\n"
		"=SP	sp\n"
		"gpr	a	.8	0	0\n"
		"gpr	x	.8	1	0\n"
		"gpr	y	.8	2	0\n"

		"gpr	flags	.8	3	0\n"
		"gpr	C	.1	.24	0\n"
		"gpr	Z	.1	.25	0\n"
		"gpr	I	.1	.26	0\n"
		"gpr	D	.1	.27	0\n"
		"gpr	V	.1	.30	0\n"
		"gpr	N	.1	.31	0\n"
		"gpr	sp	.8	4	0\n"
		"gpr	pc	.16	5	0\n";
	return r_reg_set_profile_string (anal->reg, p);
}