https://sourceware.org/bugzilla/show_bug.cgi?id=34998

            Bug ID: 34998
           Summary: ld aborts with an internal error when a section
                    without SHF_ALLOC holds a relocation against an
                    STT_GNU_IFUNC symbol, and that section is not a note
                    or debug section. There are two abort sites in the
                    same function, one for global and one for local IFUNC
                    s
           Product: binutils
           Version: 2.48 (HEAD)
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: emily.west286 at gmail dot com
  Target Milestone: ---

Created attachment 17272
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17272&action=edit
Suggested patch

Binutils ld: internal error (abort) in elf_x86_64_relocate_section on IFUNC
relocation in non-SHF_ALLOC section

Component: ld
Version: binutils-gdb master, found on commit
c79376a262328164cdf81eddfcc5518314b1856a
Target: x86_64-pc-linux-gnu

Summary:
ld aborts with an internal error when a section without SHF_ALLOC holds a
relocation against an STT_GNU_IFUNC symbol, and that section is not a note or
debug section. There are two abort sites in the same function, one for global
and one for local IFUNC symbols:

ld-new: BFD (GNU Binutils) ... internal error, aborting at elf64-x86-64.c:3349
in elf_x86_64_relocate_section
ld-new: BFD (GNU Binutils) ... internal error, aborting at elf64-x86-64.c:3261
in elf_x86_64_relocate_section
ld-new: Please report this bug.

Reproducers:
id001441 (attached): aborts at 3349

        /src/build/ld/ld-new: BFD (GNU Binutils) 2.47.50.20261112 internal
error, aborting at /src/binutils-gdb/bfd/elf64-x86-64.c:3349 in
elf_x86_64_relocate_section
        Please report this bug.


id002053 (attached): aborts at 3261

        /src/build/ld/ld-new: warning: cannot find entry symbol _start;
defaulting to 0000000000401000
        /src/build/ld/ld-new: BFD (GNU Binutils) 2.47.50.20261112 internal
error, aborting at /src/binutils-gdb/bfd/elf64-x86-64.c:3261 in
elf_x86_64_relocate_section
        Please report this bug.

ld -o /tmp/out id001441
ld -o /tmp/out id002053

Backtrace for id001441
#0 _bfd_abort                  bfd/bfd.c:2315
#1 elf_x86_64_relocate_section bfd/elf64-x86-64.c:3349
#2 elf_link_input_bfd          bfd/elflink.c:11978
#3 _bfd_elf_final_link         bfd/elflink.c:13241
#4 ldwrite                     ld/ldwrite.c:548
#5 main                        ld/ldmain.c:1001

Root cause:

Line 3349 (global IFUNC). For a regular-defined STT_GNU_IFUNC symbol, the code
only handles two kinds of non-SEC_ALLOC input sections:

c
if ((input_section->flags & SEC_ALLOC) == 0)
  {
    if (elf_section_type (input_section) == SHT_NOTE)
      goto skip_ifunc;
    if ((input_section->flags & SEC_DEBUGGING) != 0)
      continue;
    abort ();
  }

Any other non-alloc section reaches abort(). SEC_DEBUGGING is set from the
section name (.debug* and similar), so a custom non-alloc section, or one with
an empty or corrupted name, is not covered. The note and debug cases were added
as separate fixes (PR ld/18808, and a 2018 fix for annobin notes). The general
case was never handled.

>From gdb, for id001441:
input_section->name = "", flags = 0x10c (SEC_HAS_CONTENTS | SEC_READONLY |
SEC_RELOC). It has no SEC_ALLOC, and SEC_DEBUGGING isn’t set because of the
empty name. Its type isn’t SHT_NOTE.
h->root.root.string = "_start", h->type = STT_GNU_IFUNC, regular-defined.
This input fails both exceptions in the (input_section->flags & SEC_ALLOC) == 0
branch and falls through to abort().

Impact:
Low. ld exits instead of reporting an error. It needs a crafted or unusual
object file. No memory corruption was observed when run with Address Sanitizer.

Suggested fix:
Replace both abort() calls with a normal error, so ld reports bad input and
fails cleanly. e.g.,

@@ -3258,7 +3258,15 @@
              h = _bfd_elf_x86_get_local_sym_hash (htab, input_bfd,
                                                   rel, false);
              if (h == NULL)
-               abort ();
+               {
+                 _bfd_error_handler
+                   (_("%pB(%pA+%#" PRIx64 "): relocation against local "
+                      "STT_GNU_IFUNC symbol in non-SHF_ALLOC section is not "
+                      "supported"),
+                    input_bfd, input_section, (uint64_t) rel->r_offset);
+                 bfd_set_error (bfd_error_bad_value);
+                 return false;
+               }

              /* Set STT_GNU_IFUNC symbol value.  */
              h->root.u.def.value = sym->st_value;
@@ -3346,7 +3354,13 @@
                 thus ld.so will not process them.  */
              if ((input_section->flags & SEC_DEBUGGING) != 0)
                continue;
-             abort ();
+             _bfd_error_handler
+               (_("%pB(%pA+%#" PRIx64 "): relocation against STT_GNU_IFUNC "
+                  "symbol `%s' in non-SHF_ALLOC section is not supported"),
+                input_bfd, input_section, (uint64_t) rel->r_offset,
+                h->root.root.string);
+             bfd_set_error (bfd_error_bad_value);
+             return false;
            }

          switch (r_type)

Alternatively, move the SEC_ALLOC check before the local-symbol lookup, so both
cases share one error path. elf32-i386.c has similar IFUNC handling and is
worth checking for the same pattern.

Fix verification:
The attached patch.Dockerfile builds ld at commit
c79376a262328164cdf81eddfcc5518314b1856a with ifunc-nonalloc.patch applied (the
suggested fix above). With the patch, both inputs fail with a normal error
instead of aborting:

docker build -f patch.Dockerfile -t ld-fix .
docker run --rm -v "$PWD":/work ld-fix id001441
docker run --rm -v "$PWD":/work ld-fix id002053

/src/build/ld/ld-new: split-inputs/id001441/part0(+0x1): relocation against
STT_GNU_IFUNC symbol `_start' in non-SHF_ALLOC section is not supported
/src/build/ld/ld-new: final link failed: bad value

/src/build/ld/ld-new: warning: cannot find entry symbol _start; defaulting to
0000000000401000
/src/build/ld/ld-new: split-inputs/id002053/part0(.text+0x1): relocation
against local STT_GNU_IFUNC symbol in non-SHF_ALLOC section is not supported
/src/build/ld/ld-new: final link failed: bad value

Attachments:
id001441 and id002053   finding
ifunc-nonalloc.patch    suggested fix
patch.Dockerfile        builds ld with the patch for the suggested fix applied

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to