The problem with warning in elf_object_p
authorAlan Modra <amodra@gmail.com>
Fri, 23 Sep 2022 12:57:13 +0000 (22:27 +0930)
committerAlan Modra <amodra@gmail.com>
Sat, 24 Sep 2022 01:58:55 +0000 (11:28 +0930)
elfcode.h can emit three warnings in elf_object_p for various things,
"section extending past end of file", "corrupt string table index",
and "program header with invalid alignment".  The problem with doing
this is that the warning can be emitted for multiple possible targets
as each one is tried.  I was looking at a fuzzer testcase that had an
object file with 6144 program headers, 5316 of which had invalid
alignment.  It would be bad enough to get 5316 messages all the same,
but this object was contained in an archive and resulted in 4975776
repeats.

Some trimming can be done by not warning if the bfd is already marked
read_only, as is done for the "section extending past end of file"
warning, but that still results in an unacceptable number of
warnings for object files in archives.  Besides that, it is just wrong
to warn about a problem detected by a target elf_object_p other than
the one that actually matches.  At some point we might have more
target specific warnings.

So what to do?  One obvious solution is to remove the warnings.
Another is to poke any warning strings into the target xvec, emitting
them if that xvec is the final one chosen.  This also has the benefit
of solving the archive problem.  A warning when recursing into
_bfd_check_format for the first element of the archive (to find the
correct target for the archive) will still be on the xvec at the point
that target is chosen for the archive.  However, target xvecs are
read-only.  Thus the need for per_xvec_warn to logically extend
bfd_target with a writable field.  I've made per_xvec_warn one larger
than bfd_target_vector to provide one place for user code that makes
private copies of target xvecs.

* elfcode.h (elf_swap_shdr_in, elf_object_p): Stash potential
warnings in _bfd_per_xvec_warn location.
* format.c (clear_warnmsg): New function.
(bfd_check_format_matches): Call clear_warnmsg before trying
a new xvec.  Print warnings for the successful non-archive
match.
* targets.c: Include libiberty.h.
(_bfd_target_vector_entries): Use ARRAY_SIZE.
(per_xvec_warn): New.
(_bfd_per_xvec_warn): New function.
* Makefile.am (LIBBFD_H_FILES): Add targets.c.
* Makefile.in: Regenerate.
* libbfd.h: Regenerate.

bfd/Makefile.am
bfd/Makefile.in
bfd/elfcode.h
bfd/format.c
bfd/libbfd.h
bfd/targets.c

index c23dff6cac3ee376de6eb1ab1f80797757509fa9..a5652b2a9c774c7e72d1b2e8738a3e8b9967ead2 100644 (file)
@@ -927,7 +927,7 @@ BFD_H_FILES = bfd-in.h init.c opncls.c libbfd.c \
        linker.c simple.c compress.c
 BFD64_H_FILES = archive64.c
 LIBBFD_H_FILES = libbfd-in.h libbfd.c bfdio.c bfdwin.c \
-       cache.c reloc.c archures.c linker.c
+       cache.c reloc.c targets.c archures.c linker.c
 LIBCOFF_H_FILES = libcoff-in.h coffcode.h
 
 headers: stmp-bin2-h stmp-lbfd-h stmp-lcoff-h
index 82843d2d61dbf8c3c61e5e171f6652612997d7ee..83d686529a0c45cb1bc5bc30e31f7fceee6162de 100644 (file)
@@ -1214,7 +1214,7 @@ BFD_H_FILES = bfd-in.h init.c opncls.c libbfd.c \
 
 BFD64_H_FILES = archive64.c
 LIBBFD_H_FILES = libbfd-in.h libbfd.c bfdio.c bfdwin.c \
-       cache.c reloc.c archures.c linker.c
+       cache.c reloc.c targets.c archures.c linker.c
 
 LIBCOFF_H_FILES = libcoff-in.h coffcode.h
 
index 39c84b4d0fd99a65d79c30e3a378cb4293ab1f7e..1392240f4814b721baa37dbe3ace309107b0188b 100644 (file)
@@ -323,11 +323,11 @@ elf_swap_shdr_in (bfd *abfd,
 
       if (filesize != 0
          && ((ufile_ptr) dst->sh_offset > filesize
-             || dst->sh_size > filesize - dst->sh_offset))
+             || dst->sh_size > filesize - dst->sh_offset)
+         && !abfd->read_only)
        {
-         if (!abfd->read_only)
-           _bfd_error_handler (_("warning: %pB has a section "
-                                 "extending past end of file"), abfd);
+         const char **warn = _bfd_per_xvec_warn (abfd->xvec);
+         *warn = _("warning: %pB has a section extending past end of file");
          abfd->read_only = 1;
        }
     }
@@ -771,10 +771,12 @@ elf_object_p (bfd *abfd)
             So we are kind, and reset the string index value to 0
             so that at least some processing can be done.  */
          i_ehdrp->e_shstrndx = SHN_UNDEF;
-         abfd->read_only = 1;
-         _bfd_error_handler
-           (_("warning: %pB has a corrupt string table index - ignoring"),
-            abfd);
+         if (!abfd->read_only)
+           {
+             const char **warn = _bfd_per_xvec_warn (abfd->xvec);
+             *warn = _("warning: %pB has a corrupt string table index");
+             abfd->read_only = 1;
+           }
        }
     }
   else if (i_ehdrp->e_shstrndx != SHN_UNDEF)
@@ -816,9 +818,14 @@ elf_object_p (bfd *abfd)
             two, as required by the ELF spec.  */
          if (i_phdr->p_align != (i_phdr->p_align & -i_phdr->p_align))
            {
-             abfd->read_only = 1;
-             _bfd_error_handler (_("warning: %pB has a program header "
-                                   "with invalid alignment"), abfd);
+             i_phdr->p_align &= -i_phdr->p_align;
+             if (!abfd->read_only)
+               {
+                 const char **warn = _bfd_per_xvec_warn (abfd->xvec);
+                 *warn = _("warning: %pB has a program header "
+                           "with invalid alignment");
+                 abfd->read_only = 1;
+               }
            }
        }
     }
index 489ffcffd530d0c86aaf515378fee40f2009e4b7..7e2813c97a4bcf0228e4e740a5311b069e916971 100644 (file)
@@ -202,6 +202,13 @@ bfd_preserve_finish (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_preserve *preserve)
   preserve->marker = NULL;
 }
 
+static void
+clear_warnmsg (const bfd_target *targ)
+{
+  const char **warn = _bfd_per_xvec_warn (targ);
+  *warn = NULL;
+}
+
 /*
 FUNCTION
        bfd_check_format_matches
@@ -275,6 +282,7 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
       if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)        /* rewind! */
        goto err_ret;
 
+      clear_warnmsg (abfd->xvec);
       cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
 
       if (cleanup)
@@ -341,6 +349,7 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
       if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
        goto err_ret;
 
+      clear_warnmsg (abfd->xvec);
       cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
       if (cleanup)
        {
@@ -506,6 +515,13 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
        bfd_preserve_finish (abfd, &preserve_match);
       bfd_preserve_finish (abfd, &preserve);
 
+      if (!abfd->my_archive)
+       {
+         const char **warn = _bfd_per_xvec_warn (abfd->xvec);
+         if (*warn)
+           _bfd_error_handler (*warn, abfd);
+       }
+
       /* File position has moved, BTW.  */
       return true;
     }
index 68d0c4278b3b8f7e45a87b5136a89f742b8eb877..a17b98e8e30ecc01c6b3146b7aee7166652aa8ef 100644 (file)
@@ -1,6 +1,6 @@
 /* DO NOT EDIT!  -*- buffer-read-only: t -*-  This file is automatically
    generated from "libbfd-in.h", "libbfd.c", "bfdio.c", "bfdwin.c",
-   "cache.c", "reloc.c", "archures.c" and "linker.c".
+   "cache.c", "reloc.c", "targets.c", "archures.c" and "linker.c".
    Run "make headers" in your build bfd/ to regenerate.  */
 
 /* libbfd.h -- Declarations used by bfd library *implementation*.
@@ -3546,6 +3546,9 @@ bool _bfd_unrecognized_reloc
     sec_ptr section,
     unsigned int r_type);
 
+/* Extracted from targets.c.  */
+const char **_bfd_per_xvec_warn (const bfd_target *);
+
 /* Extracted from archures.c.  */
 extern const bfd_arch_info_type bfd_default_arch_struct;
 const bfd_arch_info_type *bfd_default_compatible
index dc331230affe1cbe072f101ba7efd995350dd501..7dbc3a5dbf6d4204e97ae1c7905354bb1365bebe 100644 (file)
@@ -20,6 +20,7 @@
    MA 02110-1301, USA.  */
 
 #include "sysdep.h"
+#include "libiberty.h"
 #include "bfd.h"
 #include "libbfd.h"
 #include "fnmatch.h"
@@ -1454,7 +1455,10 @@ const bfd_target *const *const bfd_associated_vector = _bfd_associated_vector;
 /* When there is an ambiguous match, bfd_check_format_matches puts the
    names of the matching targets in an array.  This variable is the maximum
    number of entries that the array could possibly need.  */
-const size_t _bfd_target_vector_entries = sizeof (_bfd_target_vector)/sizeof (*_bfd_target_vector);
+const size_t _bfd_target_vector_entries = ARRAY_SIZE (_bfd_target_vector);
+
+/* A place to stash a warning from _bfd_check_format.  */
+static const char *per_xvec_warn[ARRAY_SIZE (_bfd_target_vector) + 1];
 \f
 /* This array maps configuration triplets onto BFD vectors.  */
 
@@ -1474,6 +1478,29 @@ static const struct targmatch bfd_target_match[] = {
   { NULL, NULL }
 };
 
+/*
+INTERNAL_FUNCTION
+       _bfd_per_xvec_warn
+
+SYNOPSIS
+       const char **_bfd_per_xvec_warn (const bfd_target *);
+
+DESCRIPTION
+       Return a location for the given target xvec to use for
+       warnings specific to that target.
+*/
+
+const char **
+_bfd_per_xvec_warn (const bfd_target *targ)
+{
+  size_t idx;
+
+  for (idx = 0; idx < ARRAY_SIZE (_bfd_target_vector); idx++)
+    if (_bfd_target_vector[idx] == targ)
+      break;
+  return per_xvec_warn + idx;
+}
+
 /* Find a target vector, given a name or configuration triplet.  */
 
 static const bfd_target *