diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 216 | ||||
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/addr2line.c | 56 | ||||
| -rw-r--r-- | src/elfcmp.c | 8 | ||||
| -rw-r--r-- | src/elflint.c | 8 | ||||
| -rw-r--r-- | src/findtextrel.c | 14 | ||||
| -rw-r--r-- | src/objdump.c | 18 | ||||
| -rw-r--r-- | src/readelf.c | 657 | ||||
| -rw-r--r-- | src/size.c | 14 | ||||
| -rw-r--r-- | src/strip.c | 13 | ||||
| -rw-r--r-- | src/unstrip.c | 28 |
11 files changed, 782 insertions, 252 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 2c065d17..1162f6e1 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,219 @@ +2014-12-18 Ulrich Drepper <[email protected]> + + * Makefile.am: Suppress output of textrel_check command. + +2014-12-17 Mark Wielaard <[email protected]> + + * readelf.c (print_cfa_program): Add bounds check before each op that + takes at least one argument. + +2014-12-16 Mark Wielaard <[email protected]> + + * readelf.c (print_decoded_line_section): Print dwarf_errmsg if + dwarf_onesrcline or dwarf_linesrc fails. + +2014-12-16 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_line_section): Correct overflow check for + unit_length. + (print_debug_aranges_section): Correct overflow check for length. + +2014-12-15 Mark Wielaard <[email protected]> + + * readelf.c (notice_listptr): Return false if offset doesn't fit + in 61-bits. + (attr_callback): Warn if loclist or rangelist offset doesn't fit. + +2014-12-15 Mark Wielaard <[email protected]> + + * readelf.c (print_ops): Don't assert when addr_size or ref_size + is not 4 or 8, just report invalid data. + +2014-12-15 Mark Wielaard <[email protected]> + + * readelf.c (print_gdb_index_section): Add more bounds checks. + +2014-12-15 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_line_section): Check there is enough room + for DW_LNE_set_address argument. Make sure there is enough room + for the the initial unit_length. + +2014-12-14 Mark Wielaard <[email protected]> + + * elflint.c (check_attributes): Call get_uleb128 with end pointer. + * readelf.c (print_attributes): Likewise. + (print_ops): Likewise and also for get_sleb128. + (print_cfa_program): Likewise and add more readp bounds checks. + (read_encoded): Likewise. + (print_debug_frame_section): Likewise. + (print_debug_line_section): Likewise. + (print_debug_macinfo_section): Likewise. + (print_debug_macro_section): Likewise. + (print_debug_exception_table): Likewise. + +2014-12-16 Mark Wielaard <[email protected]> + + * elfcmp.c (compare_Elf32_Word): Make sure (unsigned) Elf32_Word + difference doesn't wrap around before returning as int. + +2014-12-11 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_exception_table): Check TType base offset + and Action table are sane. + +2014-12-11 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_frame_section): Check number of augmentation + chars to print. + +2014-12-09 Mark Wielaard <[email protected]> + + * readelf.c (handle_file_note): Check count fits data section and + doesn't overflow fptr. + +2014-12-08 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_exception_table): Report invalid data if + action table doesn't immediately follow call site table. + +2014-12-10 Josh Stone <[email protected]> + + * addr2line.c (get_diename): New, get linkage_name or name. + * addr2line.c (print_dwarf_function): Use get_diename. + * addr2line.c (handle_address): Likewise. + * addr2line.c (print_diesym): Removed. + +2014-12-10 Josh Stone <[email protected]> + + * addr2line.c (handle_address): Find the proper inline parents. + +2014-12-07 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_line_section): max_ops_per_instr cannot + be zero. + +2014-12-07 Mark Wielaard <[email protected]> + + * readelf.c (print_ops): Handle zero ref_size for DW_OP_call_ref + and DW_OP_GNU_implicit_pointer. + +2014-12-04 Mark Wielaard <[email protected]> + + * objdump.c (show_relocs_x): Make sure destshdr exists. + (show_relocs_rel): Don't rely on shdr->sh_entsize, use gelf_fsize. + (show_relocs_rela): Likewise. + (show_relocs): Make sure destshdr, symshdr and symdata exists. + +2014-11-30 Mark Wielaard <[email protected]> + + * readelf.c (handle_sysv_hash64): Fix overflow check. + +2014-11-28 Mark Wielaard <[email protected]> + + * readelf.c (handle_relocs_rel): Don't reuse destshdr to store + section header of a relocation against a STT_SECTION symbol. Use + a new local variable secshdr. + (handle_relocs_rela): Likewise. + +2014-11-26 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_aranges_section): Cast Dwarf_Word length + to ptrdiff_t for comparison. + +2014-11-24 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_line_section): Check line_range is not zero + before usage. + +2014-11-23 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_aranges_section): Check length to catch + nexthdr overflow. + +2014-11-21 Mark Wielaard <[email protected]> + + * readelf.c (print_attributes): Guard against empty section. + Document attribute format. Break when vendor name isn't terminated. + Add overflow check for subsection_len. Handle both gnu and non-gnu + attribute tags. + +2014-11-22 Mark Wielaard <[email protected]> + + * elflint.c (check_sections): Call ebl_bss_plt_p without ehdr. + * findtextrel.c (process_file): Use elf_getphdrnum. + * readelf.c (process_elf_file): Remove redundant ehdr->e_phoff check. + (print_phdr): Check phnum. + * size.c (show_segments): Use elf_getphdrnum. + * strip.c (handle_elf): Likewise. + * unstrip.c (copy_elf): Likewise. + (copy_elided_sections): Likewise. + (handle_file): Likewise. + +2014-11-18 Mark Wielaard <[email protected]> + + * readelf.c (print_cfa_program): Fix sanity check of DW_FORM_block + length. + +2014-11-17 Mark Wielaard <[email protected]> + + * readelf.c (handle_verneed): Check vna_next and vn_next exist. + (handle_verdef): Check vda_next and vd_next exist. + (handle_versym): Check vd_next, vna_next and vn_next exist. + Check vername and filename are not NULL before use. + +2014-11-17 Mark Wielaard <[email protected]> + + * elfcmp.c (main): Check section names are NULL before use. + * objdump.c (section_match): Likewise. + * size.c (show_sysv): Likewise. + +2014-11-17 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_frame_section): Warn if ptr_size is not 4 + or 8 instead of just calling print_cfa_program. + +2014-11-16 Mark Wielaard <[email protected]> + + * readelf (process_elf_file): Set phnum to zero if there aren't + actually any pheaders. + (print_phdr): Check there actually is a phdr. + +2014-11-16 Mark Wielaard <[email protected]> + + * readelf.c (print_cfa_program): Check block len before calling + print_ops. + +2014-11-14 Mark Wielaard <[email protected]> + + * readelf.c (print_debug_frame_section): Sanity Check CIE + unit_length and augmentationlen. + +2014-11-14 Mark Wielaard <[email protected]> + + * readelf.c (handle_versym): Check def == NULL before use. + +2014-11-08 Mark Wielaard <[email protected]> + + * readelf.c (handle_versym): Initialize vername and filename array + elements. + +2014-11-07 Mark Wielaard <[email protected]> + + * readelf.c (handle_sysv_hash): Sanity check section contents. + (handle_sysv_hash64): Likewise. + (handle_gnu_hash): Likewise. + +2014-09-14 Petr Machata <[email protected]> + + * readelf.c (handle_relocs_rela): Typo fix, test DESTSHDR properly. + +2014-09-12 Petr Machata <[email protected]> + + * readelf.c (encoded_ptr_size): In the switch statement, change + magic constants 3 and 4 to DW_EH_PE_* counterparts. Still accept + 0. Print diagnostic for anything else. + 2014-08-25 Josh Stone <[email protected]> * Makefile.am: Prevent premature @AR@ replacement in a sed expression. diff --git a/src/Makefile.am b/src/Makefile.am index 835db6c3..e84c7a59 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -132,7 +132,7 @@ libld_elf_i386.so: libld_elf_i386_pic.a libld_elf_i386.map $(LINK) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ $(libelf) $(libeu) \ -Wl,--version-script,$(srcdir)/libld_elf_i386.map - $(textrel_check) + @$(textrel_check) endif # Special rule to make it possible to define libld_elf_a_SOURCES as we do. diff --git a/src/addr2line.c b/src/addr2line.c index 2a0e408b..e982982d 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -258,6 +258,23 @@ parse_opt (int key, char *arg, struct argp_state *state) } +static const char * +get_diename (Dwarf_Die *die) +{ + Dwarf_Attribute attr; + const char *name; + + name = dwarf_formstring (dwarf_attr_integrate (die, DW_AT_MIPS_linkage_name, + &attr) + ?: dwarf_attr_integrate (die, DW_AT_linkage_name, + &attr)); + + if (name == NULL) + name = dwarf_diename (die) ?: "??"; + + return name; +} + static bool print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) { @@ -274,7 +291,7 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) { case DW_TAG_subprogram: { - const char *name = dwarf_diename (&scopes[i]); + const char *name = get_diename (&scopes[i]); if (name == NULL) return false; puts (name); @@ -283,7 +300,7 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) case DW_TAG_inlined_subroutine: { - const char *name = dwarf_diename (&scopes[i]); + const char *name = get_diename (&scopes[i]); if (name == NULL) return false; printf ("%s inlined", name); @@ -395,23 +412,6 @@ print_addrsym (Dwfl_Module *mod, GElf_Addr addr) } } -static void -print_diesym (Dwarf_Die *die) -{ - Dwarf_Attribute attr; - const char *name; - - name = dwarf_formstring (dwarf_attr_integrate (die, DW_AT_MIPS_linkage_name, - &attr) - ?: dwarf_attr_integrate (die, DW_AT_linkage_name, - &attr)); - - if (name == NULL) - name = dwarf_diename (die) ?: "??"; - - puts (name); -} - static int see_one_module (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), @@ -672,7 +672,23 @@ handle_address (const char *string, Dwfl *dwfl) continue; if (show_functions) - print_diesym (&scopes[i + 1]); + { + /* Search for the parent inline or function. It + might not be directly above this inline -- e.g. + there could be a lexical_block in between. */ + for (int j = i + 1; j < nscopes; j++) + { + Dwarf_Die *parent = &scopes[j]; + int tag = dwarf_tag (parent); + if (tag == DW_TAG_inlined_subroutine + || tag == DW_TAG_entry_point + || tag == DW_TAG_subprogram) + { + puts (get_diename (parent)); + break; + } + } + } src = NULL; lineno = 0; diff --git a/src/elfcmp.c b/src/elfcmp.c index 2d85f0b2..d1008b3c 100644 --- a/src/elfcmp.c +++ b/src/elfcmp.c @@ -1,5 +1,5 @@ /* Compare relevant content of two ELF files. - Copyright (C) 2005-2012 Red Hat, Inc. + Copyright (C) 2005-2012, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2005. @@ -355,7 +355,8 @@ main (int argc, char *argv[]) sym1->st_name); const char *name2 = elf_strptr (elf2, shdr2->sh_link, sym2->st_name); - if (unlikely (strcmp (name1, name2) != 0 + if (unlikely (name1 == NULL || name2 == NULL + || strcmp (name1, name2) != 0 || sym1->st_value != sym2->st_value || (sym1->st_size != sym2->st_size && sym1->st_shndx != SHN_UNDEF) @@ -810,8 +811,7 @@ compare_Elf32_Word (const void *p1, const void *p2) { const Elf32_Word *w1 = p1; const Elf32_Word *w2 = p2; - assert (sizeof (int) >= sizeof (*w1)); - return (int) *w1 - (int) *w2; + return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0; } static int diff --git a/src/elflint.c b/src/elflint.c index d6a47748..7e732531 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -3266,7 +3266,7 @@ section [%2d] '%s': offset %zu: unterminated vendor name string\n"), unsigned const char *chunk = q; unsigned int subsection_tag; - get_uleb128 (subsection_tag, q); + get_uleb128 (subsection_tag, q, p); if (q >= p) { @@ -3321,13 +3321,13 @@ section [%2d] '%s': offset %zu: attribute subsection has unexpected tag %u\n"), while (chunk < q) { unsigned int tag; - get_uleb128 (tag, chunk); + get_uleb128 (tag, chunk, q); uint64_t value = 0; const unsigned char *r = chunk; if (tag == 32 || (tag & 1) == 0) { - get_uleb128 (value, r); + get_uleb128 (value, r, q); if (r > q) { ERROR (gettext ("\ @@ -3532,7 +3532,7 @@ cannot get section header for section [%2zu] '%s': %s\n"), GElf_Word good_type = special_sections[s].type; if (IS_KNOWN_SPECIAL (s, ".plt", false) - && ebl_bss_plt_p (ebl, ehdr)) + && ebl_bss_plt_p (ebl)) good_type = SHT_NOBITS; /* In a debuginfo file, any normal section can be SHT_NOBITS. diff --git a/src/findtextrel.c b/src/findtextrel.c index 9913b82c..d7de202b 100644 --- a/src/findtextrel.c +++ b/src/findtextrel.c @@ -1,5 +1,5 @@ /* Locate source files or functions which caused text relocations. - Copyright (C) 2005-2010, 2012 Red Hat, Inc. + Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2005. @@ -324,14 +324,20 @@ process_file (const char *fname, bool more_than_one) if (segments == NULL) error (1, errno, gettext ("while reading ELF file")); - for (int i = 0; i < ehdr->e_phnum; ++i) + size_t phnum; + if (elf_getphdrnum (elf, &phnum) != 0) + error (1, 0, gettext ("cannot get program header count: %s"), + elf_errmsg (-1)); + + + for (size_t i = 0; i < phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); if (phdr == NULL) { error (0, 0, - gettext ("cannot get program header index at offset %d: %s"), + gettext ("cannot get program header index at offset %zd: %s"), i, elf_errmsg (-1)); result = 1; goto next; @@ -349,7 +355,7 @@ process_file (const char *fname, bool more_than_one) if (segments == NULL) { error (0, 0, gettext ("\ -cannot get program header index at offset %d: %s"), +cannot get program header index at offset %zd: %s"), i, elf_errmsg (-1)); result = 1; goto next; diff --git a/src/objdump.c b/src/objdump.c index ebad25d5..87290ccb 100644 --- a/src/objdump.c +++ b/src/objdump.c @@ -1,5 +1,5 @@ /* Print information from ELF file in human-readable form. - Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012 Red Hat, Inc. + Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2005. @@ -389,7 +389,7 @@ show_relocs_x (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *symdata, ? xndx : sym->st_shndx), &destshdr_mem); - if (shdr == NULL) + if (shdr == NULL || destshdr == NULL) printf ("<%s %ld>", gettext ("INVALID SECTION"), (long int) (sym->st_shndx == SHN_XINDEX @@ -418,7 +418,8 @@ show_relocs_rel (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, Elf_Data *symdata, Elf_Data *xndxdata, size_t symstrndx, size_t shstrndx) { - int nentries = shdr->sh_size / shdr->sh_entsize; + size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT); + int nentries = shdr->sh_size / sh_entsize; for (int cnt = 0; cnt < nentries; ++cnt) { @@ -438,7 +439,8 @@ show_relocs_rela (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, Elf_Data *symdata, Elf_Data *xndxdata, size_t symstrndx, size_t shstrndx) { - int nentries = shdr->sh_size / shdr->sh_entsize; + size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT); + int nentries = shdr->sh_size / sh_entsize; for (int cnt = 0; cnt < nentries; ++cnt) { @@ -460,13 +462,13 @@ section_match (Elf *elf, uint32_t scnndx, GElf_Shdr *shdr, size_t shstrndx) return true; struct section_list *runp = section_list; + const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); do { if (runp->is_name) { - if (strcmp (runp->name, - elf_strptr (elf, shstrndx, shdr->sh_name)) == 0) + if (name && strcmp (runp->name, name) == 0) return true; } else @@ -506,6 +508,8 @@ show_relocs (Ebl *ebl, const char *fname, uint32_t shstrndx) GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), &destshdr_mem); + if (unlikely (destshdr == NULL)) + continue; printf (gettext ("\nRELOCATION RECORDS FOR [%s]:\n" "%-*s TYPE VALUE\n"), @@ -522,6 +526,8 @@ show_relocs (Ebl *ebl, const char *fname, uint32_t shstrndx) GElf_Shdr symshdr_mem; GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); Elf_Data *symdata = elf_getdata (symscn, NULL); + if (unlikely (symshdr == NULL || symdata == NULL)) + continue; /* Search for the optional extended section index table. */ Elf_Data *xndxdata = NULL; diff --git a/src/readelf.c b/src/readelf.c index 0787e862..772cfcaa 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -1157,7 +1157,7 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) { - if (ehdr->e_phnum == 0) + if (phnum == 0) /* No program header, this is OK in relocatable objects. */ return; @@ -1894,12 +1894,15 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); else { - destshdr = gelf_getshdr (elf_getscn (ebl->elf, - sym->st_shndx == SHN_XINDEX - ? xndx : sym->st_shndx), - &destshdr_mem); - - if (unlikely (destshdr == NULL)) + /* This is a relocation against a STT_SECTION symbol. */ + GElf_Shdr secshdr_mem; + GElf_Shdr *secshdr; + secshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &secshdr_mem); + + if (unlikely (secshdr == NULL)) printf (" %#0*" PRIx64 " %-20s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1921,7 +1924,7 @@ handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) buf, sizeof (buf)) + 2 : gettext ("<INVALID RELOC>"), class == ELFCLASS32 ? 10 : 18, sym->st_value, - elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + elf_strptr (ebl->elf, shstrndx, secshdr->sh_name)); } } } @@ -2085,12 +2088,15 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name)); else { - destshdr = gelf_getshdr (elf_getscn (ebl->elf, - sym->st_shndx == SHN_XINDEX - ? xndx : sym->st_shndx), - &destshdr_mem); - - if (unlikely (shdr == NULL)) + /* This is a relocation against a STT_SECTION symbol. */ + GElf_Shdr secshdr_mem; + GElf_Shdr *secshdr; + secshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &secshdr_mem); + + if (unlikely (secshdr == NULL)) printf (" %#0*" PRIx64 " %-15s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -2114,7 +2120,7 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr) : gettext ("<INVALID RELOC>"), class == ELFCLASS32 ? 10 : 18, sym->st_value, rel->r_addend, - elf_strptr (ebl->elf, shstrndx, destshdr->sh_name)); + elf_strptr (ebl->elf, shstrndx, secshdr->sh_name)); } } } @@ -2500,10 +2506,16 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) get_ver_flags (aux->vna_flags), (unsigned short int) aux->vna_other); + if (aux->vna_next == 0) + break; + auxoffset += aux->vna_next; } /* Find the next offset. */ + if (need->vn_next == 0) + break; + offset += need->vn_next; } } @@ -2578,10 +2590,15 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) auxoffset, cnt2, elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name)); + if (aux->vda_next == 0) + break; + auxoffset += aux->vda_next; } /* Find the next offset. */ + if (def->vd_next == 0) + break; offset += def->vd_next; } } @@ -2660,6 +2677,8 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) nvername = MAX (nvername, (size_t) (def->vd_ndx & 0x7fff)); + if (def->vd_next == 0) + break; offset += def->vd_next; } } @@ -2704,9 +2723,13 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) nvername = MAX (nvername, (size_t) (aux->vna_other & 0x7fff)); + if (aux->vna_next == 0) + break; auxoffset += aux->vna_next; } + if (need->vn_next == 0) + break; offset += need->vn_next; } } @@ -2716,7 +2739,9 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Allocate the array. */ vername = (const char **) alloca (nvername * sizeof (const char *)); + memset(vername, 0, nvername * sizeof (const char *)); filename = (const char **) alloca (nvername * sizeof (const char *)); + memset(filename, 0, nvername * sizeof (const char *)); /* Run through the data structures again and collect the strings. */ if (defscn != NULL) @@ -2742,17 +2767,22 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the data at the next offset. */ GElf_Verdef defmem; GElf_Verdef *def = gelf_getverdef (defdata, offset, &defmem); + if (unlikely (def == NULL)) + break; + GElf_Verdaux auxmem; GElf_Verdaux *aux = gelf_getverdaux (defdata, offset + def->vd_aux, &auxmem); - if (unlikely (def == NULL || aux == NULL)) + if (unlikely (aux == NULL)) break; vername[def->vd_ndx & 0x7fff] = elf_strptr (ebl->elf, defshdr->sh_link, aux->vda_name); filename[def->vd_ndx & 0x7fff] = NULL; + if (def->vd_next == 0) + break; offset += def->vd_next; } } @@ -2790,9 +2820,13 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) filename[aux->vna_other & 0x7fff] = elf_strptr (ebl->elf, needshdr->sh_link, need->vn_file); + if (aux->vna_next == 0) + break; auxoffset += aux->vna_next; } + if (need->vn_next == 0) + break; offset += need->vn_next; } } @@ -2853,10 +2887,11 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) default: n = printf ("%4d%c%s", *sym & 0x7fff, *sym & 0x8000 ? 'h' : ' ', - (unsigned int) (*sym & 0x7fff) < nvername + (vername != NULL + && (unsigned int) (*sym & 0x7fff) < nvername) ? vername[*sym & 0x7fff] : "???"); if ((unsigned int) (*sym & 0x7fff) < nvername - && filename[*sym & 0x7fff] != NULL) + && filename != NULL && filename[*sym & 0x7fff] != NULL) n += printf ("(%s)", filename[*sym & 0x7fff]); printf ("%*s", MAX (0, 33 - (int) n), " "); break; @@ -2954,8 +2989,21 @@ handle_sysv_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) return; } + if (unlikely (data->d_size < 2 * sizeof (Elf32_Word))) + { + invalid_data: + error (0, 0, gettext ("invalid data in sysv.hash section %d"), + (int) elf_ndxscn (scn)); + return; + } + Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1]; + + uint64_t used_buf = (2ULL + nchain + nbucket) * sizeof (Elf32_Word); + if (used_buf > data->d_size) + goto invalid_data; + Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[2]; Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + nbucket]; @@ -2996,8 +3044,23 @@ handle_sysv_hash64 (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) return; } + if (unlikely (data->d_size < 2 * sizeof (Elf64_Xword))) + { + invalid_data: + error (0, 0, gettext ("invalid data in sysv.hash64 section %d"), + (int) elf_ndxscn (scn)); + return; + } + Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0]; Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1]; + + uint64_t maxwords = data->d_size / sizeof (Elf64_Xword); + if (maxwords < 2 + || maxwords - 2 < nbucket + || maxwords - 2 - nbucket < nchain) + goto invalid_data; + Elf64_Xword *bucket = &((Elf64_Xword *) data->d_buf)[2]; Elf64_Xword *chain = &((Elf64_Xword *) data->d_buf)[2 + nbucket]; @@ -3037,18 +3100,37 @@ handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) return; } + if (unlikely (data->d_size < 4 * sizeof (Elf32_Word))) + { + invalid_data: + error (0, 0, gettext ("invalid data in gnu.hash section %d"), + (int) elf_ndxscn (scn)); + return; + } + Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1]; /* Next comes the size of the bitmap. It's measured in words for the architecture. It's 32 bits for 32 bit archs, and 64 bits for - 64 bit archs. */ + 64 bit archs. There is always a bloom filter present, so zero is + an invalid value. */ Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2]; if (gelf_getclass (ebl->elf) == ELFCLASS64) bitmask_words *= 2; + if (bitmask_words == 0) + goto invalid_data; + Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3]; + /* Is there still room for the sym chain? + Use uint64_t calculation to prevent 32bit overlow. */ + uint64_t used_buf = (4ULL + bitmask_words + nbucket) * sizeof (Elf32_Word); + uint32_t max_nsyms = (data->d_size - used_buf) / sizeof (Elf32_Word); + if (used_buf > data->d_size) + goto invalid_data; + uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t)); Elf32_Word *bitmask = &((Elf32_Word *) data->d_buf)[4]; @@ -3068,6 +3150,8 @@ handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) ++nsyms; if (maxlength < ++lengths[cnt]) ++maxlength; + if (inner > max_nsyms) + goto invalid_data; } while ((chain[inner++] & 1) == 0); } @@ -3233,11 +3317,12 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) shdr->sh_size, shdr->sh_offset); Elf_Data *data = elf_rawdata (scn, NULL); - if (data == NULL) + if (unlikely (data == NULL || data->d_size == 0)) return; const unsigned char *p = data->d_buf; + /* There is only one 'version', A. */ if (unlikely (*p++ != 'A')) return; @@ -3248,8 +3333,10 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) return (const unsigned char *) data->d_buf + data->d_size - p; } + /* Loop over the sections. */ while (left () >= 4) { + /* Section length. */ uint32_t len; memcpy (&len, p, sizeof len); @@ -3259,25 +3346,29 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) if (unlikely (len > left ())) break; + /* Section vendor name. */ const unsigned char *name = p + sizeof len; p += len; unsigned const char *q = memchr (name, '\0', len); if (unlikely (q == NULL)) - continue; + break; ++q; printf (gettext (" %-13s %4" PRIu32 "\n"), name, len); + bool gnu_vendor = (q - name == sizeof "gnu" + && !memcmp (name, "gnu", sizeof "gnu")); + + /* Loop over subsections. */ if (shdr->sh_type != SHT_GNU_ATTRIBUTES - || (q - name == sizeof "gnu" - && !memcmp (name, "gnu", sizeof "gnu"))) + || gnu_vendor) while (q < p) { const unsigned char *const sub = q; unsigned int subsection_tag; - get_uleb128 (subsection_tag, q); + get_uleb128 (subsection_tag, q, p); if (unlikely (q >= p)) break; @@ -3290,7 +3381,10 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) CONVERT (subsection_len); - if (unlikely (p - sub < (ptrdiff_t) subsection_len)) + /* Don't overflow, ptrdiff_t might be 32bits, but signed. */ + if (unlikely (subsection_len == 0 + || subsection_len >= (uint32_t) PTRDIFF_MAX + || p - sub < (ptrdiff_t) subsection_len)) break; const unsigned char *r = q + sizeof subsection_len; @@ -3299,6 +3393,7 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) switch (subsection_tag) { default: + /* Unknown subsection, print and skip. */ printf (gettext (" %-4u %12" PRIu32 "\n"), subsection_tag, subsection_len); break; @@ -3310,20 +3405,34 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) while (r < q) { unsigned int tag; - get_uleb128 (tag, r); + get_uleb128 (tag, r, q); if (unlikely (r >= q)) break; + /* GNU style tags have either a uleb128 value, + when lowest bit is not set, or a string + when the lowest bit is set. + "compatibility" (32) is special. It has + both a string and a uleb128 value. For + non-gnu we assume 6 till 31 only take ints. + XXX see arm backend, do we need a separate + hook? */ uint64_t value = 0; const char *string = NULL; - if (tag == 32 || (tag & 1) == 0) + if (tag == 32 || (tag & 1) == 0 + || (! gnu_vendor && (tag > 5 && tag < 32))) { - get_uleb128 (value, r); + get_uleb128 (value, r, q); if (r > q) break; } - if (tag == 32 || (tag & 1) != 0) + if (tag == 32 + || ((tag & 1) != 0 + && (gnu_vendor + || (! gnu_vendor && tag > 32))) + || (! gnu_vendor && tag > 3 && tag < 6)) { + string = (const char *) r; r = memchr (r, '\0', q - r); if (r == NULL) break; @@ -3350,7 +3459,10 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr) } else { - assert (tag != 32); + /* For "gnu" vendor 32 "compatibility" has + already been handled above. */ + assert (tag != 32 + || strcmp ((const char *) name, "gnu")); if (string == NULL) printf (gettext (" %u: %" PRId64 "\n"), tag, value); @@ -3869,11 +3981,10 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, NEED (addrsize); if (addrsize == 4) addr = read_4ubyte_unaligned (dbg, data); + else if (addrsize == 8) + addr = read_8ubyte_unaligned (dbg, data); else - { - assert (addrsize == 8); - addr = read_8ubyte_unaligned (dbg, data); - } + goto invalid; data += addrsize; CONSUME (addrsize); @@ -3887,14 +3998,13 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_call_ref: /* Offset operand. */ + if (ref_size != 4 && ref_size != 8) + goto invalid; /* Cannot be used in CFA. */ NEED (ref_size); if (ref_size == 4) addr = read_4ubyte_unaligned (dbg, data); else - { - assert (ref_size == 8); - addr = read_8ubyte_unaligned (dbg, data); - } + addr = read_8ubyte_unaligned (dbg, data); data += ref_size; CONSUME (ref_size); @@ -4002,7 +4112,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, const unsigned char *start = data; uint64_t uleb; NEED (1); - get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb, data, data + len); printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", indent, "", (uintmax_t) offset, op_name, uleb); CONSUME (data - start); @@ -4012,9 +4122,10 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_bit_piece: start = data; uint64_t uleb2; - NEED (2); - get_uleb128 (uleb, data); /* XXX check overrun */ - get_uleb128 (uleb2, data); /* XXX check overrun */ + NEED (1); + get_uleb128 (uleb, data, data + len); + NEED (1); + get_uleb128 (uleb2, data, data + len); printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 ", %" PRIu64 "\n", indent, "", (uintmax_t) offset, op_name, uleb, uleb2); CONSUME (data - start); @@ -4027,7 +4138,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, start = data; int64_t sleb; NEED (1); - get_sleb128 (sleb, data); /* XXX check overrun */ + get_sleb128 (sleb, data, data + len); printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", indent, "", (uintmax_t) offset, op_name, sleb); CONSUME (data - start); @@ -4036,9 +4147,10 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_bregx: start = data; - NEED (2); - get_uleb128 (uleb, data); /* XXX check overrun */ - get_sleb128 (sleb, data); /* XXX check overrun */ + NEED (1); + get_uleb128 (uleb, data, data + len); + NEED (1); + get_sleb128 (sleb, data, data + len); printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %" PRId64 "\n", indent, "", (uintmax_t) offset, op_name, uleb, sleb); CONSUME (data - start); @@ -4077,7 +4189,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_implicit_value: start = data; NEED (1); - get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb, data, data + len); printf ("%*s[%4" PRIuMAX "] %s: ", indent, "", (uintmax_t) offset, op_name); NEED (uleb); @@ -4090,17 +4202,17 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, case DW_OP_GNU_implicit_pointer: /* DIE offset operand. */ start = data; - NEED (ref_size + 1); + NEED (ref_size); + if (ref_size != 4 && ref_size != 8) + goto invalid; /* Cannot be used in CFA. */ if (ref_size == 4) addr = read_4ubyte_unaligned (dbg, data); else - { - assert (ref_size == 8); - addr = read_8ubyte_unaligned (dbg, data); - } + addr = read_8ubyte_unaligned (dbg, data); data += ref_size; /* Byte offset operand. */ - get_sleb128 (sleb, data); /* XXX check overrun */ + NEED (1); + get_sleb128 (sleb, data, data + len); printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] %+" PRId64 "\n", indent, "", (intmax_t) offset, @@ -4113,7 +4225,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, /* Size plus expression block. */ start = data; NEED (1); - get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb, data, data + len); printf ("%*s[%4" PRIuMAX "] %s:\n", indent, "", (uintmax_t) offset, op_name); NEED (uleb); @@ -4128,10 +4240,11 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, /* uleb128 CU relative DW_TAG_base_type DIE offset, 1-byte unsigned size plus block. */ start = data; - NEED (2); - get_uleb128 (uleb, data); /* XXX check overrun */ + NEED (1); + get_uleb128 (uleb, data, data + len); if (! print_unresolved_addresses && cu != NULL) uleb += cu->start; + NEED (1); uint8_t usize = *(uint8_t *) data++; NEED (usize); printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] ", @@ -4146,9 +4259,10 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, /* uleb128 register number, uleb128 CU relative DW_TAG_base_type DIE offset. */ start = data; - NEED (2); - get_uleb128 (uleb, data); /* XXX check overrun */ - get_uleb128 (uleb2, data); /* XXX check overrun */ + NEED (1); + get_uleb128 (uleb, data, data + len); + NEED (1); + get_uleb128 (uleb2, data, data + len); if (! print_unresolved_addresses && cu != NULL) uleb2 += cu->start; printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " [%6" PRIx64 "]\n", @@ -4161,9 +4275,10 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, /* 1-byte unsigned size of value, uleb128 CU relative DW_TAG_base_type DIE offset. */ start = data; - NEED (2); + NEED (1); usize = *(uint8_t *) data++; - get_uleb128 (uleb, data); /* XXX check overrun */ + NEED (1); + get_uleb128 (uleb, data, data + len); if (! print_unresolved_addresses && cu != NULL) uleb += cu->start; printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 " [%6" PRIxMAX "]\n", @@ -4179,7 +4294,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest, for conversion to untyped. */ start = data; NEED (1); - get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb, data, data + len); if (uleb != 0 && ! print_unresolved_addresses && cu != NULL) uleb += cu->start; printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "]\n", @@ -4313,7 +4428,8 @@ reset_listptr (struct listptr_table *table) table->n = table->alloc = 0; } -static void +/* Returns false if offset doesn't fit. See struct listptr. */ +static bool notice_listptr (enum section_e section, struct listptr_table *table, uint_fast8_t address_size, uint_fast8_t offset_size, struct Dwarf_CU *cu, Dwarf_Off offset) @@ -4339,8 +4455,14 @@ notice_listptr (enum section_e section, struct listptr_table *table, .offset = offset, .cu = cu }; - assert (p->offset == offset); + + if (p->offset != offset) + { + table->n--; + return false; + } } + return true; } static void @@ -4594,7 +4716,7 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), printf (gettext ("\n Length: %6" PRIu64 "\n"), (uint64_t) length); - if (nexthdr > readendp) + if (unlikely (length > (size_t) (readendp - readp))) goto invalid_data; if (length == 0) @@ -4854,54 +4976,70 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp, puts (" nop"); break; case DW_CFA_set_loc: - // XXX overflow check - get_uleb128 (op1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); op1 += vma_base; printf (" set_loc %" PRIu64 "\n", op1 * code_align); break; case DW_CFA_advance_loc1: + if ((uint64_t) (endp - readp) < 1) + goto invalid; printf (" advance_loc1 %u to %#" PRIx64 "\n", *readp, pc += *readp * code_align); ++readp; break; case DW_CFA_advance_loc2: + if ((uint64_t) (endp - readp) < 2) + goto invalid; op1 = read_2ubyte_unaligned_inc (dbg, readp); printf (" advance_loc2 %" PRIu64 " to %#" PRIx64 "\n", op1, pc += op1 * code_align); break; case DW_CFA_advance_loc4: + if ((uint64_t) (endp - readp) < 4) + goto invalid; op1 = read_4ubyte_unaligned_inc (dbg, readp); printf (" advance_loc4 %" PRIu64 " to %#" PRIx64 "\n", op1, pc += op1 * code_align); break; case DW_CFA_offset_extended: - // XXX overflow check - get_uleb128 (op1, readp); - get_uleb128 (op2, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op2, readp, endp); printf (" offset_extended r%" PRIu64 " (%s) at cfa%+" PRId64 "\n", op1, regname (op1), op2 * data_align); break; case DW_CFA_restore_extended: - // XXX overflow check - get_uleb128 (op1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); printf (" restore_extended r%" PRIu64 " (%s)\n", op1, regname (op1)); break; case DW_CFA_undefined: - // XXX overflow check - get_uleb128 (op1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); printf (" undefined r%" PRIu64 " (%s)\n", op1, regname (op1)); break; case DW_CFA_same_value: - // XXX overflow check - get_uleb128 (op1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); printf (" same_value r%" PRIu64 " (%s)\n", op1, regname (op1)); break; case DW_CFA_register: - // XXX overflow check - get_uleb128 (op1, readp); - get_uleb128 (op2, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op2, readp, endp); printf (" register r%" PRIu64 " (%s) in r%" PRIu64 " (%s)\n", op1, regname (op1), op2, regname (op2)); break; @@ -4912,86 +5050,123 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp, puts (" restore_state"); break; case DW_CFA_def_cfa: - // XXX overflow check - get_uleb128 (op1, readp); - get_uleb128 (op2, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op2, readp, endp); printf (" def_cfa r%" PRIu64 " (%s) at offset %" PRIu64 "\n", op1, regname (op1), op2); break; case DW_CFA_def_cfa_register: - // XXX overflow check - get_uleb128 (op1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); printf (" def_cfa_register r%" PRIu64 " (%s)\n", op1, regname (op1)); break; case DW_CFA_def_cfa_offset: - // XXX overflow check - get_uleb128 (op1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); printf (" def_cfa_offset %" PRIu64 "\n", op1); break; case DW_CFA_def_cfa_expression: - // XXX overflow check - get_uleb128 (op1, readp); /* Length of DW_FORM_block. */ + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); /* Length of DW_FORM_block. */ printf (" def_cfa_expression %" PRIu64 "\n", op1); + if ((uint64_t) (endp - readp) < op1) + { + invalid: + fputs (gettext (" <INVALID DATA>\n"), stdout); + return; + } print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL, op1, readp); readp += op1; break; case DW_CFA_expression: - // XXX overflow check - get_uleb128 (op1, readp); - get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op2, readp, endp); /* Length of DW_FORM_block. */ printf (" expression r%" PRIu64 " (%s) \n", op1, regname (op1)); + if ((uint64_t) (endp - readp) < op2) + goto invalid; print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL, op2, readp); readp += op2; break; case DW_CFA_offset_extended_sf: - // XXX overflow check - get_uleb128 (op1, readp); - get_sleb128 (sop2, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_sleb128 (sop2, readp, endp); printf (" offset_extended_sf r%" PRIu64 " (%s) at cfa%+" PRId64 "\n", op1, regname (op1), sop2 * data_align); break; case DW_CFA_def_cfa_sf: - // XXX overflow check - get_uleb128 (op1, readp); - get_sleb128 (sop2, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_sleb128 (sop2, readp, endp); printf (" def_cfa_sf r%" PRIu64 " (%s) at offset %" PRId64 "\n", op1, regname (op1), sop2 * data_align); break; case DW_CFA_def_cfa_offset_sf: - // XXX overflow check - get_sleb128 (sop1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_sleb128 (sop1, readp, endp); printf (" def_cfa_offset_sf %" PRId64 "\n", sop1 * data_align); break; case DW_CFA_val_offset: - // XXX overflow check - get_uleb128 (op1, readp); - get_uleb128 (op2, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op2, readp, endp); printf (" val_offset %" PRIu64 " at offset %" PRIu64 "\n", op1, op2 * data_align); break; case DW_CFA_val_offset_sf: - // XXX overflow check - get_uleb128 (op1, readp); - get_sleb128 (sop2, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_sleb128 (sop2, readp, endp); printf (" val_offset_sf %" PRIu64 " at offset %" PRId64 "\n", op1, sop2 * data_align); break; case DW_CFA_val_expression: - // XXX overflow check - get_uleb128 (op1, readp); - get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op2, readp, endp); /* Length of DW_FORM_block. */ printf (" val_expression r%" PRIu64 " (%s)\n", op1, regname (op1)); + if ((uint64_t) (endp - readp) < op2) + goto invalid; print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL, op2, readp); readp += op2; break; case DW_CFA_MIPS_advance_loc8: + if ((uint64_t) (endp - readp) < 8) + goto invalid; op1 = read_8ubyte_unaligned_inc (dbg, readp); printf (" MIPS_advance_loc8 %" PRIu64 " to %#" PRIx64 "\n", op1, pc += op1 * code_align); @@ -5000,8 +5175,9 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp, puts (" GNU_window_save"); break; case DW_CFA_GNU_args_size: - // XXX overflow check - get_uleb128 (op1, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (op1, readp, endp); printf (" args_size %" PRIu64 "\n", op1); break; default: @@ -5014,8 +5190,9 @@ print_cfa_program (const unsigned char *readp, const unsigned char *const endp, else if (opcode < DW_CFA_restore) { uint64_t offset; - // XXX overflow check - get_uleb128 (offset, readp); + if ((uint64_t) (endp - readp) < 1) + goto invalid; + get_uleb128 (offset, readp, endp); printf (" offset r%u (%s) at cfa%+" PRId64 "\n", opcode & 0x3f, regname (opcode & 0x3f), offset * data_align); } @@ -5031,15 +5208,17 @@ encoded_ptr_size (int encoding, unsigned int ptr_size) { switch (encoding & 7) { - case 2: - return 2; - case 3: + case DW_EH_PE_udata4: return 4; - case 4: + case DW_EH_PE_udata8: return 8; - default: + case 0: return ptr_size; } + + fprintf (stderr, "Unsupported pointer encoding: %#x, " + "assuming pointer size of %d.\n", encoding, ptr_size); + return ptr_size; } @@ -5152,12 +5331,10 @@ read_encoded (unsigned int encoding, const unsigned char *readp, switch (encoding & 0xf) { case DW_EH_PE_uleb128: - // XXX buffer overrun check - get_uleb128 (*res, readp); + get_uleb128 (*res, readp, endp); break; case DW_EH_PE_sleb128: - // XXX buffer overrun check - get_sleb128 (*res, readp); + get_sleb128 (*res, readp, endp); break; case DW_EH_PE_udata2: if (readp + 2 > endp) @@ -5283,6 +5460,10 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, continue; } + Dwarf_Word maxsize = dataend - readp; + if (unlikely (unit_length > maxsize)) + goto invalid_data; + unsigned int ptr_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; ptrdiff_t start = readp - (unsigned char *) data->d_buf; @@ -5326,21 +5507,24 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, segment_size = *readp++; } - // XXX Check overflow - get_uleb128 (code_alignment_factor, readp); - // XXX Check overflow - get_sleb128 (data_alignment_factor, readp); + if (cieend - readp < 1) + goto invalid_data; + get_uleb128 (code_alignment_factor, readp, cieend); + if (cieend - readp < 1) + goto invalid_data; + get_sleb128 (data_alignment_factor, readp, cieend); /* In some variant for unwind data there is another field. */ if (strcmp (augmentation, "eh") == 0) readp += ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; unsigned int return_address_register; + if (cieend - readp < 1) + goto invalid_data; if (unlikely (version == 1)) return_address_register = *readp++; else - // XXX Check overflow - get_uleb128 (return_address_register, readp); + get_uleb128 (return_address_register, readp, cieend); printf ("\n [%6tx] CIE length=%" PRIu64 "\n" " CIE_id: %" PRIu64 "\n" @@ -5361,14 +5545,18 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, if (augmentation[0] == 'z') { unsigned int augmentationlen; - get_uleb128 (augmentationlen, readp); + get_uleb128 (augmentationlen, readp, cieend); - if (augmentationlen > (size_t) (dataend - readp)) - error (1, 0, gettext ("invalid augmentation length")); + if (augmentationlen > (size_t) (cieend - readp)) + { + error (0, 0, gettext ("invalid augmentation length")); + readp = cieend; + continue; + } const char *hdr = "Augmentation data:"; const char *cp = augmentation + 1; - while (*cp != '\0') + while (*cp != '\0' && cp < augmentation + augmentationlen + 1) { printf (" %-26s%#x ", hdr, *readp); hdr = ""; @@ -5460,9 +5648,9 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, const unsigned char *base = readp; // XXX There are sometimes relocations for this value - initial_location = read_ubyte_unaligned_inc (ptr_size, dbg, readp); + initial_location = read_addr_unaligned_inc (ptr_size, dbg, readp); Dwarf_Word address_range - = read_ubyte_unaligned_inc (ptr_size, dbg, readp); + = read_addr_unaligned_inc (ptr_size, dbg, readp); /* pcrel for an FDE address is relative to the runtime address of the start_address field itself. Sign extend @@ -5510,14 +5698,24 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, if (cie->augmentation[0] == 'z') { unsigned int augmentationlen; - get_uleb128 (augmentationlen, readp); + if (cieend - readp < 1) + goto invalid_data; + get_uleb128 (augmentationlen, readp, cieend); + + if (augmentationlen > (size_t) (cieend - readp)) + { + error (0, 0, gettext ("invalid augmentation length")); + readp = cieend; + continue; + } if (augmentationlen > 0) { const char *hdr = "Augmentation data:"; const char *cp = cie->augmentation + 1; unsigned int u = 0; - while (*cp != '\0') + while (*cp != '\0' + && cp < cie->augmentation + augmentationlen + 1) { if (*cp == 'L') { @@ -5547,9 +5745,12 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, } /* Handle the initialization instructions. */ - print_cfa_program (readp, cieend, vma_base, code_alignment_factor, - data_alignment_factor, version, ptr_size, - dwflmod, ebl, dbg); + if (ptr_size != 4 && ptr_size !=8) + printf ("invalid CIE pointer size (%u), must be 4 or 8.\n", ptr_size); + else + print_cfa_program (readp, cieend, vma_base, code_alignment_factor, + data_alignment_factor, version, ptr_size, + dwflmod, ebl, dbg); readp = cieend; } } @@ -5698,23 +5899,29 @@ attr_callback (Dwarf_Attribute *attrp, void *arg) case DW_AT_GNU_call_site_data_value: case DW_AT_GNU_call_site_target: case DW_AT_GNU_call_site_target_clobbered: - notice_listptr (section_loc, &known_loclistptr, - cbargs->addrsize, cbargs->offset_size, - cbargs->cu, num); - if (!cbargs->silent) - printf (" %*s%-20s (%s) location list [%6" PRIxMAX "]\n", - (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), (uintmax_t) num); + { + bool nlpt = notice_listptr (section_loc, &known_loclistptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num); + if (!cbargs->silent) + printf (" %*s%-20s (%s) location list [%6" PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + nlpt ? "" : " <WARNING offset too big>"); + } return DWARF_CB_OK; case DW_AT_ranges: - notice_listptr (section_ranges, &known_rangelistptr, - cbargs->addrsize, cbargs->offset_size, - cbargs->cu, num); - if (!cbargs->silent) - printf (" %*s%-20s (%s) range list [%6" PRIxMAX "]\n", - (int) (level * 2), "", dwarf_attr_name (attr), - dwarf_form_name (form), (uintmax_t) num); + { + bool nlpt = notice_listptr (section_ranges, &known_rangelistptr, + cbargs->addrsize, cbargs->offset_size, + cbargs->cu, num); + if (!cbargs->silent) + printf (" %*s%-20s (%s) range list [%6" PRIxMAX "]%s\n", + (int) (level * 2), "", dwarf_attr_name (attr), + dwarf_form_name (form), (uintmax_t) num, + nlpt ? "" : " <WARNING offset too big>"); + } return DWARF_CB_OK; case DW_AT_language: @@ -6092,9 +6299,19 @@ print_decoded_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, for (size_t n = 0; n < nlines; n++) { Dwarf_Line *line = dwarf_onesrcline (lines, n); + if (line == NULL) + { + printf (" dwarf_onesrcline: %s\n", dwarf_errmsg (-1)); + continue; + } Dwarf_Word mtime, length; const char *file = dwarf_linesrc (line, &mtime, &length); - if (strcmp (last_file, file) != 0) + if (file == NULL) + { + printf (" <%s> (mtime: ?, length: ?)\n", dwarf_errmsg (-1)); + last_file = ""; + } + else if (strcmp (last_file, file) != 0) { printf (" %s (mtime: %" PRIu64 ", length: %" PRIu64 ")\n", file, mtime, length); @@ -6175,6 +6392,8 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, printf (gettext ("\nTable at offset %Zu:\n"), start_offset); + if (unlikely (linep + 4 > lineendp)) + goto invalid_data; Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); unsigned int length = 4; if (unlikely (unit_length == 0xffffffff)) @@ -6191,8 +6410,8 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, } /* Check whether we have enough room in the section. */ - if (unit_length < 2 + length + 5 * 1 - || unlikely (linep + unit_length > lineendp)) + if (unlikely (unit_length > (size_t) (lineendp - linep) + || unit_length < 2 + length + 5 * 1)) goto invalid_data; lineendp = linep + unit_length; @@ -6301,15 +6520,21 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, /* Then the index. */ unsigned int diridx; - get_uleb128 (diridx, linep); + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (diridx, linep, lineendp); /* Next comes the modification time. */ unsigned int mtime; - get_uleb128 (mtime, linep); + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (mtime, linep, lineendp); /* Finally the length of the file. */ unsigned int fsize; - get_uleb128 (fsize, linep); + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (fsize, linep, lineendp); printf (" %-5u %-5u %-9u %-9u %s\n", cnt, diridx, mtime, fsize, fname); @@ -6365,6 +6590,14 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, op_index = (op_index + op_advance) % max_ops_per_instr; } + if (max_ops_per_instr == 0) + { + error (0, 0, + gettext ("invalid maximum operations per instruction is zero")); + linep = lineendp; + continue; + } + while (linep < lineendp) { size_t offset = linep - (const unsigned char *) data->d_buf; @@ -6378,6 +6611,9 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, /* Is this a special opcode? */ if (likely (opcode >= opcode_base)) { + if (unlikely (line_range == 0)) + goto invalid_unit; + /* Yes. Handling this is quite easy since the opcode value is computed with @@ -6434,6 +6670,8 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, case DW_LNE_set_address: op_index = 0; + if (unlikely ((size_t) (lineendp - linep) < address_size)) + goto invalid_unit; if (address_size == 4) address = read_4ubyte_unaligned_inc (dbg, linep); else @@ -6455,11 +6693,17 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, linep = endp + 1; unsigned int diridx; - get_uleb128 (diridx, linep); + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (diridx, linep, lineendp); Dwarf_Word mtime; - get_uleb128 (mtime, linep); + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (mtime, linep, lineendp); Dwarf_Word filelength; - get_uleb128 (filelength, linep); + if (lineendp - linep < 1) + goto invalid_unit; + get_uleb128 (filelength, linep, lineendp); printf (gettext ("\ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"), @@ -6473,7 +6717,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, if (unlikely (standard_opcode_lengths[opcode] != 1)) goto invalid_unit; - get_uleb128 (u128, linep); + get_uleb128 (u128, linep, lineendp); printf (gettext (" set discriminator to %u\n"), u128); break; @@ -6497,7 +6741,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, case DW_LNS_advance_pc: /* Takes one uleb128 parameter which is added to the address. */ - get_uleb128 (u128, linep); + get_uleb128 (u128, linep, lineendp); advance_pc (u128); { char *a = format_dwarf_addr (dwflmod, 0, address, address); @@ -6515,7 +6759,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, case DW_LNS_advance_line: /* Takes one sleb128 parameter which is added to the line. */ - get_sleb128 (s128, linep); + get_sleb128 (s128, linep, lineendp); line += s128; printf (gettext ("\ advance line by constant %d to %" PRId64 "\n"), @@ -6524,7 +6768,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, case DW_LNS_set_file: /* Takes one uleb128 parameter which is stored in file. */ - get_uleb128 (u128, linep); + get_uleb128 (u128, linep, lineendp); printf (gettext (" set file to %" PRIu64 "\n"), (uint64_t) u128); break; @@ -6534,7 +6778,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, if (unlikely (standard_opcode_lengths[opcode] != 1)) goto invalid_unit; - get_uleb128 (u128, linep); + get_uleb128 (u128, linep, lineendp); printf (gettext (" set column to %" PRIu64 "\n"), (uint64_t) u128); break; @@ -6553,6 +6797,10 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, case DW_LNS_const_add_pc: /* Takes no argument. */ + + if (unlikely (line_range == 0)) + goto invalid_unit; + advance_pc ((255 - opcode_base) / line_range); { char *a = format_dwarf_addr (dwflmod, 0, address, address); @@ -6601,7 +6849,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, if (unlikely (standard_opcode_lengths[opcode] != 1)) goto invalid_unit; - get_uleb128 (u128, linep); + get_uleb128 (u128, linep, lineendp); printf (gettext (" set isa to %u\n"), u128); break; } @@ -6617,7 +6865,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, standard_opcode_lengths[opcode]); for (int n = standard_opcode_lengths[opcode]; n > 0; --n) { - get_uleb128 (u128, linep); + get_uleb128 (u128, linep, lineendp); if (n != standard_opcode_lengths[opcode]) putc_unlocked (',', stdout); printf (" %u", u128); @@ -6853,7 +7101,7 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), For the latter number, string. We can treat these cases together. */ - get_uleb128 (u128, readp); + get_uleb128 (u128, readp, readendp); endp = memchr (readp, '\0', readendp - readp); if (unlikely (endp == NULL)) @@ -6878,8 +7126,15 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), case DW_MACINFO_start_file: /* The two parameters are line and file index, in this order. */ - get_uleb128 (u128, readp); - get_uleb128 (u128_2, readp); + get_uleb128 (u128, readp, readendp); + if (readendp - readp < 1) + { + printf (gettext ("\ +%*s*** missing DW_MACINFO_start_file argument at end of section"), + level, ""); + return; + } + get_uleb128 (u128_2, readp, readendp); /* Find the CU DIE for this file. */ size_t macoff = readp - (const unsigned char *) data->d_buf; @@ -7090,8 +7345,10 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), switch (opcode) { case DW_MACRO_GNU_start_file: - get_uleb128 (u128, readp); - get_uleb128 (u128_2, readp); + get_uleb128 (u128, readp, readendp); + if (readp >= readendp) + goto invalid_data; + get_uleb128 (u128_2, readp, readendp); /* Find the CU DIE that matches this line offset. */ const char *fname = "???"; @@ -7123,7 +7380,7 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), break; case DW_MACRO_GNU_define: - get_uleb128 (u128, readp); + get_uleb128 (u128, readp, readendp); endp = memchr (readp, '\0', readendp - readp); if (endp == NULL) goto invalid_data; @@ -7133,7 +7390,7 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), break; case DW_MACRO_GNU_undef: - get_uleb128 (u128, readp); + get_uleb128 (u128, readp, readendp); endp = memchr (readp, '\0', readendp - readp); if (endp == NULL) goto invalid_data; @@ -7143,7 +7400,7 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), break; case DW_MACRO_GNU_define_indirect: - get_uleb128 (u128, readp); + get_uleb128 (u128, readp, readendp); if (readp + offset_len > readendp) goto invalid_data; if (offset_len == 8) @@ -7155,7 +7412,7 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), break; case DW_MACRO_GNU_undef_indirect: - get_uleb128 (u128, readp); + get_uleb128 (u128, readp, readendp); if (readp + offset_len > readendp) goto invalid_data; if (offset_len == 8) @@ -7225,17 +7482,17 @@ print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)), break; case DW_FORM_sdata: - get_sleb128 (val, readp); + get_sleb128 (val, readp, readendp); printf (" %" PRIx64, val); break; case DW_FORM_udata: - get_uleb128 (val, readp); + get_uleb128 (val, readp, readendp); printf (" %" PRIx64, val); break; case DW_FORM_block: - get_uleb128 (val, readp); + get_uleb128 (val, readp, readendp); printf (" block[%" PRIu64 "]", val); if (readp + val > readendp) goto invalid_data; @@ -7551,9 +7808,10 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), if (ttype_encoding != DW_EH_PE_omit) { unsigned int ttype_base_offset; - get_uleb128 (ttype_base_offset, readp); + get_uleb128 (ttype_base_offset, readp, dataend); printf (" TType base offset: %#x\n", ttype_base_offset); - ttype_base = readp + ttype_base_offset; + if ((size_t) (dataend - readp) > ttype_base_offset) + ttype_base = readp + ttype_base_offset; } if (unlikely (readp + 1 > dataend)) @@ -7562,7 +7820,7 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), printf (gettext (" Call site encoding: %#x "), call_site_encoding); print_encoding_base ("", call_site_encoding); unsigned int call_site_table_len; - get_uleb128 (call_site_table_len, readp); + get_uleb128 (call_site_table_len, readp, dataend); const unsigned char *const action_table = readp + call_site_table_len; if (unlikely (action_table > dataend)) @@ -7584,7 +7842,7 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), readp = read_encoded (call_site_encoding, readp, dataend, &landing_pad, dbg); unsigned int action; - get_uleb128 (action, readp); + get_uleb128 (action, readp, dataend); max_action = MAX (action, max_action); printf (gettext (" [%4u] Call site start: %#" PRIx64 "\n" " Call site length: %" PRIu64 "\n" @@ -7592,13 +7850,20 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), " Action: %u\n"), u++, call_site_start, call_site_length, landing_pad, action); } - assert (readp == action_table); + if (readp != action_table) + goto invalid_data; unsigned int max_ar_filter = 0; if (max_action > 0) { puts ("\n Action table:"); + if ((size_t) (dataend - action_table) < max_action + 1) + { + fputs (gettext (" <INVALID DATA>\n"), stdout); + return; + } + const unsigned char *const action_table_end = action_table + max_action + 1; @@ -7606,11 +7871,11 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), do { int ar_filter; - get_sleb128 (ar_filter, readp); + get_sleb128 (ar_filter, readp, action_table_end); if (ar_filter > 0 && (unsigned int) ar_filter > max_ar_filter) max_ar_filter = ar_filter; int ar_disp; - get_sleb128 (ar_disp, readp); + get_sleb128 (ar_disp, readp, action_table_end); printf (" [%4u] ar_filter: % d\n" " ar_disp: % -5d", @@ -7626,7 +7891,7 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)), while (readp < action_table_end); } - if (max_ar_filter > 0) + if (max_ar_filter > 0 && ttype_base != NULL) { puts ("\n TType table:"); @@ -7842,21 +8107,23 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr, if (name != 0 || vector != 0) { const unsigned char *sym = data->d_buf + const_off + name; - if (unlikely (sym > dataend)) + if (unlikely (sym > dataend + || memchr (sym, '\0', dataend - sym) == NULL)) goto invalid_data; printf (" [%4zu] symbol: %s, CUs: ", n, sym); const unsigned char *readcus = data->d_buf + const_off + vector; - if (unlikely (readcus + 8 > dataend)) + if (unlikely (readcus + 4 > dataend)) goto invalid_data; - uint32_t cus = read_4ubyte_unaligned (dbg, readcus); while (cus--) { uint32_t cu_kind, cu, kind; bool is_static; readcus += 4; + if (unlikely (readcus + 4 > dataend)) + goto invalid_data; cu_kind = read_4ubyte_unaligned (dbg, readcus); cu = cu_kind & ((1 << 24) - 1); kind = (cu_kind >> 28) & 7; @@ -8860,9 +9127,13 @@ handle_file_note (Elf *core, GElf_Word descsz, GElf_Off desc_pos) return; } + size_t addrsize = gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT); + uint64_t maxcount = (size_t) (end - ptr) / (3 * addrsize); + if (count > maxcount) + goto fail; + /* Where file names are stored. */ - unsigned char const *const fstart - = ptr + 3 * count * gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT); + unsigned char const *const fstart = ptr + 3 * count * addrsize; char const *fptr = (char *) fstart; printf (" %" PRId64 " files:\n", count); @@ -427,10 +427,9 @@ show_sysv (Elf *elf, const char *prefix, const char *fname, INTERNAL_ERROR (fullname); /* Ignore all sections which are not used at runtime. */ - if ((shdr->sh_flags & SHF_ALLOC) != 0) - maxlen = MAX (maxlen, - (int) strlen (elf_strptr (elf, shstrndx, - shdr->sh_name))); + const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); + if (name != NULL && (shdr->sh_flags & SHF_ALLOC) != 0) + maxlen = MAX (maxlen, (int) strlen (name)); } fputs_unlocked (fname, stdout); @@ -601,14 +600,13 @@ show_bsd_totals (void) static void show_segments (Elf *elf, const char *fullname) { - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); - if (ehdr == NULL) + size_t phnum; + if (elf_getphdrnum (elf, &phnum) != 0) INTERNAL_ERROR (fullname); GElf_Off total = 0; bool first = true; - for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt) + for (size_t cnt = 0; cnt < phnum; ++cnt) { GElf_Phdr phdr_mem; GElf_Phdr *phdr; diff --git a/src/strip.c b/src/strip.c index ebe18a92..1b34eeea 100644 --- a/src/strip.c +++ b/src/strip.c @@ -518,6 +518,11 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); + /* Get the number of phdrs in the old file. */ + size_t phnum; + if (elf_getphdrnum (elf, &phnum) != 0) + error (EXIT_FAILURE, 0, gettext ("cannot get number of phdrs")); + /* We now create a new ELF descriptor for the same file. We construct it almost exactly in the same way with some information dropped. */ @@ -529,7 +534,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (unlikely (gelf_newehdr (newelf, gelf_getclass (elf)) == 0) || (ehdr->e_type != ET_REL - && unlikely (gelf_newphdr (newelf, ehdr->e_phnum) == 0))) + && unlikely (gelf_newphdr (newelf, phnum) == 0))) { error (0, 0, gettext ("cannot create new file '%s': %s"), output_fname, elf_errmsg (-1)); @@ -538,7 +543,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Copy over the old program header if needed. */ if (ehdr->e_type != ET_REL) - for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + for (cnt = 0; cnt < phnum; ++cnt) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); @@ -553,7 +558,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, debugelf = elf_begin (debug_fd, ELF_C_WRITE_MMAP, NULL); if (unlikely (gelf_newehdr (debugelf, gelf_getclass (elf)) == 0) || (ehdr->e_type != ET_REL - && unlikely (gelf_newphdr (debugelf, ehdr->e_phnum) == 0))) + && unlikely (gelf_newphdr (debugelf, phnum) == 0))) { error (0, 0, gettext ("cannot create new file '%s': %s"), debug_fname, elf_errmsg (-1)); @@ -562,7 +567,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Copy over the old program header if needed. */ if (ehdr->e_type != ET_REL) - for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) + for (cnt = 0; cnt < phnum; ++cnt) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); diff --git a/src/unstrip.c b/src/unstrip.c index f6af4500..989ac5ff 100644 --- a/src/unstrip.c +++ b/src/unstrip.c @@ -241,7 +241,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ #define ELF_CHECK(call, msg) \ do \ { \ - if (!(call)) \ + if (unlikely (!(call))) \ error (EXIT_FAILURE, 0, msg, elf_errmsg (-1)); \ } while (0) @@ -257,13 +257,17 @@ copy_elf (Elf *outelf, Elf *inelf) ELF_CHECK (gelf_update_ehdr (outelf, ehdr), _("cannot copy ELF header: %s")); - if (ehdr->e_phnum > 0) + size_t phnum; + ELF_CHECK (elf_getphdrnum (inelf, &phnum) == 0, + _("cannot get number of program headers: %s")); + + if (phnum > 0) { - ELF_CHECK (gelf_newphdr (outelf, ehdr->e_phnum), + ELF_CHECK (gelf_newphdr (outelf, phnum), _("cannot create program headers: %s")); GElf_Phdr phdr_mem; - for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + for (size_t i = 0; i < phnum; ++i) ELF_CHECK (gelf_update_phdr (outelf, i, gelf_getphdr (inelf, i, &phdr_mem)), _("cannot copy program header: %s")); @@ -1823,12 +1827,16 @@ more sections in stripped file than debug file -- arguments reversed?")); } while (skip_reloc); - if (stripped_ehdr->e_phnum > 0) - ELF_CHECK (gelf_newphdr (unstripped, stripped_ehdr->e_phnum), + size_t phnum; + ELF_CHECK (elf_getphdrnum (stripped, &phnum) == 0, + _("cannot get number of program headers: %s")); + + if (phnum > 0) + ELF_CHECK (gelf_newphdr (unstripped, phnum), _("cannot create program headers: %s")); /* Copy each program header from the stripped file. */ - for (uint_fast16_t i = 0; i < stripped_ehdr->e_phnum; ++i) + for (size_t i = 0; i < phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem); @@ -1863,11 +1871,15 @@ handle_file (const char *output_file, bool create_dirs, Elf *stripped, const GElf_Ehdr *stripped_ehdr, Elf *unstripped) { + size_t phnum; + ELF_CHECK (elf_getphdrnum (stripped, &phnum) == 0, + _("cannot get number of program headers: %s")); + /* Determine the address bias between the debuginfo file and the main file, which may have been modified by prelinking. */ GElf_Addr bias = 0; if (unstripped != NULL) - for (uint_fast16_t i = 0; i < stripped_ehdr->e_phnum; ++i) + for (size_t i = 0; i < phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem); |
