Commit message: Fix #7698 - UAF in r_config_set when loading a dex
CVE: CVE-2017-9520
CWE: CWE-416
Vulnerability description: The r_config_set function in libr/config/config.c in radare2 1.5.0 allows remote attackers to cause a denial of service (use-after-free and application crash) via a crafted DEX file.

Project: radare2

static void _ht_node_free_kv(HtKv *kv) {
	free (kv->key);
	free (kv);
}

Project: radare2
File: config.c

static int cmp(RConfigNode *a, RConfigNode *b) {
	return strcmp (a->name, b->name);
}

Project: radare2
File: config.c

static void config_print_node(RConfig *cfg, RConfigNode *node, const char *pfx, const char *sfx, bool verbose, bool json) {
	char *option;
	bool isFirst;
	RListIter *iter;
	char *es = NULL;

	if (json) {
		if (verbose) {
			cfg->cb_printf ("{");
			cfg->cb_printf ("\"name\":\"%s\",", node->name);
			cfg->cb_printf ("\"value\":");
			config_print_value_json (cfg, node);
			cfg->cb_printf (",\"type\":\"%s\",", r_config_node_type (node));
			es = r_str_escape (node->desc);
			if (es) {
				cfg->cb_printf ("\"desc\":\"%s\",", es);
				free (es);
			}
			cfg->cb_printf ("\"ro\":%s", node->flags & CN_RO ? "true" : "false");
			if (!r_list_empty (node->options)) {
				isFirst = true;
				cfg->cb_printf (",\"options\":[");
				r_list_foreach (node->options, iter, option) {
					es = r_str_escape (option);
					if (es) {
						if (isFirst) {
							isFirst = false;
						} else {
							cfg->cb_printf (",");
						}
						cfg->cb_printf ("\"%s\"", es);
						free (es);
					}
				}
				cfg->cb_printf ("]");
			}
			cfg->cb_printf ("}");
		} else {
			cfg->cb_printf ("\"%s\":", node->name);
			config_print_value_json (cfg, node);
		}
	} else {
		if (verbose) {
			cfg->cb_printf ("%s%s = %s%s %s; %s", pfx,
				node->name, node->value, sfx, 
				node->flags & CN_RO ? "(ro)" : "", 
				node->desc);
			if (!r_list_empty (node->options)) {
				isFirst = true;
				cfg->cb_printf(" [");
				r_list_foreach (node->options, iter, option) {
					if (isFirst) {
						isFirst = false;
					} else {
						cfg->cb_printf(", ");
					}
					cfg->cb_printf("%s", option);
				}
				cfg->cb_printf("]");
			}
			cfg->cb_printf ("\n");
		} else {
			cfg->cb_printf ("%s%s = %s%s\n", pfx,
				node->name, node->value, sfx);
		}
	}
}

Project: radare2
File: config.c

static void config_print_value_json(RConfig *cfg, RConfigNode *node) {
	const char *val = node->value;
	if (!val) {
		val = "0";
	}
	char *sval = r_str_escape (val);
	if (node->flags & CN_BOOL || node->flags & CN_INT || node->flags & CN_OFFT) {
		if (!strncmp (val, "0x", 2)) {
			ut64 n = r_num_get (NULL, val);
			cfg->cb_printf ("%"PFMT64d, n);
		} else if (r_str_isnumber (val) || !strcmp (val, "true") || !strcmp (val, "false")) {
			cfg->cb_printf ("%s", val);
		} else {
			cfg->cb_printf ("\"%s\"", sval);
		}
	} else {
		cfg->cb_printf ("\"%s\"", sval);
	}
	free (sval);
}

Project: radare2
File: config.c

static bool is_bool(const char *s) {
	return !strcasecmp ("true", s) || !strcasecmp ("false", s);
}

Project: radare2
File: config.c

R_API void r_config_bump(RConfig *cfg, const char *key) {
	char *orig = strdup (r_config_get (cfg, key));
	r_config_set (cfg, key, orig);
	free (orig);
}

Project: radare2
File: config.c

R_API RConfig* r_config_clone(RConfig *cfg) {
	RListIter *iter;
	RConfigNode *node;
	RConfig *c = r_config_new (cfg->user);
	if (!c) {
		return NULL;
	}
	r_list_foreach (cfg->nodes, iter, node) {
		RConfigNode *nn = r_config_node_clone (node);
		ht_insert (c->ht, node->name, nn);
		r_list_append (c->nodes, nn);
		c->n_nodes++;
	}
	c->lock = cfg->lock;
	c->cb_printf = cfg->cb_printf;
	return c;
}

Project: radare2
File: config.c

R_API const char* r_config_desc(RConfig *cfg, const char *name, const char *desc) {
	RConfigNode *node = r_config_node_get (cfg, name);
	return r_config_node_desc (node, desc);
}

Project: radare2
File: config.c

R_API int r_config_eval(RConfig *cfg, const char *str) {
	char *ptr, *a, *b, name[1024];
	unsigned int len;
	if (!str || !cfg) {
		return false;
	}
	len = strlen (str) + 1;
	if (len >= sizeof (name)) {
		return false;
	}
	memcpy (name, str, len);
	str = r_str_chop (name);

	if (!str) {
		return false;
	}

	if (str[0] == '\0' || !strcmp (str, "help")) {
		r_config_list (cfg, NULL, 0);
		return false;
	}

	if (str[0] == '-') {
		r_config_rm (cfg, str + 1);
		return false;
	}

	ptr = strchr (str, '=');
	if (ptr) {
		/* set */
		ptr[0] = '\0';
		a = r_str_chop (name);
		b = r_str_chop (ptr + 1);
		(void) r_config_set (cfg, a, b);
	} else {
		char *foo = r_str_chop (name);
		if (foo[strlen (foo) - 1] == '.') {
			r_config_list (cfg, name, 0);
			return false;
		} else {
			/* get */
			const char *str = r_config_get (cfg, foo);
			if (str) {
				cfg->cb_printf ("%s\n",
					(((int) (size_t) str) == 1)? "true": str);
			}
		}
	}
	return true;
}

Project: radare2
File: config.c

R_API int r_config_free(RConfig *cfg) {
	if (!cfg) {
		return 0;
	}
	cfg->nodes->free = r_config_node_free; // damn
	r_list_free (cfg->nodes);
	ht_free (cfg->ht);
	free (cfg);
	return 0;
}

Project: radare2
File: config.c

R_API const char* r_config_get(RConfig *cfg, const char *name) {
	RConfigNode *node = r_config_node_get (cfg, name);
	if (node) {
		if (node->getter) {
			node->getter (cfg->user, node);
		}
		cfg->last_notfound = 0;
		if (node->flags & CN_BOOL) {
			return r_str_bool (is_true (node->value));
		}
		return node->value;
	} else {
		eprintf ("r_config_get: variable '%s' not found\n", name);
	}
	cfg->last_notfound = 1;
	return NULL;
}

Project: radare2
File: config.c

R_API ut64 r_config_get_i(RConfig *cfg, const char *name) {
	RConfigNode *node = r_config_node_get (cfg, name);
	if (node) {
		if (node->getter) {
			node->getter (cfg->user, node);
		}
		if (node->i_value || !strcmp (node->value, "false")) {
			return node->i_value;
		}
		return (ut64) r_num_math (cfg->num, node->value);
	}
	return (ut64) 0LL;
}

Project: radare2
File: config.c

R_API void r_config_hold_free(RConfigHold *h) {
	if (h) {
		r_list_free (h->list_num);
		r_list_free (h->list_char);
		R_FREE (h);
	}
}

Project: radare2
File: config.c

R_API RConfigHold* r_config_hold_new(RConfig *cfg) {
	if (cfg) {
		RConfigHold *hold = R_NEW0 (RConfigHold);
		if (hold) {
			hold->cfg = cfg;
			return hold;
		}
	}
	return NULL;
}

Project: radare2
File: config.c

R_API void r_config_list(RConfig *cfg, const char *str, int rad) {
	RConfigNode *node;
	RListIter *iter;
	const char *sfx = "";
	const char *pfx = "";
	int len = 0;
	bool verbose = false;
	bool json = false;
	bool isFirst = false;

	if (!STRNULL (str)) {
		str = r_str_chop_ro (str);
		len = strlen (str);
		if (len > 0 && str[0] == 'j') {
			str++;
			len--;
			json = true;
			rad = 'J';
		}
		if (len > 0 && str[0] == ' ') {
			str++;
			len--;
		}
		if (strlen (str) == 0) {
			str = NULL;
			len = 0;
		}
	}

	switch (rad) {
	case 1:
		pfx = "\"e ";
		sfx = "\"";
	/* fallthrou */
	case 0:
		r_list_foreach (cfg->nodes, iter, node) {
			if (!str || (str && (!strncmp (str, node->name, len)))) {
				config_print_node (cfg, node, pfx, sfx, verbose, json);
			}
		}
		break;
	case 2:
		r_list_foreach (cfg->nodes, iter, node) {
			if (!str || (str && (!strncmp (str, node->name, len)))) {
				if (!str || !strncmp (str, node->name, len)) {
					cfg->cb_printf ("%20s: %s\n", node->name,
						node->desc? node->desc: "");
				}
			}
		}
		break;
	case 'v':
		verbose = true;
		r_list_foreach (cfg->nodes, iter, node) {
			if (!str || (str && (!strncmp (str, node->name, len)))) {
				config_print_node (cfg, node, pfx, sfx, verbose, json);
			}
		}
		break;
	case 'q':
		r_list_foreach (cfg->nodes, iter, node) {
			cfg->cb_printf ("%s\n", node->name);
		}
		break;
	case 'J':
		verbose = true;
	/* fallthrou */
	case 'j':
		json = true;
		isFirst = true;
		if (verbose) {
			cfg->cb_printf ("[");
		} else {
			cfg->cb_printf ("{");
		}
		r_list_foreach (cfg->nodes, iter, node) {
			if (!str || (str && (!strncmp (str, node->name, len)))) {
				if (!str || !strncmp (str, node->name, len)) {
					if (isFirst) {
						isFirst = false;
					} else {
						cfg->cb_printf (",");
					}
					config_print_node (cfg, node, pfx, sfx, verbose, json);
				}
			}
		}
		if (verbose) {
			cfg->cb_printf ("]\n");
		} else {
			cfg->cb_printf ("}\n");
		}
		break;
	}
}

Project: radare2
File: config.c

R_API void r_config_lock(RConfig *cfg, int l) {
	r_list_sort (cfg->nodes, (RListComparator) cmp);
	cfg->lock = l;
}

Project: radare2
File: config.c

R_API RConfig* r_config_new(void *user) {
	RConfig *cfg = R_NEW0 (RConfig);
	if (!cfg) {
		return NULL;
	}
	cfg->ht = ht_new (NULL, _ht_node_free_kv, NULL);
	cfg->nodes = r_list_newf ((RListFree)r_config_node_free);
	if (!cfg->nodes) {
		R_FREE (cfg);
		return NULL;
	}
	cfg->user = user;
	cfg->num = NULL;
	cfg->n_nodes = 0;
	cfg->lock = 0;
	cfg->cb_printf = (void *) printf;
	return cfg;
}

Project: radare2
File: config.c

R_API const char* r_config_node_desc(RConfigNode *node, const char *desc) {
	if (node) {
		if (desc) {
			free (node->desc);
			node->desc = strdup (desc);
		}
		return node->desc;
	}
	return NULL;
}

Project: radare2
File: config.c

R_API void r_config_node_free(void *n) {
	RConfigNode *node = (RConfigNode *)n;
	if (!node) {
		return;
	}
	free (node->name);
	free (node->desc);
	free (node->value);
	r_list_free (node->options);
	free (node);
}

Project: radare2
File: config.c

R_API RConfigNode* r_config_node_get(RConfig *cfg, const char *name) {
	if (STRNULL (name)) {
		return NULL;
	}
	return ht_find (cfg->ht, name, NULL);
}

Project: radare2
File: config.c

R_API RConfigNode* r_config_node_new(const char *name, const char *value) {
	RConfigNode *node;
	if (STRNULL (name)) {
		return NULL;
	}
	node = R_NEW0 (RConfigNode);
	if (!node) {
		return NULL;
	}
	node->name = strdup (name);
	node->value = strdup (value? value: "");
	node->flags = CN_RW | CN_STR;
	node->i_value = r_num_get (NULL, value);
	node->options = r_list_new ();
	return node;
}

Project: radare2
File: config.c

R_API const char* r_config_node_type(RConfigNode *node) {
	if (node) {
		int f = node->flags;
		if (f & CN_BOOL) {
			return "bool";
		}
		if (f & CN_STR) {
			return "str";
		}
		if (f & CN_OFFT || f & CN_INT) {
			if (!strncmp (node->value, "0x", 2)) {
				return "addr";
			}
			return "int";
		}
	}
	return "";
}

Project: radare2
File: config.c

R_API int r_config_readonly(RConfig *cfg, const char *key) {
	RConfigNode *n = r_config_node_get (cfg, key);
	if (!n) {
		return false;
	}
	n->flags |= CN_RO;
	return true;
}

Project: radare2
File: config.c

R_API void r_config_restore(RConfigHold *h) {
	RListIter *iter;
	RConfigHoldChar *hchar;
	RConfigHoldNum *hnum;
	if (h) {
		r_list_foreach (h->list_num, iter, hnum) {
			(void)r_config_set_i (h->cfg, hnum->key, hnum->value);
		}
		r_list_foreach (h->list_char, iter, hchar) {
			(void)r_config_set (h->cfg, hchar->key, hchar->value);
		}
	}
}

Project: radare2
File: config.c

R_API int r_config_rm(RConfig *cfg, const char *name) {
	RConfigNode *node = r_config_node_get (cfg, name);
	if (node) {
		ht_delete (cfg->ht, node->name);
		r_list_delete_data (cfg->nodes, node);
		cfg->n_nodes--;
		return true;
	}
	return false;
}

Project: radare2
File: config.c

R_API bool r_config_save_char(RConfigHold *h, ...) {
	va_list ap;
	char *key;
	if (!h->list_char) {
		h->list_char = r_list_newf ((RListFree) free);
		if (!h->list_char) {
			return false;
		}
	}
	va_start (ap, h);
	while ((key = va_arg (ap, char *))) {
		RConfigHoldChar *hc = R_NEW0 (RConfigHoldChar);
		if (!hc) {
			continue;
		}
		hc->key = key;
		hc->value = r_config_get (h->cfg, key);
		r_list_append (h->list_char, hc);
	}
	va_end (ap);
	return true;
}

Project: radare2
File: config.c

R_API bool r_config_save_num(RConfigHold *h, ...) {
	va_list ap;
	char *key;
	if (!h->list_num) {
		h->list_num = r_list_newf ((RListFree) free);
		if (!h->list_num) {
			return false;
		}
	}
	va_start (ap, h);
	while ((key = va_arg (ap, char *))) {
		RConfigHoldNum *hc = R_NEW0 (RConfigHoldNum);
		if (!hc) {
			continue;
		}
		hc->key = key;
		hc->value = r_config_get_i (h->cfg, key);
		r_list_append (h->list_num, hc);
	}
	va_end (ap);
	return true;
}

Project: radare2
File: config.c

R_API RConfigNode* r_config_set_cb(RConfig *cfg, const char *name, const char *value, RConfigCallback cb) {
	RConfigNode *node = r_config_set (cfg, name, value);
	if (node && (node->setter = cb)) {
		if (!cb (cfg->user, node)) {
			return NULL;
		}
	}
	return node;
}

Project: radare2

R_API RConfigNode* r_config_set_i(RConfig *cfg, const char *name, const ut64 i) {
	char buf[128], *ov = NULL;
	if (!cfg || !name) {
		return NULL;
	}
	RConfigNode *node = r_config_node_get (cfg, name);
	if (node) {
		if (node->flags & CN_RO) {
			node = NULL;
			goto beach;
		}
		if (node->value) {
			ov = strdup (node->value);
			if (!ov) {
				node = NULL;
				goto beach;
			}
			free (node->value);
		}
		if (node->flags & CN_BOOL) {
			node->value = strdup (r_str_bool (i));
		} else {
			snprintf (buf, sizeof (buf) - 1, "%" PFMT64d, i);
			node->value = strdup (buf);
		}
		if (!node->value) {
			node = NULL;
			goto beach;
		}
		node->i_value = i;
	} else {
		if (!cfg->lock) {
			if (i < 1024) {
				snprintf (buf, sizeof (buf), "%" PFMT64d "", i);
			} else {
				snprintf (buf, sizeof (buf), "0x%08" PFMT64x "", i);
			}
			node = r_config_node_new (name, buf);
			if (!node) {
				node = NULL;
				goto beach;
			}
			node->flags = CN_RW | CN_OFFT;
			node->i_value = i;
			if (cfg->ht) {
				ht_insert (cfg->ht, node->name, node);
			}
			if (cfg->nodes) {
				r_list_append (cfg->nodes, node);
				cfg->n_nodes++;
			}
		} else {
			eprintf ("(locked: no new keys can be created (%s))\n", name);
		}
	}

	if (node && node->setter) {
		ut64 oi = node->i_value;
		int ret = node->setter (cfg->user, node);
		if (!ret) {
			node->i_value = oi;
			free (node->value);
			node->value = strdup (ov? ov: "");
		}
	}
beach:
	free (ov);
	return node;
}

Project: radare2
File: config.c

R_API RConfigNode* r_config_set_i_cb(RConfig *cfg, const char *name, int ivalue, RConfigCallback cb) {
	RConfigNode *node = r_config_set_i (cfg, name, ivalue);
	if (node && (node->setter = cb)) {
		if (!node->setter (cfg->user, node)) {
			return NULL;
		}
	}
	return node;
}

Project: radare2
File: config.c

R_API void r_config_visual_hit_i(RConfig *cfg, const char *name, int delta) {
	RConfigNode *node = r_config_node_get (cfg, name);
	if (node && (node->flags & CN_INT || node->flags & CN_OFFT)) {
		(void)r_config_set_i (cfg, name, r_config_get_i (cfg, name) + delta);
	}
}

Project: radare2
File: cbin.c

static void _print_strings(RCore *r, RList *list, int mode, int va) {
	int minstr = r_config_get_i (r->config, "bin.minstr");
	int maxstr = r_config_get_i (r->config, "bin.maxstr");
	RBin *bin = r->bin;
	RListIter *iter;
	RBinString *string;
	RBinSection *section;
	char *q;

	bin->minstrlen = minstr;
	bin->maxstrlen = maxstr;
	if (IS_MODE_JSON (mode)) {
		r_cons_printf ("[");
	}
	if (IS_MODE_RAD (mode)) {
		r_cons_printf ("fs strings");
	}
	if (IS_MODE_SET (mode) && r_config_get_i (r->config, "bin.strings")) {
		r_flag_space_set (r->flags, "strings");
		r_cons_break_push (NULL, NULL);
	}
	r_list_foreach (list, iter, string) {
		const char *section_name, *type_string;
		ut64 paddr, vaddr, addr;
		if (!string_filter (r, string->string)) {
			continue;
		}
		paddr = string->paddr;
		vaddr = r_bin_get_vaddr (bin, paddr, string->vaddr);
		addr = va ? vaddr : paddr;
		if (string->length < minstr) {
			continue;
		}
		if (maxstr && string->length > maxstr) {
			continue;
		}

		section = r_bin_get_section_at (r_bin_cur_object (bin), paddr, 0);
		section_name = section ? section->name : "unknown";
		type_string = r_bin_string_type (string->type);
		if (IS_MODE_SET (mode)) {
			char *f_name, *str;
			if (r_cons_is_breaked ()) {
				break;
			}
			r_meta_add (r->anal, R_META_TYPE_STRING, addr, addr + string->size, string->string);
			f_name = strdup (string->string);
			r_name_filter (f_name, -1);
			if (r->bin->prefix) {
				str = r_str_newf ("%s.str.%s", r->bin->prefix, f_name);
			} else {
				str = r_str_newf ("str.%s", f_name);
			}
			r_flag_set (r->flags, str, addr, string->size);
			free (str);
			free (f_name);
		} else if (IS_MODE_SIMPLE (mode)) {
			r_cons_printf ("0x%"PFMT64x" %d %d %s\n", addr, string->size, string->length, string->string);
		} else if (IS_MODE_SIMPLEST (mode)) {
			r_cons_println (string->string);
		} else if (IS_MODE_JSON (mode)) {
			q = r_base64_encode_dyn (string->string, -1);
			r_cons_printf ("%s{\"vaddr\":%"PFMT64d
				",\"paddr\":%"PFMT64d",\"ordinal\":%d"
				",\"size\":%d,\"length\":%d,\"section\":\"%s\","
				"\"type\":\"%s\",\"string\":\"%s\"}",
				iter->p ? ",": "",
				vaddr, paddr, string->ordinal, string->size,
				string->length, section_name, type_string, q);
			free (q);
		} else if (IS_MODE_RAD (mode)) {
			char *f_name, *str;
			f_name = strdup (string->string);
			r_name_filter (f_name, R_FLAG_NAME_SIZE);
			if (r->bin->prefix) {
				str = r_str_newf ("%s.str.%s", r->bin->prefix, f_name);
				r_cons_printf ("f %s.str.%s %"PFMT64d" @ 0x%08"PFMT64x"\n"
					"Cs %"PFMT64d" @ 0x%08"PFMT64x"\n",
					r->bin->prefix, f_name, string->size, addr,
					string->size, addr);
			} else {
				str = r_str_newf ("str.%s", f_name);
				r_cons_printf ("f str.%s %"PFMT64d" @ 0x%08"PFMT64x"\n"
					"Cs %"PFMT64d" @ 0x%08"PFMT64x"\n",
					f_name, string->size, addr,
					string->size, addr);
			}
			free (str);
			free (f_name);
		} else {
			r_cons_printf ("vaddr=0x%08"PFMT64x" paddr=0x%08"
				PFMT64x" ordinal=%03u sz=%u len=%u "
				"section=%s type=%s string=%s\n",
				vaddr, paddr, string->ordinal, string->size,
				string->length, section_name, type_string,
				string->string);
		}
	}
	if (IS_MODE_JSON (mode)) {
		r_cons_printf ("]");
	}
	if (IS_MODE_SET (mode)) {
		r_cons_break_pop ();
	}
}