From d0bf6c003f5bf92b2ad89d427e525cd49ce39db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Felipe=20Melchor?= Date: Fri, 1 May 2015 12:15:06 +0200 Subject: [PATCH] Added checks in mach parser & fix #2465 --- libr/bin/format/mach0/fatmach0.c | 14 ++- libr/bin/format/mach0/mach0.c | 171 +++++++++++++++++++++++++++---- 2 files changed, 161 insertions(+), 24 deletions(-) diff --git a/libr/bin/format/mach0/fatmach0.c b/libr/bin/format/mach0/fatmach0.c index e0896ab4917b0..7929fd192b784 100644 --- a/libr/bin/format/mach0/fatmach0.c +++ b/libr/bin/format/mach0/fatmach0.c @@ -7,7 +7,7 @@ static int r_bin_fatmach0_init(struct r_bin_fatmach0_obj_t* bin) { int len = r_buf_fread_at (bin->b, 0, (ut8*)&bin->hdr, "2I", 1); - if (len == -1) { + if (len == 0 || len == -1) { perror ("read (fat_header)"); return R_FALSE; } @@ -19,8 +19,9 @@ static int r_bin_fatmach0_init(struct r_bin_fatmach0_obj_t* bin) { return R_FALSE; } len = r_buf_fread_at (bin->b, R_BUF_CUR, (ut8*)bin->archs, "5I", bin->nfat_arch); - if (len == -1) { + if (len == 0 || len == -1) { perror ("read (fat_arch)"); + R_FREE (bin->archs); return R_FALSE; } return R_TRUE; @@ -30,9 +31,14 @@ struct r_bin_fatmach0_arch_t *r_bin_fatmach0_extract(struct r_bin_fatmach0_obj_t struct r_bin_fatmach0_arch_t *ret; ut8 *buf = NULL; - if (!bin || (idx < 0) || (idx > bin->hdr.nfat_arch)) + if (!bin || (idx < 0) || (idx > bin->nfat_arch)) return NULL; - if (narch) *narch = bin->hdr.nfat_arch; + + if (bin->archs[idx].offset > bin->size ||\ + bin->archs[idx].offset + bin->archs[idx].size > bin->size) + return NULL; + + if (narch) *narch = bin->nfat_arch; if (!(ret = R_NEW0 (struct r_bin_fatmach0_arch_t))) { perror ("malloc (ret)"); return NULL; diff --git a/libr/bin/format/mach0/mach0.c b/libr/bin/format/mach0/mach0.c index f3171e6bedca7..81ef372b4bc23 100644 --- a/libr/bin/format/mach0/mach0.c +++ b/libr/bin/format/mach0/mach0.c @@ -70,10 +70,19 @@ static int init_hdr(struct MACH0_(obj_t)* bin) { static int parse_segments(struct MACH0_(obj_t)* bin, ut64 off) { int sect, len, seg = bin->nsegs - 1; + ut32 size_sects; + + if (!UT32_MUL (&size_sects, bin->nsegs, sizeof (struct MACH0_(segment_command)))){ + return R_FALSE; + } + if (!size_sects || size_sects > bin->size) + return R_FALSE; if (!(bin->segs = realloc (bin->segs, bin->nsegs * sizeof(struct MACH0_(segment_command))))) { perror ("realloc (seg)"); return R_FALSE; } + if (off > bin->size || off + sizeof (struct MACH0_(segment_command)) > bin->size) + return R_FALSE; #if R_BIN_MACH064 len = r_buf_fread_at (bin->b, off, (ut8*)&bin->segs[seg], bin->endian?"2I16c4L4I":"2i16c4l4i", 1); @@ -93,7 +102,7 @@ static int parse_segments(struct MACH0_(obj_t)* bin, ut64 off) { "enum { FOO=1, FOO=2, FOO=4 }", 0); #endif - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (seg)\n"); return R_FALSE; } @@ -107,6 +116,16 @@ static int parse_segments(struct MACH0_(obj_t)* bin, ut64 off) { bin->nsects = new_nsects; } if ((int)bin->nsects>0) { + if (!UT32_MUL (&size_sects, bin->nsects-sect, sizeof (struct MACH0_(section)))){ + return R_FALSE; + } + if (!size_sects || size_sects > bin->size) + return R_FALSE; + + if (off + sizeof (struct MACH0_(segment_command)) > bin->size ||\ + off + sizeof (struct MACH0_(segment_command)) + size_sects > bin->size) + return R_FALSE; + if (!(bin->sects = realloc (bin->sects, bin->nsects * sizeof (struct MACH0_(section))))) { perror ("realloc (sects)"); return R_FALSE; @@ -119,7 +138,7 @@ static int parse_segments(struct MACH0_(obj_t)* bin, ut64 off) { bin->endian?"16c16c9I":"16c16c9i", #endif bin->nsects - sect); - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (sects)\n"); return R_FALSE; } @@ -133,9 +152,13 @@ static int parse_segments(struct MACH0_(obj_t)* bin, ut64 off) { static int parse_symtab(struct MACH0_(obj_t)* bin, ut64 off) { struct symtab_command st; + ut32 size_sym; + + if (off > bin->size || off + sizeof (struct symtab_command) > bin->size) + return R_FALSE; int len = r_buf_fread_at (bin->b, off, (ut8*)&st, bin->endian?"6I":"6i", 1); - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (symtab)\n"); return R_FALSE; } @@ -143,6 +166,14 @@ static int parse_symtab(struct MACH0_(obj_t)* bin, ut64 off) { bin->nsymtab = 0; if (st.strsize > 0 && st.strsize < bin->size && st.nsyms > 0) { bin->nsymtab = st.nsyms; + if (st.stroff > bin->size || st.stroff + st.strsize > bin->size) + return R_FALSE; + if (!UT32_MUL (&size_sym, bin->nsymtab, sizeof (struct MACH0_(nlist)))) + return R_FALSE; + if (!size_sym) + return R_FALSE; + if (st.symoff > bin->size || st.symoff + size_sym > bin->size) + return R_FALSE; if (!(bin->symstr = malloc (st.strsize))) { perror ("malloc (symstr)"); return R_FALSE; @@ -166,7 +197,7 @@ static int parse_symtab(struct MACH0_(obj_t)* bin, ut64 off) { len = r_buf_fread_at (bin->b, st.symoff, (ut8*)bin->symtab, bin->endian?"I2cSI":"i2csi", bin->nsymtab); #endif - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (nlist)\n"); R_FREE (bin->symtab); return R_FALSE; @@ -177,9 +208,13 @@ static int parse_symtab(struct MACH0_(obj_t)* bin, ut64 off) { static int parse_dysymtab(struct MACH0_(obj_t)* bin, ut64 off) { int len; + ut32 size_tab; + + if (off > bin->size || off + sizeof (struct dysymtab_command) > bin->size) + return R_FALSE; len = r_buf_fread_at(bin->b, off, (ut8*)&bin->dysymtab, bin->endian?"20I":"20i", 1); - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (dysymtab)\n"); return R_FALSE; } @@ -189,9 +224,21 @@ static int parse_dysymtab(struct MACH0_(obj_t)* bin, ut64 off) { perror ("malloc (toc)"); return R_FALSE; } + if (!UT32_MUL (&size_tab, bin->ntoc, sizeof (struct dylib_table_of_contents))){ + R_FREE (bin->toc); + return R_FALSE; + } + if (!size_tab){ + R_FREE (bin->toc); + return R_FALSE; + } + if (bin->dysymtab.tocoff > bin->size || bin->dysymtab.tocoff + size_tab > bin->size){ + R_FREE (bin->toc); + return R_FALSE; + } len = r_buf_fread_at(bin->b, bin->dysymtab.tocoff, (ut8*)bin->toc, bin->endian?"2I":"2i", bin->ntoc); - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (toc)\n"); R_FREE (bin->toc); return R_FALSE; @@ -203,6 +250,19 @@ static int parse_dysymtab(struct MACH0_(obj_t)* bin, ut64 off) { perror ("malloc (modtab)"); return R_FALSE; } + if (!UT32_MUL (&size_tab, bin->nmodtab, sizeof (struct MACH0_(dylib_module)))){ + R_FREE (bin->modtab); + return R_FALSE; + } + if (!size_tab){ + R_FREE (bin->modtab); + return R_FALSE; + } + if (bin->dysymtab.modtaboff > bin->size || \ + bin->dysymtab.modtaboff + size_tab > bin->size){ + R_FREE (bin->modtab); + return R_FALSE; + } #if R_BIN_MACH064 len = r_buf_fread_at (bin->b, bin->dysymtab.modtaboff, (ut8*)bin->modtab, bin->endian?"12IL":"12il", bin->nmodtab); @@ -222,6 +282,20 @@ static int parse_dysymtab(struct MACH0_(obj_t)* bin, ut64 off) { perror ("malloc (indirectsyms)"); return R_FALSE; } + if (!UT32_MUL (&size_tab, bin->nindirectsyms, sizeof (ut32))){ + R_FREE (bin->indirectsyms); + return R_FALSE; + } + if (!size_tab){ + R_FREE (bin->indirectsyms); + return R_FALSE; + } + if (bin->dysymtab.indirectsymoff > bin->size || \ + bin->dysymtab.indirectsymoff + size_tab > bin->size){ + R_FREE (bin->indirectsyms); + return R_FALSE; + } + len = r_buf_fread_at (bin->b, bin->dysymtab.indirectsymoff, (ut8*)bin->indirectsyms, bin->endian?"I":"i", bin->nindirectsyms); if (len == -1) { @@ -239,9 +313,12 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 ut32 flavor, count; int len; + if (off > bin->size || off + sizeof (struct thread_command) > bin->size) + return R_FALSE; + len = r_buf_fread_at (bin->b, off, (ut8*)&bin->thread, bin->endian?"2I":"2i", 1); - if (len == -1) + if (len == 0 || len == -1) goto wrong_read; len = r_buf_fread_at(bin->b, off + sizeof(struct thread_command), @@ -249,6 +326,10 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 if (len == -1) goto wrong_read; + if (off + sizeof(struct thread_command) + sizeof(flavor) > bin->size || \ + off + sizeof(struct thread_command) + sizeof(flavor) + sizeof (ut32) > bin->size) + return R_FALSE; + // TODO: use count for checks len = r_buf_fread_at(bin->b, off + sizeof(struct thread_command) + sizeof(flavor), (ut8*)&count, bin->endian?"1I":"1i", 1); @@ -257,11 +338,16 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 ptr_thread = off + sizeof(struct thread_command) + sizeof(flavor) + sizeof(count); + if (ptr_thread > bin->size) + return R_FALSE; + switch (bin->hdr.cputype) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: switch (flavor) { case X86_THREAD_STATE32: + if (ptr_thread + sizeof (struct x86_thread_state32) > bin->size) + return R_FALSE; if ((len = r_buf_fread_at (bin->b, ptr_thread, (ut8*)&bin->thread_state.x86_32, "16i", 1)) == -1) { eprintf ("Error: read (thread state x86_32)\n"); @@ -272,6 +358,8 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 pc_offset = ptr_thread + r_offsetof(struct x86_thread_state32, eip); break; case X86_THREAD_STATE64: + if (ptr_thread + sizeof (struct x86_thread_state64) > bin->size) + return R_FALSE; if ((len = r_buf_fread_at (bin->b, ptr_thread, (ut8*)&bin->thread_state.x86_64, "32l", 1)) == -1) { eprintf ("Error: read (thread state x86_64)\n"); @@ -286,6 +374,8 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 case CPU_TYPE_POWERPC: case CPU_TYPE_POWERPC64: if (flavor == X86_THREAD_STATE32) { + if (ptr_thread + sizeof (struct ppc_thread_state32)) + return R_FALSE; if ((len = r_buf_fread_at (bin->b, ptr_thread, (ut8*)&bin->thread_state.ppc_32, bin->endian?"40I":"40i", 1)) == -1) { eprintf ("Error: read (thread state ppc_32)\n"); @@ -294,6 +384,8 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 pc = bin->thread_state.ppc_32.srr0; pc_offset = ptr_thread + r_offsetof(struct ppc_thread_state32, srr0); } else if (flavor == X86_THREAD_STATE64) { + if (ptr_thread + sizeof (struct ppc_thread_state64)) + return R_FALSE; if ((len = r_buf_fread_at (bin->b, ptr_thread, (ut8*)&bin->thread_state.ppc_64, bin->endian?"34LI3LI":"34li3li", 1)) == -1) { eprintf ("Error: read (thread state ppc_64)\n"); @@ -304,6 +396,8 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 } break; case CPU_TYPE_ARM: + if (ptr_thread + sizeof (struct arm_thread_state32) > bin->size) + return R_FALSE; if ((len = r_buf_fread_at (bin->b, ptr_thread, (ut8*)&bin->thread_state.arm_32, bin->endian?"17I":"17i", 1)) == -1) { eprintf ("Error: read (thread state arm)\n"); @@ -313,6 +407,8 @@ static int parse_thread(struct MACH0_(obj_t)* bin, struct load_command *lc, ut64 pc_offset = ptr_thread + r_offsetof(struct arm_thread_state32, r15); break; case CPU_TYPE_ARM64: + if (ptr_thread + sizeof (struct arm_thread_state64) > bin->size) + return R_FALSE; if ((len = r_buf_fread_at(bin->b, ptr_thread, (ut8*)&bin->thread_state.arm_64, bin->endian?"34LI1I":"34Li1i", 1)) == -1) { eprintf ("Error: read (thread state arm)\n"); @@ -345,17 +441,26 @@ static int parse_dylib(struct MACH0_(obj_t)* bin, ut64 off) { struct dylib_command dl; int lib, len; + if (off > bin->size || off + sizeof (struct dylib_command) > bin->size) + return R_FALSE; lib = bin->nlibs - 1; + if (!(bin->libs = realloc (bin->libs, bin->nlibs * R_BIN_MACH0_STRING_LENGTH))) { perror ("realloc (libs)"); return R_FALSE; } len = r_buf_fread_at (bin->b, off, (ut8*)&dl, bin->endian?"6I":"6i", 1); - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (dylib)\n"); return R_FALSE; } - if (r_buf_read_at (bin->b, off+dl.dylib.name.offset, (ut8*)bin->libs[lib], R_BIN_MACH0_STRING_LENGTH) == -1) { + + if (off + dl.dylib.name.offset > bin->size ||\ + off + dl.dylib.name.offset + R_BIN_MACH0_STRING_LENGTH > bin->size) + return R_FALSE; + + len = r_buf_read_at (bin->b, off+dl.dylib.name.offset, (ut8*)bin->libs[lib], R_BIN_MACH0_STRING_LENGTH); + if (len == 0 || len == -1) { eprintf ("Error: read (dylib str)"); return R_FALSE; } @@ -371,10 +476,15 @@ static int init_items(struct MACH0_(obj_t)* bin) { bin->uuidn = 0; bin->os = 0; bin->has_crypto = 0; + if (bin->hdr.sizeofcmds > bin->size) + return R_FALSE; for (i = 0, off = sizeof (struct MACH0_(mach_header)); \ i < bin->hdr.ncmds; i++, off += lc.cmdsize) { + if (off > bin->size || off + sizeof (struct load_command) > bin->size){ + return R_FALSE; + } len = r_buf_fread_at (bin->b, off, (ut8*)&lc, bin->endian?"2I":"2i", 1); - if (len == -1) { + if (len == 0 || len == -1) { eprintf ("Error: read (lc) at 0x%08"PFMT64x"\n", off); return R_FALSE; } @@ -427,6 +537,8 @@ static int init_items(struct MACH0_(obj_t)* bin) { case LC_UUID: { struct uuid_command uc = {0}; + if (off + sizeof (struct uuid_command) > bin->size) + return R_FALSE; if (r_buf_fread_at (bin->b, off, (ut8*)&uc, "24c", 1) != -1) { char key[128]; char val[128]; @@ -442,6 +554,8 @@ static int init_items(struct MACH0_(obj_t)* bin) { case LC_ENCRYPTION_INFO: { struct encryption_info_command eic = {0}; + if (off + sizeof (struct encryption_info_command) > bin->size) + return R_FALSE; if (r_buf_fread_at (bin->b, off, (ut8*)&eic, bin->endian?"5I":"5i", 1) != -1) { bin->has_crypto = 1; @@ -465,7 +579,8 @@ static int init_items(struct MACH0_(obj_t)* bin) { eprintf("Error: LC_MAIN with other threads\n"); return R_FALSE; } - + if (off+8 > bin->size || off + sizeof (ep) > bin->size) + return R_FALSE; r_buf_fread_at (bin->b, off+8, (void*)&ep, bin->endian?"2L": "2l", 1); bin->entry = ep.eo; @@ -496,6 +611,10 @@ static int init_items(struct MACH0_(obj_t)* bin) { case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: bin->dyld_info = malloc (sizeof(struct dyld_info_command)); + if (off + sizeof (struct dyld_info_command) > bin->size){ + free (bin->dyld_info); + return R_FALSE; + } if (r_buf_fread_at (bin->b, off, (ut8*)bin->dyld_info, bin->endian?"12I":"12i", 1) == -1) { free (bin->dyld_info); @@ -679,6 +798,8 @@ static int parse_import_stub(struct MACH0_(obj_t)* bin, struct symbol_t *symbol, if (bin->indirectsyms) if (idx != bin->indirectsyms[bin->sects[i].reserved1 + j]) continue; + if (idx > bin->nsymtab) + continue; symbol->type = R_BIN_MACH0_SYMBOL_TYPE_LOCAL; symbol->offset = bin->sects[i].offset + j * bin->sects[i].reserved2; symbol->addr = bin->sects[i].addr + j * bin->sects[i].reserved2; @@ -818,7 +939,7 @@ struct symbol_t* MACH0_(get_symbols)(struct MACH0_(obj_t)* bin) { len = bin->symstrlen - stridx; if (len>0) { for (i = 0; isymstrlen - stridx; if (len>0) { for (i = 0; idyld_info) { ut8 *opcodes, *p, *end, type = 0, rel_type = 0; @@ -1011,6 +1134,11 @@ struct reloc_t* MACH0_(get_relocs)(struct MACH0_(obj_t)* bin) { if ((bind_size +lazy_size)<1) { return NULL; } + if (bin->dyld_info->bind_off > bin->size || bin->dyld_info->bind_off + bind_size > bin->size) + return NULL; + if (bin->dyld_info->lazy_bind_off > bin->size || \ + bin->dyld_info->lazy_bind_off + lazy_size > bin->size) + return NULL; // NOTE(eddyb) it's a waste of memory, but we don't know the actual number of relocs. if (!(relocs = malloc ((bind_size + lazy_size) * sizeof (struct reloc_t)))) return NULL; @@ -1020,18 +1148,18 @@ struct reloc_t* MACH0_(get_relocs)(struct MACH0_(obj_t)* bin) { free (relocs); return NULL; } - if (r_buf_read_at (bin->b, bin->dyld_info->bind_off, opcodes, bind_size) == -1 - || r_buf_read_at (bin->b, bin->dyld_info->lazy_bind_off, opcodes + bind_size, lazy_size) == -1) { + len = r_buf_read_at (bin->b, bin->dyld_info->bind_off, opcodes, bind_size); + i = r_buf_read_at (bin->b, bin->dyld_info->lazy_bind_off, opcodes + bind_size, lazy_size); + if (len == 0 || len == -1 || i == 0 || i == -1) { eprintf ("Error: read (dyld_info bind) at 0x%08"PFMT64x"\n", (ut64)(size_t)bin->dyld_info->bind_off); free (opcodes); relocs[i].last = 1; return relocs; } - - for (p = opcodes, end = opcodes + bind_size + lazy_size; p < end; ) { + i = 0; + for (p = opcodes, end = opcodes + bind_size + lazy_size; p < end; p++) { ut8 imm = *p & BIND_IMMEDIATE_MASK, op = *p & BIND_OPCODE_MASK; - p++; switch (op) { #define ULEB() read_uleb128 (&p) #define SLEB() read_sleb128 (&p) @@ -1462,7 +1590,10 @@ ut64 MACH0_(get_main)(struct MACH0_(obj_t)* bin) { ut8 b[128]; ut64 entry = addr_to_offset(bin, bin->entry); // XXX: X86 only and hacky! - if (r_buf_read_at (bin->b, entry, b, sizeof (b)) == -1) + if (entry > bin->size || entry + sizeof (b) > bin->size) + return 0; + i = r_buf_read_at (bin->b, entry, b, sizeof (b)); + if (i == 0 || i == -1) return 0; for (i=0; i<64; i++) { if (b[i] == 0xe8 && !b[i+3] && !b[i+4]) {