CVE-2022-0849: Use After Free (CWE-416)
value: Use After Free in r_reg_get_name_idx in GitHub repository radareorg/radare2 prior to 5.6.6.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 5.5
Published: 2022-03-05T10:15Z
Repository: https://github.com/radareorg/radare2
Language: C
Files changed: libr/anal/arch/kvx/kvx-dis.c, libr/core/canal.c
Commit date: 2022-03-04 11:11:49 +0100
Commit message: aaef on arm/thumb switches causes uaf ##crash

* Reported by peacock-doris via huntr.dev
* Reproducer: poc_uaf_r_reg_get

Diff: diff --git a/libr/anal/arch/kvx/kvx-dis.c b/libr/anal/arch/kvx/kvx-dis.c
index e25d3943fd0..083511386e5 100644
--- a/libr/anal/arch/kvx/kvx-dis.c
+++ b/libr/anal/arch/kvx/kvx-dis.c
@@ -92,13 +92,15 @@ static inline int kvx_has_parallel_bit(ut32 x) {
 static inline int kvx_is_tca_opcode(ut32 x) {
 	unsigned major = ((x)>>24) & 0x1F;
 	return (major > 1) && (major < 8);
 }
 
+#if UNUSED0
 static inline int kvx_is_nop_opcode(ut32 x) {
 	return ((x)<<1) == 0xFFFFFFFE;
 }
+#endif
 
 static inline int kvx_opc_match(const opc_t *opc, insn_t *insn) {
 	int i;
 
 	if (opc->len != insn->len)
diff --git a/libr/core/canal.c b/libr/core/canal.c
index 9b4ded95c1f..408f3bc0b7e 100644
--- a/libr/core/canal.c
+++ b/libr/core/canal.c
@@ -1,6 +1,6 @@
-/* radare - LGPL - Copyright 2009-2021 - pancake, nibble */
+/* radare - LGPL - Copyright 2009-2022 - pancake, nibble */
 
 #include <r_types.h>
 #include <r_list.h>
 #include <r_flag.h>
 #include <r_core.h>
@@ -4725,11 +4725,11 @@ static bool myvalid(RIO *io, ut64 addr) {
 }
 
 typedef struct {
 	RAnalOp *op;
 	RAnalFunction *fcn;
-	const char *spname;
+	char *spname;
 	ut64 initial_sp;
 } EsilBreakCtx;
 
 static const char *reg_name_for_access(RAnalOp* op, RAnalVarAccessType type) {
 	if (type == R_ANAL_VAR_ACCESS_TYPE_WRITE) {
@@ -5097,11 +5097,11 @@ R_API void r_core_anal_esil(RCore *core, const char *str, const char *target) {
 	bool cfg_anal_strings = r_config_get_i (core->config, "anal.strings");
 	bool emu_lazy = r_config_get_i (core->config, "emu.lazy");
 	bool gp_fixed = r_config_get_i (core->config, "anal.gpfixed");
 	RAnalEsil *ESIL = core->anal->esil;
 	ut64 refptr = 0LL;
-	const char *pcname;
+	char *pcname = NULL;
 	RAnalOp op = R_EMPTY;
 	ut8 *buf = NULL;
 	bool end_address_set = false;
 	int iend;
 	int minopsize = 4; // XXX this depends on asm->mininstrsize
@@ -5186,15 +5186,16 @@ R_API void r_core_anal_esil(RCore *core, const char *str, const char *target) {
 			return;
 		}
 		r_core_cmd0 (core, "aeim");
 		ESIL = core->anal->esil;
 	}
-	const char *spname = r_reg_get_name (core->anal->reg, R_REG_NAME_SP);
-	if (!spname) {
+	const char *kspname = r_reg_get_name (core->anal->reg, R_REG_NAME_SP);
+	if (R_STR_ISEMPTY (kspname)) {
 		eprintf ("Error: No =SP defined in the reg profile.\n");
 		return;
 	}
+	char *spname = strdup (kspname);
 	EsilBreakCtx ctx = {
 		&op,
 		fcn,
 		spname,
 		r_reg_getv (core->anal->reg, spname)
@@ -5208,15 +5209,16 @@ R_API void r_core_anal_esil(RCore *core, const char *str, const char *target) {
 	if (fcn && fcn->reg_save_area) {
 		r_reg_setv (core->anal->reg, ctx.spname, ctx.initial_sp - fcn->reg_save_area);
 	}
 	//eprintf ("Analyzing ESIL refs from 0x%"PFMT64x" - 0x%"PFMT64x"\n", addr, end);
 	// TODO: backup/restore register state before/after analysis
-	pcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
-	if (!pcname || !*pcname) {
+	const char *kpcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
+	if (!kpcname || !*kpcname) {
 		eprintf ("Cannot find program counter register in the current profile.\n");
 		return;
 	}
+	pcname = strdup (kpcname);
 	esil_anal_stop = false;
 	r_cons_break_push (cccb, core);
 
 	int arch = -1;
 	if (!strcmp (core->anal->cur->arch, "arm")) {
@@ -5297,15 +5299,13 @@ R_API void r_core_anal_esil(RCore *core, const char *str, const char *target) {
 		}
 
 		r_anal_op_fini (&op);
 		r_asm_set_pc (core->rasm, cur);
 		i_old = i;
-#if 1
 		if (i > iend) {
 			goto repeat;
 		}
-#endif
 		if (!r_anal_op (core->anal, &op, cur, buf + i, iend - i, R_ANAL_OP_MASK_ESIL | R_ANAL_OP_MASK_VAL | R_ANAL_OP_MASK_HINT)) {
 			i += minopsize - 1; //   XXX dupe in op.size below
 		}
 		if (op.type == R_ANAL_OP_TYPE_ILL || op.type == R_ANAL_OP_TYPE_UNK) {
 			// i += 2
@@ -5542,10 +5542,12 @@ repeat:
 		}
 		if (i >= iend) {
 			break;
 		}
 	} while (get_next_i (&ictx, &i));
+	free (pcname);
+	free (spname);
 	r_list_free (ictx.bbl);
 	r_list_free (ictx.path);
 	r_list_free (ictx.switch_path);
 	free (buf);
 	ESIL->cb.hook_mem_read = NULL;

Vulnerable code: /* radare - LGPL - Copyright 2009-2021 - pancake, nibble */
	const char *spname;
	const char *pcname;
	const char *spname = r_reg_get_name (core->anal->reg, R_REG_NAME_SP);
	if (!spname) {
	pcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
	if (!pcname || !*pcname) {
#if 1
#endif
Fixed code: #if UNUSED0
#endif
/* radare - LGPL - Copyright 2009-2022 - pancake, nibble */
	char *spname;
	char *pcname = NULL;
	const char *kspname = r_reg_get_name (core->anal->reg, R_REG_NAME_SP);
	if (R_STR_ISEMPTY (kspname)) {
	char *spname = strdup (kspname);
	const char *kpcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
	if (!kpcname || !*kpcname) {
	pcname = strdup (kpcname);
	free (pcname);
	free (spname);