CVE-2023-4322: Out-of-bounds Write (CWE-787)
value: Heap-based Buffer Overflow in GitHub repository radareorg/radare2 prior to 5.9.0.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2023-08-14T16:15Z
Repository: https://github.com/radareorg/radare2
Language: C
Files changed: libr/arch/p/bf/plugin.c, libr/core/cmd_anal.c
Commit date: 2023-08-14 16:33:25 +0200
Commit message: Fix 1byte heap oobread in the brainfuck disassembler

* https://huntr.dev/bounties/06e2484c-d6f1-4497-af67-26549be9fffd/
* Kudos to @7resp4ss for reporting

Diff: diff --git a/libr/arch/p/bf/plugin.c b/libr/arch/p/bf/plugin.c
index e2f99e949a73..87ebd3cb014e 100644
--- a/libr/arch/p/bf/plugin.c
+++ b/libr/arch/p/bf/plugin.c
@@ -11,12 +11,12 @@ static size_t countChar(const ut8 *buf, int len, char ch) {
 	}
 	return i;
 }
 
 static int getid(char ch) {
-	const char *keys = "[]<>+-,.";
-	const char *cidx = strchr (keys, ch);
+	const char *const keys = "[]<>+-,.";
+	const char *const cidx = strchr (keys, ch);
 	return cidx? cidx - keys + 1: 0;
 }
 
 static int disassemble(RAnalOp *op, const ut8 *buf, int len) {
 	const ut8 *b;
@@ -134,17 +134,15 @@ static int assemble(const char *buf, ut8 **outbuf) {
 }
 
 #define BUFSIZE_INC 32
 static bool decode(RArchSession *as, RAnalOp *op, RArchDecodeMask mask) {
 	int len = op->size;
-	const ut8 *_buf = op->bytes;
-	const ut64 addr = op->addr;
 	if (len < 1) {
 		return false;
 	}
-
-	ut8 *buf = (ut8*)_buf; // XXX
+	ut8 *buf = op->bytes;
+	const ut64 addr = op->addr;
 	ut64 dst = 0LL;
 	if (!op) {
 		return 1;
 	}
 	if (mask & R_ARCH_OP_MASK_DISASM) {
@@ -167,33 +165,36 @@ static bool decode(RArchSession *as, RAnalOp *op, RArchDecodeMask mask) {
 			buf = malloc (0xff);
 			read_at (bin->iob.io, op->addr, buf, 0xff);
 		}
 		r_strbuf_set (&op->esil, "1,pc,-,brk,=[4],4,brk,+=");
 #if 1
-		{
+		if (len > 1) {
 			const ut8 *p = buf + 1;
 			int lev = 0, i = 1;
 			len--;
 			while (i < len && *p) {
-				if (*p == '[') {
+				switch (*p) {
+				case '[':
 					lev++;
-				}
-				if (*p == ']') {
+					break;
+				case ']':
 					lev--;
-					if (lev == -1) {
-						dst = addr + (size_t)(p - buf) + 1;
+					if (lev < 1) {
+						size_t delta = p - buf;
+						dst = addr + (size_t)delta + 1;
 						op->jump = dst;
 						r_strbuf_set (&op->esil, "1,pc,-,brk,=[4],4,brk,+=,");
 						goto beach;
 					}
-				}
-				if (*p == 0x00 || *p == 0xff) {
+					break;
+				case 0:
+				case 0xff:
 					op->type = R_ANAL_OP_TYPE_ILL;
 					goto beach;
 				}
 				if (read_at && i == len - 1) {
-					break;
+#if 0
 					// XXX unnecessary just break
 					int new_buf_len = len + 1 + BUFSIZE_INC;
 					ut8 *new_buf = calloc (new_buf_len, 1);
 					if (new_buf) {
 						free (buf);
@@ -201,10 +202,13 @@ static bool decode(RArchSession *as, RAnalOp *op, RArchDecodeMask mask) {
 						buf = new_buf;
 						read_at (bin->iob.io, op->addr + i, buf + i, 0xff);
 						p = buf + i;
 						len += BUFSIZE_INC;
 					}
+#else
+					break;
+#endif
 				}
 				p++;
 				i++;
 			}
 		}
diff --git a/libr/core/cmd_anal.c b/libr/core/cmd_anal.c
index 061bf210d743..bc194859150d 100644
--- a/libr/core/cmd_anal.c
+++ b/libr/core/cmd_anal.c
@@ -8717,13 +8717,13 @@ static void _anal_calls(RCore *core, ut64 addr, ut64 addr_end, bool printCommand
 				RBinReloc *rel = r_core_getreloc (core, addr, op.size);
 				if (rel && (rel->import || rel->symbol)) {
 					isValidCall = false;
 				}
 				if (isValidCall) {
-					ut8 buf[4] = {0};
-					r_io_read_at (core->io, op.jump, buf, 4);
-					isValidCall = memcmp (buf, "\x00\x00\x00\x00", 4);
+					ut8 zbuf[4] = {0};
+					r_io_read_at (core->io, op.jump, zbuf, 4);
+					isValidCall = memcmp (zbuf, "\x00\x00\x00\x00", 4);
 				}
 				if (isValidCall) {
 #if JAYRO_03
 					if (!anal_is_bad_call (core, from, to, addr, buf, bufi)) {
 						fcn = r_anal_get_fcn_in (core->anal, op.jump, R_ANAL_FCN_TYPE_ROOT);
@@ -8815,13 +8815,12 @@ static void cmd_anal_calls(RCore *core, const char *input, bool printCommands, b
 	r_cons_break_pop ();
 	r_list_free (ranges);
 }
 
 static void cmd_sdbk(Sdb *db, const char *input) {
-	char *out = (input[0] == ' ')
-		? sdb_querys (db, NULL, 0, input + 1)
-		: sdb_querys (db, NULL, 0, "*");
+	const char *arg = (input[0] == ' ')? input + 1: "*";
+	char *out = sdb_querys (db, NULL, 0, arg);
 	if (out) {
 		r_cons_println (out);
 		free (out);
 	} else {
 		R_LOG_ERROR ("Usage: ask [query]");
@@ -9116,11 +9115,11 @@ static void anal_axg(RCore *core, const char *input, int level, Sdb *db, int opt
 	}
 	RVecAnalRef_free (xrefs);
 }
 
 static void cmd_anal_ucall_ref(RCore *core, ut64 addr) {
-	RAnalFunction * fcn = r_anal_get_function_at (core->anal, addr);
+	RAnalFunction *fcn = r_anal_get_function_at (core->anal, addr);
 	if (fcn) {
 		r_cons_printf (" ; %s", fcn->name);
 	} else {
 		r_cons_printf (" ; 0x%" PFMT64x, addr);
 	}
@@ -9255,11 +9254,10 @@ static void axfm(RCore *core) {
 			r_cons_printf ("  0x%"PFMT64x": %s\n", ref->at, name? name: "?");
 
 			last_addr = ref->addr;
 		}
 	}
-
 	RVecAnalRef_free (refs);
 }
 
 static bool cmd_anal_refs(RCore *core, const char *input) {
 	bool be = R_ARCH_CONFIG_IS_BIG_ENDIAN (core->print->config);

Vulnerable code: 	const char *keys = "[]<>+-,.";
	const char *cidx = strchr (keys, ch);
	const ut8 *_buf = op->bytes;
	const ut64 addr = op->addr;

	ut8 *buf = (ut8*)_buf; // XXX
		{
				if (*p == '[') {
				}
				if (*p == ']') {
					if (lev == -1) {
						dst = addr + (size_t)(p - buf) + 1;
				}
				if (*p == 0x00 || *p == 0xff) {
					break;
					ut8 buf[4] = {0};
					r_io_read_at (core->io, op.jump, buf, 4);
					isValidCall = memcmp (buf, "\x00\x00\x00\x00", 4);
	char *out = (input[0] == ' ')
		? sdb_querys (db, NULL, 0, input + 1)
		: sdb_querys (db, NULL, 0, "*");
	RAnalFunction * fcn = r_anal_get_function_at (core->anal, addr);

Fixed code: 	const char *const keys = "[]<>+-,.";
	const char *const cidx = strchr (keys, ch);
	ut8 *buf = op->bytes;
	const ut64 addr = op->addr;
		if (len > 1) {
				switch (*p) {
				case '[':
					break;
				case ']':
					if (lev < 1) {
						size_t delta = p - buf;
						dst = addr + (size_t)delta + 1;
					break;
				case 0:
				case 0xff:
#if 0
#else
					break;
#endif
					ut8 zbuf[4] = {0};
					r_io_read_at (core->io, op.jump, zbuf, 4);
					isValidCall = memcmp (zbuf, "\x00\x00\x00\x00", 4);
	const char *arg = (input[0] == ' ')? input + 1: "*";
	char *out = sdb_querys (db, NULL, 0, arg);
	RAnalFunction *fcn = r_anal_get_function_at (core->anal, addr);