Commit message: Fix #9970 - heap oobread in mach0 parser (#10026)
CVE: CVE-2018-11380
CWE: CWE-125
Vulnerability description: The parse_import_ptr() 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 Mach-O file.

Project: radare2
File: mach0.c

struct import_t* MACH0_(get_imports)(struct MACH0_(obj_t)* bin) {
	struct import_t *imports;
 	int i, j, idx, stridx;
 	const char *symstr;
 
	if (!bin->symtab || !bin->symstr || !bin->sects || !bin->indirectsyms) {
 		return NULL;
	}
 	if (bin->dysymtab.nundefsym < 1 || bin->dysymtab.nundefsym > 0xfffff) {
 		return NULL;
 	}
	if (!(imports = malloc ((bin->dysymtab.nundefsym + 1) * sizeof (struct import_t)))) {
		return NULL;
	}
	for (i = j = 0; i < bin->dysymtab.nundefsym; i++) {
		idx = bin->dysymtab.iundefsym + i;
		if (idx < 0 || idx >= bin->nsymtab) {
			bprintf ("WARNING: Imports index out of bounds. Ignoring relocs\n");
			free (imports);
			return NULL;
		}
		stridx = bin->symtab[idx].n_strx;
		if (stridx >= 0 && stridx < bin->symstrlen) {
			symstr = (char *)bin->symstr + stridx;
		} else {
			symstr = "";
		}
		if (!*symstr) {
			continue;
		}
		{
			int i = 0;
			int len = 0;
			char *symstr_dup = NULL;
			len = bin->symstrlen - stridx;
			imports[j].name[0] = 0;
			if (len > 0) {
				for (i = 0; i < len; i++) {
					if ((unsigned char)symstr[i] == 0xff || !symstr[i]) {
						len = i;
						break;
					}
				}
				symstr_dup = r_str_ndup (symstr, len);
				if (symstr_dup) {
					r_str_ncpy (imports[j].name, symstr_dup, R_BIN_MACH0_STRING_LENGTH);
					r_str_filter (imports[j].name, - 1);
					imports[j].name[R_BIN_MACH0_STRING_LENGTH - 2] = 0;
					free (symstr_dup);
				}
			}
		}
		imports[j].ord = i;
		imports[j++].last = 0;
	}
	imports[j].last = 1;

	if (!bin->imports_by_ord_size) {
		if (j > 0) {
			bin->imports_by_ord_size = j;
			bin->imports_by_ord = (RBinImport**)calloc (j, sizeof (RBinImport*));
		} else {
			bin->imports_by_ord_size = 0;
			bin->imports_by_ord = NULL;
		}
	}

	return imports;
}

Project: radare2

struct symbol_t* MACH0_(get_symbols)(struct MACH0_(obj_t)* bin) {
	const char *symstr;
	struct symbol_t *symbols;
	int from, to, i, j, s, stridx, symbols_size, symbols_count;
	SdbHash *hash;

	if (!bin || !bin->symtab || !bin->symstr) {
		return NULL;
	}
	/* parse symbol table */
	/* parse dynamic symbol table */
	symbols_count = (bin->dysymtab.nextdefsym + \
			bin->dysymtab.nlocalsym + \
			bin->dysymtab.nundefsym );
	symbols_count += bin->nsymtab;
	symbols_size = (symbols_count + 1) * 2 * sizeof (struct symbol_t);

	if (symbols_size < 1) {
		return NULL;
	}
	if (!(symbols = calloc (1, symbols_size))) {
		return NULL;
	}
	hash = sdb_ht_new ();
	j = 0; // symbol_idx
	for (s = 0; s < 2; s++) {
		switch (s) {
		case 0:
			from = bin->dysymtab.iextdefsym;
			to = from + bin->dysymtab.nextdefsym;
			break;
		case 1:
			from = bin->dysymtab.ilocalsym;
			to = from + bin->dysymtab.nlocalsym;
			break;
#if NOT_USED
		case 2:
			from = bin->dysymtab.iundefsym;
			to = from + bin->dysymtab.nundefsym;
			break;
#endif
		}
		if (from == to) {
			continue;
		}
#define OLD 1
#if OLD
		from = R_MIN (R_MAX (0, from), symbols_size / sizeof (struct symbol_t));
		to = R_MIN (to , symbols_size / sizeof (struct symbol_t));
		to = R_MIN (to, bin->nsymtab);
#else
		from = R_MIN (R_MAX (0, from), symbols_size/sizeof (struct symbol_t));
		to = symbols_count; //symbols_size/sizeof(struct symbol_t);
#endif
		int maxsymbols = symbols_size / sizeof (struct symbol_t);
		if (to > 0x500000) {
			bprintf ("WARNING: corrupted mach0 header: symbol table is too big %d\n", to);
			free (symbols);
			sdb_ht_free (hash);
			return NULL;
		}
		if (symbols_count >= maxsymbols) {
			symbols_count = maxsymbols - 1;
		}
		for (i = from; i < to && j < symbols_count; i++, j++) {
			symbols[j].offset = addr_to_offset (bin, bin->symtab[i].n_value);
			symbols[j].addr = bin->symtab[i].n_value;
			symbols[j].size = 0; /* TODO: Is it anywhere? */
			if (bin->symtab[i].n_type & N_EXT) {
				symbols[j].type = R_BIN_MACH0_SYMBOL_TYPE_EXT;
			} else {
				symbols[j].type = R_BIN_MACH0_SYMBOL_TYPE_LOCAL;
			}
			stridx = bin->symtab[i].n_strx;
			if (stridx >= 0 && stridx < bin->symstrlen) {
				symstr = (char*)bin->symstr + stridx;
			} else {
				symstr = "???";
			}
			{
				int i = 0;
				int len = 0;
				len = bin->symstrlen - stridx;
				if (len > 0) {
					for (i = 0; i < len; i++) {
						if ((ut8)(symstr[i] & 0xff) == 0xff || !symstr[i]) {
							len = i;
							break;
						}
					}
					char *symstr_dup = NULL;
					if (len > 0) {
						symstr_dup = r_str_ndup (symstr, len);
					}
					if (!symstr_dup) {
						symbols[j].name[0] = 0;
					} else {
						r_str_ncpy (symbols[j].name, symstr_dup, R_BIN_MACH0_STRING_LENGTH);
						r_str_filter (symbols[j].name, -1);
						symbols[j].name[R_BIN_MACH0_STRING_LENGTH - 2] = 0;
					}
					free (symstr_dup);
				} else {
					symbols[j].name[0] = 0;
				}
				symbols[j].last = 0;
			}
			if (inSymtab (hash, symbols, symbols[j].name, symbols[j].addr)) {
				symbols[j].name[0] = 0;
				j--;
			}
		}
	}
	to = R_MIN (bin->nsymtab, bin->dysymtab.iundefsym + bin->dysymtab.nundefsym);
	for (i = bin->dysymtab.iundefsym; i < to; i++) {
		if (j > symbols_count) {
 			bprintf ("mach0-get-symbols: error\n");
 			break;
 		}
		if (parse_import_stub(bin, &symbols[j], i)) {
 			symbols[j++].last = 0;
		}
 	}
 
 #if 1
	for (i = 0; i < bin->nsymtab; i++) {
		struct MACH0_(nlist) *st = &bin->symtab[i];
#if 0
		bprintf ("stridx %d -> section %d type %d value = %d\n",
			st->n_strx, st->n_sect, st->n_type, st->n_value);
#endif
		stridx = st->n_strx;
		if (stridx >= 0 && stridx < bin->symstrlen) {
			symstr = (char*)bin->symstr + stridx;
		} else {
			symstr = "???";
		}
		int section = st->n_sect;
		if (section == 1 && j < symbols_count) { // text ??st->n_type == 1)
			/* is symbol */
			symbols[j].addr = st->n_value; // + text_base;
			symbols[j].offset = addr_to_offset (bin, symbols[j].addr);
			symbols[j].size = 0; /* find next symbol and crop */
			if (st->n_type & N_EXT) {
				symbols[j].type = R_BIN_MACH0_SYMBOL_TYPE_EXT;
			} else {
				symbols[j].type = R_BIN_MACH0_SYMBOL_TYPE_LOCAL;
			}
			strncpy (symbols[j].name, symstr, R_BIN_MACH0_STRING_LENGTH);
			symbols[j].name[R_BIN_MACH0_STRING_LENGTH - 1] = 0;
			symbols[j].last = 0;
			if (inSymtab (hash, symbols, symbols[j].name, symbols[j].addr)) {
				symbols[j].name[0] = 0;
			} else {
				j++;
			}
		}
	}
#endif
	sdb_ht_free (hash);
	symbols[j].last = 1;
	return symbols;
}

Project: radare2
File: mach0.c

static int parse_import_ptr(struct MACH0_(obj_t)* bin, struct reloc_t *reloc, int idx) {
	int i, j, sym, wordsize;
	ut32 stype;
	wordsize = MACH0_(get_bits)(bin) / 8;
	if (idx < 0 || idx >= bin->nsymtab) {
		return 0;
	}
	if ((bin->symtab[idx].n_desc & REFERENCE_TYPE) == REFERENCE_FLAG_UNDEFINED_LAZY) {
		stype = S_LAZY_SYMBOL_POINTERS;
	} else {
		stype = S_NON_LAZY_SYMBOL_POINTERS;
	}

	reloc->offset = 0;
	reloc->addr = 0;
	reloc->addend = 0;
#define CASE(T) case (T / 8): reloc->type = R_BIN_RELOC_ ## T; break
	switch (wordsize) {
		CASE(8);
		CASE(16);
		CASE(32);
		CASE(64);
		default: return false;
	}
#undef CASE
 
 	for (i = 0; i < bin->nsects; i++) {
 		if ((bin->sects[i].flags & SECTION_TYPE) == stype) {
			for (j = 0, sym = -1; bin->sects[i].reserved1 + j < bin->nindirectsyms; j++) {
				int indidx = bin->sects[i].reserved1 + j;
				if (indidx < 0 || indidx >= bin->nindirectsyms) {
					break;
				}
				if (idx == bin->indirectsyms[indidx]) {
 					sym = j;
 					break;
 				}
			}
 			reloc->offset = sym == -1 ? 0 : bin->sects[i].offset + sym * wordsize;
 			reloc->addr = sym == -1 ? 0 : bin->sects[i].addr + sym * wordsize;
 			return true;
		}
	}
	return false;
}