diff options
Diffstat (limited to 'bfd/xcofflink.c')
-rw-r--r-- | bfd/xcofflink.c | 97 |
1 files changed, 70 insertions, 27 deletions
diff --git a/bfd/xcofflink.c b/bfd/xcofflink.c index 446fa5a..b50b17f 100644 --- a/bfd/xcofflink.c +++ b/bfd/xcofflink.c @@ -243,6 +243,55 @@ xcoff_get_section_contents (bfd *abfd, asection *sec) return contents; } +/* Read .loader and swap in the header. Sanity check to prevent + buffer overflows. Don't bother to check for overlap as that sort + of insanity shouldn't lead to incorrect program behaviour. */ + +static bfd_byte * +xcoff_get_ldhdr (bfd *abfd, asection *lsec, struct internal_ldhdr *ldhdr) +{ + bfd_byte *contents = xcoff_get_section_contents (abfd, lsec); + if (contents) + { + bfd_xcoff_swap_ldhdr_in (abfd, contents, ldhdr); + if (ldhdr->l_nsyms != 0) + { + bfd_vma symoff = bfd_xcoff_loader_symbol_offset (abfd, ldhdr); + if (symoff > lsec->size) + goto fail; + bfd_size_type onesym = bfd_xcoff_ldsymsz (abfd); + bfd_size_type syms; + if (_bfd_mul_overflow (ldhdr->l_nsyms, onesym, &syms) + || syms > lsec->size - symoff) + goto fail; + } + if (ldhdr->l_stlen != 0 + && (ldhdr->l_stoff > lsec->size + || ldhdr->l_stlen > lsec->size - ldhdr->l_stoff)) + goto fail; + if (ldhdr->l_nreloc != 0) + { + bfd_vma reloff = bfd_xcoff_loader_reloc_offset (abfd, ldhdr); + if (reloff > lsec->size) + goto fail; + bfd_size_type onerel = bfd_xcoff_ldrelsz (abfd); + bfd_size_type rels; + if (_bfd_mul_overflow (ldhdr->l_nreloc, onerel, &rels) + || rels > lsec->size - reloff) + goto fail; + } + if (ldhdr->l_nimpid != 0 + && (ldhdr->l_impoff > lsec->size + || ldhdr->l_istlen > lsec->size - ldhdr->l_impoff)) + goto fail; + } + return contents; + + fail: + bfd_set_error (bfd_error_file_truncated); + return NULL; +} + /* Get the size required to hold the dynamic symbols. */ long @@ -265,12 +314,10 @@ _bfd_xcoff_get_dynamic_symtab_upper_bound (bfd *abfd) return -1; } - contents = xcoff_get_section_contents (abfd, lsec); + contents = xcoff_get_ldhdr (abfd, lsec, &ldhdr); if (!contents) return -1; - bfd_xcoff_swap_ldhdr_in (abfd, (void *) contents, &ldhdr); - return (ldhdr.l_nsyms + 1) * sizeof (asymbol *); } @@ -299,12 +346,10 @@ _bfd_xcoff_canonicalize_dynamic_symtab (bfd *abfd, asymbol **psyms) return -1; } - contents = xcoff_get_section_contents (abfd, lsec); + contents = xcoff_get_ldhdr (abfd, lsec, &ldhdr); if (!contents) return -1; - bfd_xcoff_swap_ldhdr_in (abfd, contents, &ldhdr); - strings = (char *) contents + ldhdr.l_stoff; symbuf = bfd_zalloc (abfd, ldhdr.l_nsyms * sizeof (* symbuf)); @@ -322,9 +367,7 @@ _bfd_xcoff_canonicalize_dynamic_symtab (bfd *abfd, asymbol **psyms) symbuf->symbol.the_bfd = abfd; - if (ldsym._l._l_l._l_zeroes == 0) - symbuf->symbol.name = strings + ldsym._l._l_l._l_offset; - else + if (ldsym._l._l_l._l_zeroes != 0) { char *c; @@ -335,6 +378,10 @@ _bfd_xcoff_canonicalize_dynamic_symtab (bfd *abfd, asymbol **psyms) c[SYMNMLEN] = '\0'; symbuf->symbol.name = c; } + else if (ldsym._l._l_l._l_offset < ldhdr.l_stlen) + symbuf->symbol.name = strings + ldsym._l._l_l._l_offset; + else + symbuf->symbol.name = _("<corrupt>"); if (ldsym.l_smclas == XMC_XO) symbuf->symbol.section = bfd_abs_section_ptr; @@ -384,12 +431,10 @@ _bfd_xcoff_get_dynamic_reloc_upper_bound (bfd *abfd) return -1; } - contents = xcoff_get_section_contents (abfd, lsec); + contents = xcoff_get_ldhdr (abfd, lsec, &ldhdr); if (!contents) return -1; - bfd_xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr); - return (ldhdr.l_nreloc + 1) * sizeof (arelent *); } @@ -419,12 +464,10 @@ _bfd_xcoff_canonicalize_dynamic_reloc (bfd *abfd, return -1; } - contents = xcoff_get_section_contents (abfd, lsec); + contents = xcoff_get_ldhdr (abfd, lsec, &ldhdr); if (!contents) return -1; - bfd_xcoff_swap_ldhdr_in (abfd, contents, &ldhdr); - relbuf = bfd_alloc (abfd, ldhdr.l_nreloc * sizeof (arelent)); if (relbuf == NULL) return -1; @@ -905,7 +948,7 @@ xcoff_link_add_dynamic_symbols (bfd *abfd, struct bfd_link_info *info) return false; } - contents = xcoff_get_section_contents (abfd, lsec); + contents = xcoff_get_ldhdr (abfd, lsec, &ldhdr); if (!contents) return false; @@ -913,8 +956,6 @@ xcoff_link_add_dynamic_symbols (bfd *abfd, struct bfd_link_info *info) included in the link. */ bfd_section_list_clear (abfd); - bfd_xcoff_swap_ldhdr_in (abfd, contents, &ldhdr); - strings = (char *) contents + ldhdr.l_stoff; elsym = contents + bfd_xcoff_loader_symbol_offset(abfd, &ldhdr); @@ -934,14 +975,16 @@ xcoff_link_add_dynamic_symbols (bfd *abfd, struct bfd_link_info *info) if ((ldsym.l_smtype & L_EXPORT) == 0) continue; - if (ldsym._l._l_l._l_zeroes == 0) - name = strings + ldsym._l._l_l._l_offset; - else + if (ldsym._l._l_l._l_zeroes != 0) { memcpy (nambuf, ldsym._l._l_name, SYMNMLEN); nambuf[SYMNMLEN] = '\0'; name = nambuf; } + else if (ldsym._l._l_l._l_offset < ldhdr.l_stlen) + name = strings + ldsym._l._l_l._l_offset; + else + continue; /* Normally we could not call xcoff_link_hash_lookup in an add symbols routine, since we might not be using an XCOFF hash @@ -2368,12 +2411,10 @@ xcoff_link_check_dynamic_ar_symbols (bfd *abfd, /* There are no symbols, so don't try to include it. */ return true; - contents = xcoff_get_section_contents (abfd, lsec); + contents = xcoff_get_ldhdr (abfd, lsec, &ldhdr); if (!contents) return false; - bfd_xcoff_swap_ldhdr_in (abfd, contents, &ldhdr); - strings = (char *) contents + ldhdr.l_stoff; elsym = contents + bfd_xcoff_loader_symbol_offset (abfd, &ldhdr); @@ -2392,14 +2433,16 @@ xcoff_link_check_dynamic_ar_symbols (bfd *abfd, if ((ldsym.l_smtype & L_EXPORT) == 0) continue; - if (ldsym._l._l_l._l_zeroes == 0) - name = strings + ldsym._l._l_l._l_offset; - else + if (ldsym._l._l_l._l_zeroes != 0) { memcpy (nambuf, ldsym._l._l_name, SYMNMLEN); nambuf[SYMNMLEN] = '\0'; name = nambuf; } + else if (ldsym._l._l_l._l_offset < ldhdr.l_stlen) + name = strings + ldsym._l._l_l._l_offset; + else + continue; h = bfd_link_hash_lookup (info->hash, name, false, false, true); |