CVE-2017-7946 (C, published 2017-04-18T20:59Z)
Description: value: The get_relocs_64 function in libr/bin/format/mach0/mach0.c in radare2 1.3.0 allows remote attackers to cause a denial of service (use-after-free and application crash) via a crafted Mach0 file.
Severity: MEDIUM (CVSS v3 base score: 5.5)
Weakness: CWE-416 \u2014 Use After Free
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.
Repository: https://github.com/radare/radare2
Files changed:
libr/bin/format/mach0/mach0.c
Commit message: Fix null deref and uaf in mach0 parser


Vulnerable code:
			if (!parse_dylib(bin, off)){
			bin->dyld_info = malloc (sizeof(struct dyld_info_command));

			if (off + sizeof (struct dyld_info_command) > bin->size){
				bprintf ("Cannot parse dyldinfo\n");
				free (bin->dyld_info);
				return false;
			}
			if (r_buf_read_at (bin->b, off, dyldi, sizeof (struct dyld_info_command)) == -1) {
				free (bin->dyld_info);
				bin->dyld_info = NULL;
				bprintf ("Error: read (LC_DYLD_INFO) at 0x%08"PFMT64x"\n", off);
			} else {
				bin->dyld_info->cmd = r_read_ble32 (&dyldi[0], bin->big_endian);
				bin->dyld_info->cmdsize = r_read_ble32 (&dyldi[4], bin->big_endian);
				bin->dyld_info->rebase_off = r_read_ble32 (&dyldi[8], bin->big_endian);
				bin->dyld_info->rebase_size = r_read_ble32 (&dyldi[12], bin->big_endian);
				bin->dyld_info->bind_off = r_read_ble32 (&dyldi[16], bin->big_endian);
				bin->dyld_info->bind_size = r_read_ble32 (&dyldi[20], bin->big_endian);
				bin->dyld_info->weak_bind_off = r_read_ble32 (&dyldi[24], bin->big_endian);
				bin->dyld_info->weak_bind_size = r_read_ble32 (&dyldi[28], bin->big_endian);
				bin->dyld_info->lazy_bind_off = r_read_ble32 (&dyldi[32], bin->big_endian);
				bin->dyld_info->lazy_bind_size = r_read_ble32 (&dyldi[36], bin->big_endian);
				bin->dyld_info->export_off = r_read_ble32 (&dyldi[40], bin->big_endian);
				bin->dyld_info->export_size = r_read_ble32 (&dyldi[44], bin->big_endian);
		if (bin->dyld_info->bind_off > bin->size || bin->dyld_info->bind_off + bind_size > bin->size)
			bin->dyld_info->lazy_bind_off + lazy_size > bin->size)
		if (bin->dyld_info->bind_off+bind_size+lazy_size > bin->size)
		if (!(relocs = calloc (1, (1 + bind_size + lazy_size) * sizeof (struct reloc_t))))

		if (!bin->symtab || !bin->symstr || !bin->sects || !bin->indirectsyms)
		if (!(relocs = malloc ((bin->dysymtab.nundefsym + 1) * sizeof(struct reloc_t))))
			if (parse_import_ptr(bin, &relocs[i], bin->dysymtab.iundefsym + j)) {

	if (!bin->nlibs)
	if (!(libs = calloc ((bin->nlibs + 1), sizeof(struct lib_t))))
	if (bin->hdr.filetype != MH_EXECUTE && bin->hdr.filetype != MH_DYLINKER)

	for (i = 0; i < bin->nsegs; ++i)
		if (bin->segs[i].fileoff == 0 && bin->segs[i].filesize != 0)
		if (entry > bin->size || entry + sizeof (b) > bin->size)

Fixed code:
			if (!parse_dylib (bin, off)){
			bin->dyld_info = calloc (1, sizeof (struct dyld_info_command));
			if (bin->dyld_info) {
				if (off + sizeof (struct dyld_info_command) > bin->size){
					bprintf ("Cannot parse dyldinfo\n");
					R_FREE (bin->dyld_info);
					return false;
				}
				if (r_buf_read_at (bin->b, off, dyldi, sizeof (struct dyld_info_command)) == -1) {
					free (bin->dyld_info);
					bin->dyld_info = NULL;
					bprintf ("Error: read (LC_DYLD_INFO) at 0x%08"PFMT64x"\n", off);
				} else {
					bin->dyld_info->cmd = r_read_ble32 (&dyldi[0], bin->big_endian);
					bin->dyld_info->cmdsize = r_read_ble32 (&dyldi[4], bin->big_endian);
					bin->dyld_info->rebase_off = r_read_ble32 (&dyldi[8], bin->big_endian);
					bin->dyld_info->rebase_size = r_read_ble32 (&dyldi[12], bin->big_endian);
					bin->dyld_info->bind_off = r_read_ble32 (&dyldi[16], bin->big_endian);
					bin->dyld_info->bind_size = r_read_ble32 (&dyldi[20], bin->big_endian);
					bin->dyld_info->weak_bind_off = r_read_ble32 (&dyldi[24], bin->big_endian);
					bin->dyld_info->weak_bind_size = r_read_ble32 (&dyldi[28], bin->big_endian);
					bin->dyld_info->lazy_bind_off = r_read_ble32 (&dyldi[32], bin->big_endian);
					bin->dyld_info->lazy_bind_size = r_read_ble32 (&dyldi[36], bin->big_endian);
					bin->dyld_info->export_off = r_read_ble32 (&dyldi[40], bin->big_endian);
					bin->dyld_info->export_size = r_read_ble32 (&dyldi[44], bin->big_endian);
				}
		if (bin->dyld_info->bind_off > bin->size || bin->dyld_info->bind_off + bind_size > bin->size) {
		}
			bin->dyld_info->lazy_bind_off + lazy_size > bin->size) {
		}
		if (bin->dyld_info->bind_off+bind_size+lazy_size > bin->size) {
		}
		if (!(relocs = calloc (1, (1 + bind_size + lazy_size) * sizeof (struct reloc_t)))) {
		}
		if (!bin->symtab || !bin->symstr || !bin->sects || !bin->indirectsyms) {
		}
		if (!(relocs = malloc ((bin->dysymtab.nundefsym + 1) * sizeof(struct reloc_t)))) {
		}
			if (parse_import_ptr (bin, &relocs[i], bin->dysymtab.iundefsym + j)) {
	if (!bin->nlibs) {
	}
	if (!(libs = calloc ((bin->nlibs + 1), sizeof(struct lib_t)))) {
	}
	if (bin->hdr.filetype != MH_EXECUTE && bin->hdr.filetype != MH_DYLINKER) {
	}
	for (i = 0; i < bin->nsegs; ++i) {
		if (bin->segs[i].fileoff == 0 && bin->segs[i].filesize != 0) {
		}
	}
		if (entry > bin->size || entry + sizeof (b) > bin->size) {
		}