Commit message: Fix #8764 - huge vd_aux caused pointer wraparound
CVE: CVE-2017-16359
CWE: CWE-476
Vulnerability description: In radare 2.0.1, a pointer wraparound vulnerability exists in store_versioninfo_gnu_verdef() in libr/bin/format/elf/elf.c.

Project: radare2
File: elf.c

static inline int __strnlen(const char *str, int len) {
	int l = 0;
	while (IS_PRINTABLE (*str) && --len) {
		if (((ut8)*str) == 0xff) {
			break;
		}
		str++;
		l++;
	}
	return l + 1;
}

Project: radare2
File: elf.c

static int Elf_(fix_symbols)(ELFOBJ *bin, int nsym, int type, RBinElfSymbol **sym) {
	int count = 0;
	RBinElfSymbol *ret = *sym;
	RBinElfSymbol *phdr_symbols = (type == R_BIN_ELF_SYMBOLS)
				? Elf_(r_bin_elf_get_phdr_symbols) (bin)
				: Elf_(r_bin_elf_get_phdr_imports) (bin);
	RBinElfSymbol *tmp, *p;
	if (phdr_symbols) {
		RBinElfSymbol *d = ret;
		while (!d->last) {
			/* find match in phdr */
			p = phdr_symbols;
			while (!p->last) {
				if (p->offset && d->offset == p->offset) {
					p->in_shdr = true;
					if (*p->name && strcmp (d->name, p->name)) {
						strcpy (d->name, p->name);
					}
				}
				p++;
			}
			d++;
		}
		p = phdr_symbols;
		while (!p->last) {
			if (!p->in_shdr) {
				count++;
			}
			p++;
		}
		/*Take those symbols that are not present in the shdr but yes in phdr*/
		/*This should only should happen with fucked up binaries*/
		if (count > 0) {
			/*what happens if a shdr says it has only one symbol? we should look anyway into phdr*/
			tmp = (RBinElfSymbol*)realloc (ret, (nsym + count + 1) * sizeof (RBinElfSymbol));
			if (!tmp) {
				return -1;
			}
			ret = tmp;
			ret[nsym--].last = 0;
			p = phdr_symbols;
			while (!p->last) {
				if (!p->in_shdr) {
					memcpy (&ret[++nsym], p, sizeof (RBinElfSymbol));
				}
				p++;
			}
			ret[nsym + 1].last = 1;
		}
		*sym = ret;
		return nsym + 1;
	}
	return nsym;
}

Project: radare2

static int init_ehdr(ELFOBJ *bin) {
	ut8 e_ident[EI_NIDENT];
	ut8 ehdr[sizeof (Elf_(Ehdr))] = {0};
	int i, len;
	if (r_buf_read_at (bin->b, 0, e_ident, EI_NIDENT) == -1) {
		bprintf ("Warning: read (magic)\n");
		return false;
	}
	sdb_set (bin->kv, "elf_type.cparse", "enum elf_type { ET_NONE=0, ET_REL=1,"
			" ET_EXEC=2, ET_DYN=3, ET_CORE=4, ET_LOOS=0xfe00, ET_HIOS=0xfeff,"
			" ET_LOPROC=0xff00, ET_HIPROC=0xffff };", 0);
	sdb_set (bin->kv, "elf_machine.cparse", "enum elf_machine{EM_NONE=0, EM_M32=1,"
			" EM_SPARC=2, EM_386=3, EM_68K=4, EM_88K=5, EM_486=6, "
			" EM_860=7, EM_MIPS=8, EM_S370=9, EM_MIPS_RS3_LE=10, EM_RS6000=11,"
			" EM_UNKNOWN12=12, EM_UNKNOWN13=13, EM_UNKNOWN14=14, "
			" EM_PA_RISC=15, EM_PARISC=EM_PA_RISC, EM_nCUBE=16, EM_VPP500=17,"
			" EM_SPARC32PLUS=18, EM_960=19, EM_PPC=20, EM_PPC64=21, "
			" EM_S390=22, EM_UNKNOWN22=EM_S390, EM_UNKNOWN23=23, EM_UNKNOWN24=24,"
			" EM_UNKNOWN25=25, EM_UNKNOWN26=26, EM_UNKNOWN27=27, EM_UNKNOWN28=28,"
			" EM_UNKNOWN29=29, EM_UNKNOWN30=30, EM_UNKNOWN31=31, EM_UNKNOWN32=32,"
			" EM_UNKNOWN33=33, EM_UNKNOWN34=34, EM_UNKNOWN35=35, EM_V800=36,"
			" EM_FR20=37, EM_RH32=38, EM_RCE=39, EM_ARM=40, EM_ALPHA=41, EM_SH=42,"
			" EM_SPARCV9=43, EM_TRICORE=44, EM_ARC=45, EM_H8_300=46, EM_H8_300H=47,"
			" EM_H8S=48, EM_H8_500=49, EM_IA_64=50, EM_MIPS_X=51, EM_COLDFIRE=52,"
			" EM_68HC12=53, EM_MMA=54, EM_PCP=55, EM_NCPU=56, EM_NDR1=57,"
			" EM_STARCORE=58, EM_ME16=59, EM_ST100=60, EM_TINYJ=61, EM_AMD64=62,"
			" EM_X86_64=EM_AMD64, EM_PDSP=63, EM_UNKNOWN64=64, EM_UNKNOWN65=65,"
			" EM_FX66=66, EM_ST9PLUS=67, EM_ST7=68, EM_68HC16=69, EM_68HC11=70,"
			" EM_68HC08=71, EM_68HC05=72, EM_SVX=73, EM_ST19=74, EM_VAX=75, "
			" EM_CRIS=76, EM_JAVELIN=77, EM_FIREPATH=78, EM_ZSP=79, EM_MMIX=80,"
			" EM_HUANY=81, EM_PRISM=82, EM_AVR=83, EM_FR30=84, EM_D10V=85, EM_D30V=86,"
			" EM_V850=87, EM_M32R=88, EM_MN10300=89, EM_MN10200=90, EM_PJ=91,"
			" EM_OPENRISC=92, EM_ARC_A5=93, EM_XTENSA=94, EM_NUM=95};", 0);
	sdb_num_set (bin->kv, "elf_header.offset", 0, 0);
	sdb_num_set (bin->kv, "elf_header.size", sizeof (Elf_(Ehdr)), 0);
#if R_BIN_ELF64
	sdb_set (bin->kv, "elf_header.format", "[16]z[2]E[2]Exqqqxwwwwww"
		" ident (elf_type)type (elf_machine)machine version entry phoff shoff flags ehsize"
		" phentsize phnum shentsize shnum shstrndx", 0);
#else
	sdb_set (bin->kv, "elf_header.format", "[16]z[2]E[2]Exxxxxwwwwww"
		" ident (elf_type)type (elf_machine)machine version entry phoff shoff flags ehsize"
		" phentsize phnum shentsize shnum shstrndx", 0);
#endif
	bin->endian = (e_ident[EI_DATA] == ELFDATA2MSB)? 1: 0;
	memset (&bin->ehdr, 0, sizeof (Elf_(Ehdr)));

	len = r_buf_read_at (bin->b, 0, ehdr, sizeof (Elf_(Ehdr)));
	if (len < 1) {
		bprintf ("Warning: read (ehdr)\n");
		return false;
	}
	memcpy (&bin->ehdr.e_ident, ehdr, 16);
	i = 16;
	bin->ehdr.e_type = READ16 (ehdr, i)
	bin->ehdr.e_machine = READ16 (ehdr, i)
	bin->ehdr.e_version = READ32 (ehdr, i)
#if R_BIN_ELF64
	bin->ehdr.e_entry = READ64 (ehdr, i)
	bin->ehdr.e_phoff = READ64 (ehdr, i)
	bin->ehdr.e_shoff = READ64 (ehdr, i)
#else
	bin->ehdr.e_entry = READ32 (ehdr, i)
	bin->ehdr.e_phoff = READ32 (ehdr, i)
	bin->ehdr.e_shoff = READ32 (ehdr, i)
#endif
	bin->ehdr.e_flags = READ32 (ehdr, i)
	bin->ehdr.e_ehsize = READ16 (ehdr, i)
	bin->ehdr.e_phentsize = READ16 (ehdr, i)
	bin->ehdr.e_phnum = READ16 (ehdr, i)
	bin->ehdr.e_shentsize = READ16 (ehdr, i)
	bin->ehdr.e_shnum = READ16 (ehdr, i)
	bin->ehdr.e_shstrndx = READ16 (ehdr, i)
	return handle_e_ident (bin);
}

Project: radare2

void* Elf_(r_bin_elf_free)(ELFOBJ* bin) {
	int i;
	if (!bin) {
		return NULL;
	}
	free (bin->phdr);
	free (bin->shdr);
	free (bin->strtab);
	free (bin->dyn_buf);
	free (bin->shstrtab);
	free (bin->dynstr);
	if (bin->imports_by_ord) {
		for (i = 0; i<bin->imports_by_ord_size; i++) {
			free (bin->imports_by_ord[i]);
		}
		free (bin->imports_by_ord);
	}
	if (bin->symbols_by_ord) {
		for (i = 0; i<bin->symbols_by_ord_size; i++) {
			free (bin->symbols_by_ord[i]);
		}
		free (bin->symbols_by_ord);
	}
	r_buf_free (bin->b);
	if (bin->g_symbols != bin->phdr_symbols) {
		R_FREE (bin->phdr_symbols);
	}
	if (bin->g_imports != bin->phdr_imports) {
		R_FREE (bin->phdr_imports);
	}
	R_FREE (bin->g_sections);
	R_FREE (bin->g_symbols);
	R_FREE (bin->g_imports);
	free (bin);
	return NULL;
}

Project: radare2
File: elf.c

ut64 Elf_(r_bin_elf_get_fini_offset)(ELFOBJ *bin) {
	ut64 entry = Elf_(r_bin_elf_get_entry_offset) (bin);
	ut8 buf[512];
	if (!bin) {
		return 0LL;
	}

	if (r_buf_read_at (bin->b, entry+11, buf, sizeof (buf)) == -1) {
		bprintf ("Warning: read (get_fini)\n");
		return 0;
	}
	if (*buf == 0x68) { // push // x86/32 only
		ut64 addr;
		memmove (buf, buf+1, 4);
		addr = (ut64)r_read_le32 (buf);
		return Elf_(r_bin_elf_v2p) (bin, addr);
	}
	return 0;
}

Project: radare2
File: elf.c

static RBinElfSymbol *Elf_(r_bin_elf_get_phdr_imports)(ELFOBJ *bin) {
	if (!bin) {
		return NULL;
	}
	if (bin->phdr_imports) {
		return bin->phdr_imports;
	}
	bin->phdr_imports = get_symbols_from_phdr (bin, R_BIN_ELF_IMPORTS);
	return bin->phdr_imports;
}

Project: radare2
File: elf.c

RBinElfReloc* Elf_(r_bin_elf_get_relocs)(ELFOBJ *bin) {
	int res, rel, rela, i, j;
	size_t reloc_num = 0;
	RBinElfReloc *ret = NULL;

	if (!bin || !bin->g_sections) {
		return NULL;
	}
	reloc_num = get_relocs_num (bin);
	if (!reloc_num)	{
		return NULL;
	}
	bin->reloc_num = reloc_num;
	ret = (RBinElfReloc*)calloc ((size_t)reloc_num + 1, sizeof(RBinElfReloc));
	if (!ret) {
		return NULL;
	}
#if DEAD_CODE
	ut64 section_text_offset = Elf_(r_bin_elf_get_section_offset) (bin, ".text");
	if (section_text_offset == -1) {
		section_text_offset = 0;
	}
#endif
	for (i = 0, rel = 0; !bin->g_sections[i].last && rel < reloc_num ; i++) {
		bool is_rela = 0 == strncmp (bin->g_sections[i].name, ".rela.", strlen (".rela."));
		bool is_rel  = 0 == strncmp (bin->g_sections[i].name, ".rel.",  strlen (".rel."));
		if (!is_rela && !is_rel) {
			continue;
		}
		for (j = 0; j < bin->g_sections[i].size; j += res) {
			if (bin->g_sections[i].size > bin->size) {
				break;
			}
			if (bin->g_sections[i].offset > bin->size) {
				break;
			}
			if (rel >= reloc_num) {
				bprintf ("Internal error: ELF relocation buffer too small,"
				         "please file a bug report.");
				break;
			}
			if (!bin->is_rela) {
				rela = is_rela? DT_RELA : DT_REL;
			} else {
				rela = bin->is_rela;
			}
			res = read_reloc (bin, &ret[rel], rela, bin->g_sections[i].offset + j);
			if (j + res > bin->g_sections[i].size) {
				bprintf ("Warning: malformed file, relocation entry #%u is partially beyond the end of section %u.\n", rel, i);
			}
			if (bin->ehdr.e_type == ET_REL) {
				if (bin->g_sections[i].info < bin->ehdr.e_shnum && bin->shdr) {
					ret[rel].rva = bin->shdr[bin->g_sections[i].info].sh_offset + ret[rel].offset;
					ret[rel].rva = Elf_(r_bin_elf_p2v) (bin, ret[rel].rva);
				} else {
					ret[rel].rva = ret[rel].offset;
				}
			} else {
				ret[rel].rva = ret[rel].offset;
				ret[rel].offset = Elf_(r_bin_elf_v2p) (bin, ret[rel].offset);
			}
			ret[rel].last = 0;
			if (res < 0) {
				break;
			}
			rel++;
		}
	}
	ret[reloc_num].last = 1;
	return ret;
}

Project: radare2
File: elf.c

static Sdb *store_versioninfo_gnu_versym(ELFOBJ *bin, Elf_(Shdr) *shdr, int sz) {
	int i;
	const ut64 num_entries = sz / sizeof (Elf_(Versym));
	const char *section_name = "";
	const char *link_section_name = "";
	Elf_(Shdr) *link_shdr = NULL;
	Sdb *sdb = sdb_new0();
	if (!sdb) {
		return NULL;
	}
	if (!bin->version_info[DT_VERSIONTAGIDX (DT_VERSYM)]) {
		sdb_free (sdb);
		return NULL;
	}
	if (shdr->sh_link > bin->ehdr.e_shnum) {
		sdb_free (sdb);
		return NULL;
	}
	link_shdr = &bin->shdr[shdr->sh_link];
	ut8 *edata = (ut8*) calloc (R_MAX (1, num_entries), sizeof (ut16));
	if (!edata) {
		sdb_free (sdb);
		return NULL;
	}
	ut16 *data = (ut16*) calloc (R_MAX (1, num_entries), sizeof (ut16));
	if (!data) {
		free (edata);
		sdb_free (sdb);
		return NULL;
	}
	ut64 off = Elf_(r_bin_elf_v2p) (bin, bin->version_info[DT_VERSIONTAGIDX (DT_VERSYM)]);
	if (bin->shstrtab && shdr->sh_name < bin->shstrtab_size) {
		section_name = &bin->shstrtab[shdr->sh_name];
	}
	if (bin->shstrtab && link_shdr->sh_name < bin->shstrtab_size) {
		link_section_name = &bin->shstrtab[link_shdr->sh_name];
	}
	r_buf_read_at (bin->b, off, edata, sizeof (ut16) * num_entries);
	sdb_set (sdb, "section_name", section_name, 0);
	sdb_num_set (sdb, "num_entries", num_entries, 0);
	sdb_num_set (sdb, "addr", shdr->sh_addr, 0);
	sdb_num_set (sdb, "offset", shdr->sh_offset, 0);
	sdb_num_set (sdb, "link", shdr->sh_link, 0);
	sdb_set (sdb, "link_section_name", link_section_name, 0);
	for (i = num_entries; i--;) {
		data[i] = r_read_ble16 (&edata[i * sizeof (ut16)], bin->endian);
	}
	R_FREE (edata);
	for (i = 0; i < num_entries; i += 4) {
		int j;
		int check_def;
		char key[32] = {0};
		Sdb *sdb_entry = sdb_new0 ();
		snprintf (key, sizeof (key), "entry%d", i / 4);
		sdb_ns_set (sdb, key, sdb_entry);
		sdb_num_set (sdb_entry, "idx", i, 0);

		for (j = 0; (j < 4) && (i + j) < num_entries; ++j) {
			int k;
			char *tmp_val = NULL;
			snprintf (key, sizeof (key), "value%d", j);
			switch (data[i + j]) {
			case 0:
				sdb_set (sdb_entry, key, "0 (*local*)", 0);
				break;
			case 1:
				sdb_set (sdb_entry, key, "1 (*global*)", 0);
				break;
			default:
				tmp_val = sdb_fmt (0, "%x ", data[i+j] & 0x7FFF);
				check_def = true;
				if (bin->version_info[DT_VERSIONTAGIDX (DT_VERNEED)]) {
					Elf_(Verneed) vn;
					ut8 svn[sizeof (Elf_(Verneed))] = {0};
					ut64 offset = Elf_(r_bin_elf_v2p) (bin, bin->version_info[DT_VERSIONTAGIDX (DT_VERNEED)]);
					do {
						Elf_(Vernaux) vna;
						ut8 svna[sizeof (Elf_(Vernaux))] = {0};
						ut64 a_off;
						if (offset > bin->size || offset + sizeof (vn) > bin->size) {
							goto beach;
						}
						if (r_buf_read_at (bin->b, offset, svn, sizeof (svn)) < 0) {
							bprintf ("Warning: Cannot read Verneed for Versym\n");
							goto beach;
						}
						k = 0;
						vn.vn_version = READ16 (svn, k)
						vn.vn_cnt = READ16 (svn, k)
						vn.vn_file = READ32 (svn, k)
						vn.vn_aux = READ32 (svn, k)
						vn.vn_next = READ32 (svn, k)
						a_off = offset + vn.vn_aux;
						do {
							if (a_off > bin->size || a_off + sizeof (vna) > bin->size) {
								goto beach;
							}
							if (r_buf_read_at (bin->b, a_off, svna, sizeof (svna)) < 0) {
								bprintf ("Warning: Cannot read Vernaux for Versym\n");
								goto beach;
							}
							k = 0;
							vna.vna_hash = READ32 (svna, k)
							vna.vna_flags = READ16 (svna, k)
							vna.vna_other = READ16 (svna, k)
							vna.vna_name = READ32 (svna, k)
							vna.vna_next = READ32 (svna, k)
							a_off += vna.vna_next;
						} while (vna.vna_other != data[i + j] && vna.vna_next != 0);

						if (vna.vna_other == data[i + j]) {
							if (vna.vna_name > bin->strtab_size) {
								goto beach;
							}
							sdb_set (sdb_entry, key, sdb_fmt (0, "%s(%s)", tmp_val, bin->strtab + vna.vna_name), 0);
							check_def = false;
							break;
						}
						offset += vn.vn_next;
					} while (vn.vn_next);
				}

				ut64 vinfoaddr = bin->version_info[DT_VERSIONTAGIDX (DT_VERDEF)];
				if (check_def && data[i + j] != 0x8001 && vinfoaddr) {
					Elf_(Verdef) vd;
					ut8 svd[sizeof (Elf_(Verdef))] = {0};
					ut64 offset = Elf_(r_bin_elf_v2p) (bin, vinfoaddr);
					if (offset > bin->size || offset + sizeof (vd) > bin->size) {
						goto beach;
					}
					do {
						if (r_buf_read_at (bin->b, offset, svd, sizeof (svd)) < 0) {
							bprintf ("Warning: Cannot read Verdef for Versym\n");
							goto beach;
						}
						k = 0;
						vd.vd_version = READ16 (svd, k)
						vd.vd_flags = READ16 (svd, k)
						vd.vd_ndx = READ16 (svd, k)
						vd.vd_cnt = READ16 (svd, k)
						vd.vd_hash = READ32 (svd, k)
						vd.vd_aux = READ32 (svd, k)
						vd.vd_next = READ32 (svd, k)
						offset += vd.vd_next;
					} while (vd.vd_ndx != (data[i + j] & 0x7FFF) && vd.vd_next != 0);

					if (vd.vd_ndx == (data[i + j] & 0x7FFF)) {
						Elf_(Verdaux) vda;
						ut8 svda[sizeof (Elf_(Verdaux))] = {0};
						ut64 off_vda = offset - vd.vd_next + vd.vd_aux;
						if (off_vda > bin->size || off_vda + sizeof (vda) > bin->size) {
							goto beach;
						}
						if (r_buf_read_at (bin->b, off_vda, svda, sizeof (svda)) < 0) {
							bprintf ("Warning: Cannot read Verdaux for Versym\n");
							goto beach;
						}
						k = 0;
						vda.vda_name = READ32 (svda, k)
						vda.vda_next = READ32 (svda, k)
						if (vda.vda_name > bin->strtab_size) {
							goto beach;
						}
						const char *name = bin->strtab + vda.vda_name;
						sdb_set (sdb_entry, key, sdb_fmt (0,"%s(%s%-*s)", tmp_val, name, (int)(12 - strlen (name)),")") , 0);
					}
				}
			}
		}
	}
beach:
	free (data);
	return sdb;
}