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

            Bug ID: 34874
           Summary: ld: OUTPUT_FORMAT in a linker script causes
                    heap-buffer-overflow in x86 and generic ELF
                    after_open_output hooks
           Product: binutils
           Version: 2.48 (HEAD)
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: harshit7kr at gmail dot com
  Target Milestone: ---

A linker script can retarget the output BFD with OUTPUT_FORMAT after the
emulation has been selected.  The x86 emulation's after_open_output hook
and ld's generic ELF hook (ldelf_after_open_output) still run afterwards.
Each assumes the output link hash table matches what it expects, and
each accesses memory out of bounds when it does not.

Tested at commit be7a7615a84471cea567ea8d60a192356316a0f8,
x86_64-pc-linux-gnu, gcc 16, built with --enable-targets=all and
-fsanitize=address.

Neither defect is visible without AddressSanitizer.

--- Case 1: out-of-bounds write (x86 emulation) ---

Reproducer:

  $ printf '.globl _start\n_start:\n\tnop\n' > foo.s
  $ as --64 foo.s -o foo.o
  $ cat script
  OUTPUT_FORMAT("elf64-bpfle")
  OUTPUT_ARCH(bpf)
  SECTIONS { .text : { *(.text) } }
  $ ld -melf_x86_64 -T script -o out foo.o

Result:

  ERROR: AddressSanitizer: heap-buffer-overflow
  WRITE of size 8 at 0x7cdf98be0960 thread T0
      #0 in bfd_elf_linker_x86_set_options bfd/elfxx-x86.c:5042
      #1 in elf_x86_after_open_output ld/eelf_x86_64.c:67

  The write lands 424 bytes past a 1848-byte region.

Cause: bfd_elf_linker_x86_set_options takes the target id from the
output BFD's backend data.  After OUTPUT_FORMAT retargets the output,
both that id and the link hash table belong to the foreign backend, so
the id comparison in the elf_x86_hash_table macro (bfd/elfxx-x86.h)
passes and the foreign table is treated as struct
elf_x86_link_hash_table.  The write to htab->params then overflows the
heap block.

--- Case 2: out-of-bounds read (generic ELF hook) ---

Same reproducer, with a script that selects an ELF format that uses the
generic link hash table:

  OUTPUT_FORMAT("elf64-nfp")
  OUTPUT_ARCH(nfp)
  SECTIONS { .text : { *(.text) } }

Result:

  ERROR: AddressSanitizer: heap-buffer-overflow
  READ of size 4 at 0x7bbb947e0150 thread T0
      #0 in elf_hash_table_id bfd/elf-bfd.h:833
      #1 in ldelf_after_open_output ld/ldelf.c:82

Cause: ldelf_after_open_output only checks that the output BFD is ELF
flavour, and assumes that implies an ELF link hash table.  Some ELF
flavours use the generic hash table, which is smaller, so reading
hash_table_id goes past the end of it.

--- Fix ---

Two patches, with a regression test (ld-x86-64/arch-confusion.exp):

  1. bfd: elfxx-x86: reject non-x86 target ids in elf_x86_hash_table
  2. ld: do not read ELF fields from a generic link hash table

Series on the list (v1):
  https://patchwork.sourceware.org/project/binutils/list/?series=68008

Series on the list (v2):
https://patchwork.sourceware.org/project/binutils/list/?series=67920

Notes:
- Triggering requires a linker script whose OUTPUT_FORMAT differs from
  the emulation's format, so it needs a crafted script.
- Separate pre-existing bug found while sweeping other emulations:
  -mm68hc11elf with OUTPUT_FORMAT("elf32-little") crashes in
  m68hc11elf_after_allocation (stub_file is NULL).  Not touched by
  these patches; will be reported separately.

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

Reply via email to