From ae6c7e33e1510665e8e043eb11a71e59414efbf3 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Fri, 12 Sep 2014 09:35:42 +0930 Subject: [PATCH] Test for overflow in eh_frame_hdr entries and for overlapping FDEs With larger binaries on 64-bit systems, or indeed just binaries that have a large gap between text and data, it is possible for the .eh_frame_hdr lookup table entry values to overflow a signed 32-bit relative offset. It is also a requirement for the glibc FDE lookup code that only one FDE claim to cover any given address. * elf-bfd.h (struct eh_frame_array_ent): Add "range". * elf-eh-frame.c (_bfd_elf_write_section_eh_frame): Stash address range of FDEs to hdr_info->array. (_bfd_elf_write_section_eh_frame_hdr): Report overflow in .eh_frame_hdr entries, and overlapping FDEs. --- bfd/ChangeLog | 8 ++++++++ bfd/elf-bfd.h | 1 + bfd/elf-eh-frame.c | 51 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 882d994..0bdebfc 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,11 @@ +2014-09-12 Alan Modra + + * elf-bfd.h (struct eh_frame_array_ent): Add "range". + * elf-eh-frame.c (_bfd_elf_write_section_eh_frame): Stash address + range of FDEs to hdr_info->array. + (_bfd_elf_write_section_eh_frame_hdr): Report overflow in + .eh_frame_hdr entries, and overlapping FDEs. + 2014-09-10 Alan Modra * elf.c (assign_file_positions_except_relocs): Move section header diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 2f24274..dc343ec 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -372,6 +372,7 @@ struct eh_frame_sec_info struct eh_frame_array_ent { bfd_vma initial_loc; + bfd_size_type range; bfd_vma fde; }; diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c index d56440c..02f2d23 100644 --- a/bfd/elf-eh-frame.c +++ b/bfd/elf-eh-frame.c @@ -1632,6 +1632,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, if (sizeof (address) > 4 && ptr_size == 4) address &= 0xffffffff; hdr_info->array[hdr_info->array_count].initial_loc = address; + hdr_info->array[hdr_info->array_count].range + = read_value (abfd, buf + width, width, FALSE); hdr_info->array[hdr_info->array_count++].fde = (sec->output_section->vma + sec->output_offset @@ -1805,26 +1807,55 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info) if (contents[2] != DW_EH_PE_omit) { unsigned int i; + bfd_boolean overlap, overflow; bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE); qsort (hdr_info->array, hdr_info->fde_count, sizeof (*hdr_info->array), vma_compare); + overlap = FALSE; + overflow = FALSE; for (i = 0; i < hdr_info->fde_count; i++) { - bfd_put_32 (abfd, - hdr_info->array[i].initial_loc - - sec->output_section->vma, - contents + EH_FRAME_HDR_SIZE + i * 8 + 4); - bfd_put_32 (abfd, - hdr_info->array[i].fde - sec->output_section->vma, - contents + EH_FRAME_HDR_SIZE + i * 8 + 8); + bfd_vma val; + + val = hdr_info->array[i].initial_loc - sec->output_section->vma; + val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000; + if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 + && (hdr_info->array[i].initial_loc + != sec->output_section->vma + val)) + overflow = TRUE; + bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 4); + val = hdr_info->array[i].fde - sec->output_section->vma; + val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000; + if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 + && (hdr_info->array[i].fde + != sec->output_section->vma + val)) + overflow = TRUE; + bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 8); + if (i != 0 + && (hdr_info->array[i].initial_loc + < (hdr_info->array[i - 1].initial_loc + + hdr_info->array[i - 1].range))) + overlap = TRUE; + } + if (overflow) + (*info->callbacks->einfo) + (_("%P: .eh_frame_hdr entry overflow.\n")); + if (overlap) + (*info->callbacks->einfo) + (_("%P: .eh_frame_hdr refers to overlapping FDEs.\n")); + if (overflow || overlap) + { + bfd_set_error (bfd_error_bad_value); + retval = FALSE; } } /* FIXME: octets_per_byte. */ - retval = bfd_set_section_contents (abfd, sec->output_section, contents, - (file_ptr) sec->output_offset, - sec->size); + if (!bfd_set_section_contents (abfd, sec->output_section, contents, + (file_ptr) sec->output_offset, + sec->size)) + retval = FALSE; free (contents); } if (hdr_info->array != NULL) -- 2.7.4