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

            Bug ID: 34653
           Summary: Heap-buffer-overflow write in `index_file`
                    (`binutils/objdump.c`)
           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 17012
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17012&action=edit
The `artifacts.zip` package includes the PoC generation script, the PoC, the
sanitizer report, the bug report, and a candidate patch

## Vulnerability description

When source intermixing is requested with `objdump -S`, `index_file` maps the
referenced source file and records each line start in `linemap`. The index
variable is an `unsigned int`, while the allocation capacity is wider. At the
`2^32`th line terminator, `lineno + 1` wraps to zero. The growth test is
therefore false and the subsequent pointer write uses index `UINT_MAX` beyond a
`UINT_MAX`-element allocation.

```c
if (linemap == NULL || line_map_size < lineno + 1)
  linemap = xrealloc (linemap, line_map_size * sizeof (char *));
linemap[lineno++] = lstart;
```

A source file containing `2^32` newline bytes followed by a sparse tail
triggers an 8-byte heap write while resolving source for a debug-enabled
object.

## 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; Python 3.12.3.
The binary was built with AddressSanitizer using `-O0 -g3 -fsanitize=address
-fno-omit-frame-pointer`. The proof of concept needs roughly 22 GB of logical
source-file space and tens of GB of RAM.

## 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 `generate_poc.py` in a writable directory and create the
object and replacement source file:

   ```sh
   export WORK="$PWD/poc-work"
   mkdir -p "$WORK"
   python3 generate_poc.py "$WORK"
   ```

4. Trigger the fault:

   ```sh
   ASAN_OPTIONS=detect_leaks=0:abort_on_error=1 \\
     "$BUILD/binutils/objdump" -S "$WORK/sample.o" >/dev/null
   ```

## Sanitizer report

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

```text
=================================================================
==1675241==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x7b2f1cdb27f8 at pc 0x61e109c64a23 bp 0x7ffda5db8a60 sp 0x7ffda5db8a50
WRITE of size 8 at 0x7b2f1cdb27f8 thread T0
    #0 0x61e109c64a22 in index_file ../../binutils/objdump.c:2068
    #1 0x61e109c64b95 in try_print_file_open ../../binutils/objdump.c:2097
    #2 0x61e109c64e08 in update_source_path ../../binutils/objdump.c:2120
    #3 0x61e109c65ebc in show_line ../../binutils/objdump.c:2365
    #4 0x61e109c6acea in disassemble_bytes ../../binutils/objdump.c:3407
    #5 0x61e109c6ed0a in disassemble_section ../../binutils/objdump.c:4131
    #6 0x61e109dd9924 in bfd_map_over_sections ../../bfd/section.c:1369
    #7 0x61e109c6fc8d in disassemble_data ../../binutils/objdump.c:4278
    #8 0x61e109c780b6 in dump_bfd ../../binutils/objdump.c:5910
    #9 0x61e109c78374 in display_object_bfd ../../binutils/objdump.c:5971
    #10 0x61e109c78696 in display_any_bfd ../../binutils/objdump.c:6050
    #11 0x61e109c78706 in display_file ../../binutils/objdump.c:6071
    #12 0x61e109c7a222 in main ../../binutils/objdump.c:6494
    #13 0x7b3e64e2a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #14 0x7b3e64e2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #15 0x61e109c5d374 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)

0x7b2f1cdb27f8 is located 0 bytes after 34359738360-byte region
[0x7b271cdb2800,0x7b2f1cdb27f8)
allocated by thread T0 here:
    #0 0x7b3e652fc778 in realloc
../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85
    #1 0x61e10a08d949 in xrealloc ../../libiberty/xmalloc.c:181
    #2 0x61e109c649e4 in index_file ../../binutils/objdump.c:2065
    #3 0x61e109c64b95 in try_print_file_open ../../binutils/objdump.c:2097
    #4 0x61e109c64e08 in update_source_path ../../binutils/objdump.c:2120
    #5 0x61e109c65ebc in show_line ../../binutils/objdump.c:2365
    #6 0x61e109c6acea in disassemble_bytes ../../binutils/objdump.c:3407
    #7 0x61e109c6ed0a in disassemble_section ../../binutils/objdump.c:4131
    #8 0x61e109dd9924 in bfd_map_over_sections ../../bfd/section.c:1369
    #9 0x61e109c6fc8d in disassemble_data ../../binutils/objdump.c:4278
    #10 0x61e109c780b6 in dump_bfd ../../binutils/objdump.c:5910
    #11 0x61e109c78374 in display_object_bfd ../../binutils/objdump.c:5971
    #12 0x61e109c78696 in display_any_bfd ../../binutils/objdump.c:6050
    #13 0x61e109c78706 in display_file ../../binutils/objdump.c:6071
    #14 0x61e109c7a222 in main ../../binutils/objdump.c:6494
    #15 0x7b3e64e2a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #16 0x7b3e64e2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #17 0x61e109c5d374 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 ../../binutils/objdump.c:2068
in index_file
Shadow bytes around the buggy address:
  0x7b2f1cdb2500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7b2f1cdb2580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7b2f1cdb2600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7b2f1cdb2680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7b2f1cdb2700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7b2f1cdb2780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00[fa]
  0x7b2f1cdb2800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b2f1cdb2880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b2f1cdb2900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b2f1cdb2980: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7b2f1cdb2a00: 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
==1675241==ABORTING
```

## Potential fix

Reject a further line after the largest representable line number instead of
allowing the counter to wrap. This preserves all addressable line mappings and
safely stops indexing an unrepresentable one.

```diff
diff --git a/binutils/objdump.c b/binutils/objdump.c
@@ -2051,6 +2051,11 @@ index_file (...)
       /* End of line found.  */
+
+      /* MAXLINE is unsigned int; do not wrap LINENO.  */
+      if (lineno == (unsigned int) -1)
+       break;

       if (linemap == NULL || line_map_size < lineno + 1)
```

The complete, apply-ready patch is included as `proposed-fix.patch`. It was
applied to a disposable checkout of the stated commit and the supplied proof of
concept was rerun without an AddressSanitizer finding.

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

Reply via email to