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

            Bug ID: 34657
           Summary: Heap-buffer-overflow read in ELF relocation-table
                    decoding (`bfd/elfcode.h`)
           Product: binutils
           Version: 2.47
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: binutils
          Assignee: unassigned at sourceware dot org
          Reporter: hdzhao214 at gmail dot com
  Target Milestone: ---

Created attachment 17016
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17016&action=edit
The `artifacts.zip` package includes the PoC generation script, the sanitizer
report, the bug report, and the candidate patch

## Vulnerability description

`elf_slurp_reloc_table_from_section` receives `reloc_count` as `bfd_size_type`,
but uses a 32-bit `unsigned int i` as its loop counter. A relocation section
containing at least `2^32 + 1` entries makes `i` wrap to zero while `i <
reloc_count` remains true. The independently advancing `native_relocs` pointer
then reaches past the relocation buffer allocated from the declared section
size.

```c
for (i = 0, relent = relents;
     i < reloc_count;
     i++, relent++, native_relocs += entsize)
  {
    ...
    elf_swap_reloca_in (abfd, native_relocs, &rela);
  }
```

The supplied sparse ELF declares `2^32` 24-byte ELF64 RELA entries. The first
post-wrap iteration reads just beyond a 103,079,215,104-byte heap allocation.

## Version and commit

GNU Binutils 2.47.50, commit `d715260f420066befb2d30ec8f5befcdf7ecfd84`
(2026-09-08).

## Environment

Ubuntu 24.04.4 LTS, x86_64, Linux 6.8.0-136-generic; GCC 13.3.0 and Python
3.12.3. The binary was built with AddressSanitizer using `-O0 -g3
-fsanitize=address -fno-omit-frame-pointer`. The sparse input has roughly 96
GiB of logical relocation data; reproducing it needs well over 100 GiB of
addressable memory plus ASan overhead.

## Steps to reproduce

1. Install build prerequisites (for example, on Ubuntu):

   ```sh
   sudo apt-get update
   sudo apt-get install -y build-essential bison flex texinfo python3 \
       libgmp-dev libmpfr-dev libmpc-dev zlib1g-dev
   ```

2. Obtain the affected revision and make an AddressSanitizer build:

   ```sh
   export SRC="$PWD/binutils-gdb"
   git clone https://sourceware.org/git/binutils-gdb.git "$SRC"
   git -C "$SRC" checkout d715260f420066befb2d30ec8f5befcdf7ecfd84
   mkdir "$SRC/build-asan" && cd "$SRC/build-asan"
   CC=gcc CFLAGS='-O0 -g3 -fsanitize=address -fno-omit-frame-pointer' \
   LDFLAGS='-fsanitize=address' \
   "$SRC/configure" --disable-gdb --disable-gdbserver --disable-sim \
       --disable-gprofng --disable-gold --disable-werror --disable-nls
   make -j"$(nproc)" all-binutils
   export BUILD="$SRC/build-asan"
   ```

3. Place the supplied `gen_poc.py` in a writable directory and generate the
sparse ELF:

   ```sh
   export WORK="$PWD/poc-work"
   mkdir -p "$WORK"
   cp gen_poc.py "$WORK/"
   (cd "$WORK" && python3 gen_poc.py)
   ```

4. Trigger the fault:

   ```sh
   ASAN_OPTIONS=detect_leaks=0:abort_on_error=1 \
     "$BUILD/binutils/objdump" -R "$WORK/poc.elf" >/dev/null
   ```

## Sanitizer report

The following is the complete, unmodified contents of `sanitizer_report.txt`.

```text
=================================================================
==94700==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x7568a73f9807 at pc 0x5d6e8c95f6a4 bp 0x7ffcc0a14230 sp 0x7ffcc0a14220
READ of size 1 at 0x7568a73f9807 thread T0
    #0 0x5d6e8c95f6a3 in bfd_getl64 ../../bfd/libbfd.c:903
    #1 0x5d6e8c9d0d90 in bfd_elf64_swap_reloca_in ../../bfd/elfcode.h:445
    #2 0x5d6e8c9d87d6 in elf_slurp_reloc_table_from_section
../../bfd/elfcode.h:1584
    #3 0x5d6e8c9d94de in bfd_elf64_slurp_reloc_table ../../bfd/elfcode.h:1704
    #4 0x5d6e8ca20911 in _bfd_elf_canonicalize_dynamic_reloc
../../bfd/elf.c:9319
    #5 0x5d6e8c80608d in dump_dynamic_relocs ../../binutils/objdump.c:5656
    #6 0x5d6e8c807083 in dump_bfd ../../binutils/objdump.c:5906
    #7 0x5d6e8c807374 in display_object_bfd ../../binutils/objdump.c:5971
    #8 0x5d6e8c807696 in display_any_bfd ../../binutils/objdump.c:6050
    #9 0x5d6e8c807706 in display_file ../../binutils/objdump.c:6071
    #10 0x5d6e8c809222 in main ../../binutils/objdump.c:6494
    #11 0x7590aa82a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #12 0x7590aa82a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #13 0x5d6e8c7ec374 in _start
(/home/shootduck/hotracer-experiments/0-day/hotracer/pilot/objdump/2026-09-08-2.47.50-d715260/binutils-gdb/build-asan/binutils/objdump+0x143374)
(BuildId: 7b3b22cfe8266150e71d8e259cd00c3996daee41)

0x7568a73f9807 is located 7 bytes after 103079215104-byte region
[0x7550a73f9800,0x7568a73f9800)
allocated by thread T0 here:
    #0 0x7590aacfd9c7 in malloc
../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x5d6e8c95e243 in bfd_malloc ../../bfd/libbfd.c:291
    #2 0x5d6e8c9ccf56 in _bfd_malloc_and_read ../../bfd/libbfd.h:885
    #3 0x5d6e8c9d86bf in elf_slurp_reloc_table_from_section
../../bfd/elfcode.h:1561
    #4 0x5d6e8c9d94de in bfd_elf64_slurp_reloc_table ../../bfd/elfcode.h:1704
    #5 0x5d6e8ca20911 in _bfd_elf_canonicalize_dynamic_reloc
../../bfd/elf.c:9319
    #6 0x5d6e8c80608d in dump_dynamic_relocs ../../binutils/objdump.c:5656
    #7 0x5d6e8c807083 in dump_bfd ../../binutils/objdump.c:5906
    #8 0x5d6e8c807374 in display_object_bfd ../../binutils/objdump.c:5971
    #9 0x5d6e8c807696 in display_any_bfd ../../binutils/objdump.c:6050
    #10 0x5d6e8c807706 in display_file ../../binutils/objdump.c:6071
    #11 0x5d6e8c809222 in main ../../binutils/objdump.c:6494
    #12 0x7590aa82a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #13 0x7590aa82a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #14 0x5d6e8c7ec374 in _start
(/home/shootduck/hotracer-experiments/0-day/hotracer/pilot/objdump/2026-09-08-2.47.50-d715260/binutils-gdb/build-asan/binutils/objdump+0x143374)
(BuildId: 7b3b22cfe8266150e71d8e259cd00c3996daee41)

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../bfd/libbfd.c:903 in
bfd_getl64
Shadow bytes around the buggy address:
  0x7568a73f9580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7568a73f9600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7568a73f9680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7568a73f9700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7568a73f9780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7568a73f9800:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7568a73f9880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7568a73f9900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7568a73f9980: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7568a73f9a00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7568a73f9a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==94700==ABORTING
timeout: the monitored command dumped core
```

## Potential fix

Use the same width for the loop counter as `reloc_count`. This is sufficient
because the relocation buffer is sized for exactly that many entries; the
corrected loop stops after the last valid entry rather than wrapping. The
diagnostic format must be widened accordingly.

```diff
diff --git a/bfd/elfcode.h b/bfd/elfcode.h
@@
-  unsigned int i;
+  bfd_size_type i;
@@
-    (_("%pB(%pA): relocation %d has invalid symbol index %ld"),
-     abfd, asect, i, (long) ELF_R_SYM (rela.r_info));
+    (_("%pB(%pA): relocation %llu has invalid symbol index %ld"),
+     abfd, asect, (unsigned long long) i,
+     (long) ELF_R_SYM (rela.r_info));
```

The complete, apply-ready patch is included as `proposed-fix.patch`. It was
applied to a disposable checkout of the stated commit. A dedicated ASan
regression harness invoked the same BFD dynamic-relocation canonicalization
path on the supplied input, canonicalized 4,294,967,296 relocations, and exited
without a sanitizer finding; it avoids only the subsequent formatting of
billions of output lines.

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

Reply via email to