Commit message: Fix #9902 - Fix oobread in RBin.string_scan_range
CVE: CVE-2018-11381
CWE: CWE-125
Vulnerability description: The string_scan_range() function in radare2 2.5.0 allows remote attackers to cause a denial of service (heap-based out-of-bounds read and application crash) via a crafted binary file.

Project: radare2
File: file.c

static void print_string(RBinString *string, RBinFile *bf) {
	if (!string || !bf) {
		return;
	}
	int mode = bf->strmode;
	ut64 addr , vaddr;
	RBin *bin = bf->rbin;
	const char *section_name, *type_string;
	RIO *io = bin->iob.io;
	if (!io) {
		return;
	}
	RBinSection *s = r_bin_get_section_at (bf->o, string->paddr, false);
	if (s) {
		string->vaddr = s->vaddr + (string->paddr - s->paddr);
	}
	section_name = s ? s->name : "";
	type_string = r_bin_string_type (string->type);
	vaddr = addr = r_bin_get_vaddr (bin, string->paddr, string->vaddr);

	switch(mode) {
	case MODE_SIMPLE :
		io->cb_printf ("0x%08" PFMT64x " %s\n", addr, string->string);
		break;
	case MODE_RADARE :
		{
		char *f_name, *nstr;
		f_name = strdup (string->string);
		r_name_filter (f_name, 512);
		if (bin->prefix) {
			nstr = r_str_newf ("%s.str.%s", bin->prefix, f_name);
			io->cb_printf ("f %s.str.%s %"PFMT64d" @ 0x%08"PFMT64x"\n"
					"Cs %"PFMT64d" @ 0x%08"PFMT64x"\n",
					bin->prefix, f_name, string->size, addr,
					string->size, addr);
		} else {
			nstr = r_str_newf ("str.%s", f_name);
			io->cb_printf ("f str.%s %"PFMT64d" @ 0x%08"PFMT64x"\n"
					"Cs %"PFMT64d" @ 0x%08"PFMT64x"\n",
					f_name, string->size, addr,
					string->size, addr);
		}
		free (nstr);
		free (f_name);
		break;
		}
	case MODE_PRINT :
		io->cb_printf ("%03u 0x%08"PFMT64x" 0x%08"
				PFMT64x" %3u %3u "
				"(%s) %5s %s\n",
				string->ordinal, string->paddr, vaddr,
				string->length, string->size,
				section_name, type_string, string->string);
		break;
	}
}

Project: radare2
File: file.c

R_API RBinFile *r_bin_file_create_append(RBin *bin, const char *file, const ut8 *bytes, ut64 sz, ut64 file_sz, int rawstr, int fd, const char *xtrname, bool steal_ptr) {
	RBinFile *bf = r_bin_file_new (bin, file, bytes, sz, file_sz, rawstr,
				       fd, xtrname, bin->sdb, steal_ptr);
	if (bf) {
		r_list_append (bin->binfiles, bf);
	}
	return bf;
}

Project: radare2

R_API int r_bin_file_deref(RBin *bin, RBinFile *a) {
	RBinObject *o = r_bin_cur_object (bin);
	int res = false;
	if (a && !o) {
		res = true;
	} else if (a && o->referenced - 1 < 1) {
		res = true;
	} else if (o) {
		o->referenced--;
	}
	if (bin) {
		bin->cur = NULL;
	}
	return res;
}

Project: radare2

R_API void r_bin_file_free(void /*RBinFile*/ *bf_) {
	RBinFile *a = bf_;
	RBinPlugin *plugin = r_bin_file_cur_plugin (a);

	if (!a) {
		return;
	}
	if (plugin && plugin->destroy) {
		plugin->destroy (a);
	}
	if (a->curxtr && a->curxtr->destroy && a->xtr_obj) {
		a->curxtr->free_xtr ((void *)(a->xtr_obj));
	}
	r_buf_free (a->buf);
	if (a && a->sdb_addrinfo) {
		sdb_free (a->sdb_addrinfo);
		a->sdb_addrinfo = NULL;
	}
	R_FREE (a->file);
	a->o = NULL;
	r_list_free (a->objs);
	r_list_free (a->xtr_data);
	if (a->id != -1) {
		r_id_pool_kick_id (a->rbin->file_ids, a->id);
	}
	memset (a, 0, sizeof (RBinFile));
	free (a);
}

Project: radare2
File: file.c

R_API ut64 r_bin_file_get_baddr(RBinFile *binfile) {
	return binfile? r_bin_object_get_baddr (binfile->o): UT64_MAX;
}

Project: radare2

R_API RList *r_bin_file_get_strings(RBinFile *a, int min, int dump) {
	RListIter *iter;
	RBinSection *section;
	RBinObject *o = a? a->o: NULL;
	RList *ret;

	if (dump) {
		/* dump to stdout, not stored in list */
		ret = NULL;
	} else {
		ret = r_list_newf (r_bin_string_free);
		if (!ret) {
			return NULL;
		}
	}
	if (o && o->sections && !r_list_empty (o->sections) && !a->rawstr) {
		r_list_foreach (o->sections, iter, section) {
			if (is_data_section (a, section)) {
				r_bin_file_get_strings_range (a, ret, min, section->paddr,
						section->paddr + section->size);
			}
		}
		r_list_foreach (o->sections, iter, section) {
			RBinString *s;
			RListIter *iter2;
			/* load objc/swift strings */
			const int bits = (a->o && a->o->info) ? a->o->info->bits : 32;
			const int cfstr_size = (bits == 64) ? 32 : 16;
			const int cfstr_offs = (bits == 64) ? 16 :  8;
			if (strstr (section->name, "__cfstring")) {
				int i;
				ut8 *p;
				for (i = 0; i < section->size; i += cfstr_size) {
					ut8 buf[32];
					if (!r_buf_read_at (
						    a->buf, section->paddr + i + cfstr_offs,
						    buf, sizeof (buf))) {
						break;
					}
					p = buf;
					ut64 cfstr_vaddr = section->vaddr + i;
					ut64 cstr_vaddr = (bits == 64)
								   ? r_read_le64 (p)
								   : r_read_le32 (p);
					r_list_foreach (ret, iter2, s) {
						if (s->vaddr == cstr_vaddr) {
							RBinString *bs = R_NEW0 (RBinString);
							if (bs) {
								bs->type = s->type;
								bs->length = s->length;
								bs->size = s->size;
								bs->ordinal = s->ordinal;
								bs->paddr = bs->vaddr = cfstr_vaddr;
								bs->string = r_str_newf ("cstr.%s", s->string);
								r_list_append (ret, bs);
							}
							break;
						}
					}
				}
			}
		}
	} else {
		if (a) {
			r_bin_file_get_strings_range (a, ret, min, 0, a->size);
		}
	}
	return ret;
}

Project: radare2

R_API void r_bin_file_get_strings_range(RBinFile *bf, RList *list, int min, ut64 from, ut64 to) {
	RBinPlugin *plugin = r_bin_file_cur_plugin (bf);
	RBinString *ptr;
	RListIter *it;

	if (!bf || !bf->buf) {
		return;
	}
	if (!bf->rawstr) {
		if (!plugin || !plugin->info) {
			return;
		}
	}
	if (!min) {
		min = plugin? plugin->minstrlen: 4;
	}
	/* Some plugins return zero, fix it up */
	if (!min) {
		min = 4;
	}
	if (min < 0) {
		return;
	}
	if (!to || to > bf->buf->length) {
		to = r_buf_size (bf->buf);
	}
	if (!to) {
		eprintf ("Empty file with fd %d?\n", bf->buf->fd);
		return;
	}
	if (bf->rawstr != 2) {
		ut64 size = to - from;
		if (bf->rbin->maxstrbuf && size && size > bf->rbin->maxstrbuf) {
			if (bf->rbin->verbose) {
				eprintf ("WARNING: bin_strings buffer is too big "
					"(0x%08" PFMT64x
					")."
					" Use -zzz or set bin.maxstrbuf "
					"(RABIN2_MAXSTRBUF) in r2 (rabin2)\n",
					size);
			}
			return;
		}
	}
	if (string_scan_range (list, bf, min, from, to, -1) < 0) {
		return;
	}
	r_list_foreach (list, it, ptr) {
		RBinSection *s = r_bin_get_section_at (bf->o, ptr->paddr, false);
		if (s) {
			ptr->vaddr = s->vaddr + (ptr->paddr - s->paddr);
		}
	}
}

Project: radare2

R_API RBinFile *r_bin_file_new(RBin *bin, const char *file, const ut8 *bytes, ut64 sz, ut64 file_sz, int rawstr, int fd, const char *xtrname, Sdb *sdb, bool steal_ptr) {
	RBinFile *binfile = R_NEW0 (RBinFile);
	if (!binfile) {
		return NULL;
	}
	if (!r_id_pool_grab_id (bin->file_ids, &binfile->id)) {
		if (steal_ptr) { // we own the ptr, free on error
			free ((void*) bytes);
		}
		free (binfile);		//no id means no binfile
		return NULL;
	}
	int res = r_bin_file_set_bytes (binfile, bytes, sz, steal_ptr);
	if (!res && steal_ptr) { // we own the ptr, free on error
		free((void*) bytes);
	}
	binfile->rbin = bin;
	binfile->file = file? strdup (file): NULL;
	binfile->rawstr = rawstr;
	binfile->fd = fd;
	binfile->curxtr = r_bin_get_xtrplugin_by_name (bin, xtrname);
	binfile->sdb = sdb;
	binfile->size = file_sz;
	binfile->xtr_data = r_list_newf ((RListFree)r_bin_xtrdata_free);
	binfile->objs = r_list_newf ((RListFree)r_bin_object_free);
	binfile->xtr_obj  = NULL;

	if (!binfile->buf) {
		binfile->buf = r_buf_new ();
	}

	if (sdb) {
		binfile->sdb = sdb_ns (sdb, sdb_fmt ("fd.%d", fd), 1);
		sdb_set (binfile->sdb, "archs", "0:0:x86:32", 0); // x86??
		/* NOTE */
		/* Those refs++ are necessary because sdb_ns() doesnt rerefs all
		 * sub-namespaces */
		/* And if any namespace is referenced backwards it gets
		 * double-freed */
		binfile->sdb_addrinfo = sdb_ns (binfile->sdb, "addrinfo", 1);
		binfile->sdb_addrinfo->refs++;
		sdb_ns_set (sdb, "cur", binfile->sdb);
		binfile->sdb->refs++;
	}
	return binfile;
}

Project: radare2

R_API RBinFile *r_bin_file_new_from_bytes(RBin *bin, const char *file, const ut8 *bytes, ut64 sz, ut64 file_sz, int rawstr, ut64 baseaddr, ut64 loadaddr, int fd, const char *pluginname, const char *xtrname, ut64 offset, bool steal_ptr) {
	ut8 binfile_created = false;
	RBinPlugin *plugin = NULL;
	RBinXtrPlugin *xtr = NULL;
	RBinObject *o = NULL;
	if (sz == UT64_MAX) {
		return NULL;
	}

	if (xtrname) {
		xtr = r_bin_get_xtrplugin_by_name (bin, xtrname);
	}

	if (xtr && xtr->check_bytes (bytes, sz)) {
		return r_bin_file_xtr_load_bytes (bin, xtr, file,
						bytes, sz, file_sz, baseaddr, loadaddr, 0,
						fd, rawstr);
	}

	RBinFile *bf = r_bin_file_create_append (bin, file, bytes, sz, file_sz,
				       rawstr, fd, xtrname, steal_ptr);
	if (!bf) {
		if (!steal_ptr) { // we own the ptr, free on error
			free ((void*) bytes);
		}
		return NULL;
	}
	binfile_created = true;

	if (bin->force) {
		plugin = r_bin_get_binplugin_by_name (bin, bin->force);
	}
	if (!plugin) {
		if (pluginname) {
			plugin = r_bin_get_binplugin_by_name (bin, pluginname);
		}
		if (!plugin) {
			plugin = r_bin_get_binplugin_by_bytes (bin, bytes, sz);
			if (!plugin) {
				plugin = r_bin_get_binplugin_any (bin);
			}
		}
	}

	o = r_bin_object_new (bf, plugin, baseaddr, loadaddr, 0, r_buf_size (bf->buf));
	if (o && !o->size) {
		o->size = file_sz;
	}

	if (!o) {
		if (bf && binfile_created) {
			r_list_delete_data (bin->binfiles, bf);
		}
		return NULL;
	}
#if 0
	/* WTF */
	if (strcmp (plugin->name, "any")) {
		bf->narch = 1;
	}
#endif

	/* free unnecessary rbuffer (???) */
	return bf;
}

Project: radare2

R_API RBinFile *r_bin_file_new_from_fd(RBin *bin, int fd, RBinFileOptions *options) {
int file_sz = 0;
	RBinPlugin *plugin = NULL;
	RBinFile *bf = r_bin_file_create_append (bin, "-", NULL, 0, file_sz,
				       0, fd, NULL, false);
	if (!bf) {
		return NULL;
	}
	int loadaddr = options? options->laddr: 0;
	int baseaddr = options? options->baddr: 0;
	bool binfile_created = true;
	r_buf_free (bf->buf);
	bf->buf = r_buf_new_with_io (&bin->iob, fd);
	if (bin->force) {
		plugin = r_bin_get_binplugin_by_name (bin, bin->force);
	}
	if (!plugin) {
		if (options && options->plugname) {
			plugin = r_bin_get_binplugin_by_name (bin, options->plugname);
		}
		if (!plugin) {
			ut8 bytes[1024];
			int sz = 1024;
			r_buf_read_at (bf->buf, 0, bytes, sz);
			plugin = r_bin_get_binplugin_by_bytes (bin, bytes, sz);
			if (!plugin) {
				plugin = r_bin_get_binplugin_any (bin);
			}
		}
	}

	RBinObject *o = r_bin_object_new (bf, plugin, baseaddr, loadaddr, 0, r_buf_size (bf->buf));
	if (o && !o->size) {
		o->size = file_sz;
	}

	if (!o) {
		if (bf && binfile_created) {
			r_list_delete_data (bin->binfiles, bf);
		}
		return NULL;
	}
#if 0
	/* WTF */
	if (strcmp (plugin->name, "any")) {
		bf->narch = 1;
	}
#endif
	/* free unnecessary rbuffer (???) */
	return bf;
}

Project: radare2
File: file.c

R_API int r_bin_file_object_add(RBinFile *binfile, RBinObject *o) {
	if (!o) {
		return false;
	}
	r_list_append (binfile->objs, o);
	r_bin_file_set_cur_binfile_obj (binfile->rbin, binfile, o);
	return true;
}

Project: radare2
File: file.c

R_API RBinObject *r_bin_file_object_find_by_id(RBinFile *binfile, ut32 binobj_id) {
	RBinObject *obj;
	RListIter *iter;
	if (binfile)  {
		r_list_foreach (binfile->objs, iter, obj) {
			if (obj->id == binobj_id) {
				return obj;
			}
		}
	}
	return NULL;
}

Project: radare2

R_API int r_bin_file_set_bytes(RBinFile *binfile, const ut8 *bytes, ut64 sz, bool steal_ptr) {
	if (!binfile) {
		return false;
	}
	if (!bytes) {
		return false;
	}
	r_buf_free (binfile->buf);
	binfile->buf = r_buf_new ();
#if LIMIT_SIZE
	if (sz > 1024 * 1024) {
		eprintf ("Too big\n");
		return NULL;
	}
#else
	if (steal_ptr) {
		r_buf_set_bytes_steal (binfile->buf, bytes, sz);
	} else {
		r_buf_set_bytes (binfile->buf, bytes, sz);
	}
#endif
	return binfile->buf != NULL;
}

Project: radare2
File: file.c

R_API int r_bin_file_set_cur_binfile(RBin *bin, RBinFile *bf) {
	RBinObject *obj = bf? bf->o: NULL;
	return r_bin_file_set_cur_binfile_obj (bin, bf, obj);
}

Project: radare2

R_API RBinFile *r_bin_file_xtr_load_bytes(RBin *bin, RBinXtrPlugin *xtr, const char *filename, const ut8 *bytes, ut64 sz, ut64 file_sz, ut64 baseaddr, ut64 loadaddr, int idx, int fd, int rawstr) {
	if (!bin || !bytes) {
		return NULL;
	}
	RBinFile *bf = r_bin_file_find_by_name (bin, filename);
	if (!bf) {
		bf = r_bin_file_create_append (bin, filename, bytes, sz,
					       file_sz, rawstr, fd, xtr->name, false);
		if (!bf) {
			return NULL;
		}
		if (!bin->cur) {
			bin->cur = bf;
		}
	}
	if (bf->xtr_data) {
		r_list_free (bf->xtr_data);
	}
	if (xtr && bytes) {
		RList *xtr_data_list = xtr->extractall_from_bytes (bin, bytes, sz);
		RListIter *iter;
		RBinXtrData *xtr;
		r_list_foreach (xtr_data_list, iter, xtr) {
			xtr->baddr = baseaddr? baseaddr : UT64_MAX;
			xtr->laddr = loadaddr? loadaddr : UT64_MAX;
		}
		bf->loadaddr = loadaddr;
		bf->xtr_data = xtr_data_list ? xtr_data_list : NULL;
	}
	return bf;
}

Project: radare2
File: file.c

static char *swiftField(const char *dn, const char *cn) {
	char *p = strstr (dn, ".getter_");
	if (!p) {
		p = strstr (dn, ".setter_");
		if (!p) {
			p = strstr (dn, ".method_");
		}
	}
	if (p) {
		char *q = strstr (dn, cn);
		if (q && q[strlen (cn)] == '.') {
			q = strdup (q + strlen (cn) + 1);
			char *r = strchr (q, '.');
			if (r) {
				*r = 0;
			}
			return q;
		}
	}
	return NULL;
}