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

            Bug ID: 34656
           Summary: Heap-buffer-overflow write in Intel HEX section
                    loading (`bfd/ihex.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 17015
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17015&action=edit
The `artifacts.zip` package includes the PoC generation script, the sanitizer
report, the bug report, and the candidate patch

## Vulnerability description

Intel HEX input is scanned once to determine a section size and read again to
populate the allocation. `ihex_read_section` assumes the input is unchanged and
writes each decoded record without checking the remaining capacity. An attacker
able to modify the file between the passes can enlarge a record after the scan,
causing the second pass to decode more data than was allocated.

```c
for (i = 0; i < len; i++)
  *p++ = HEX2 (buf + 2 * i);
```

The supplied modifier changes a two-byte data record into a 255-byte record
while the scanner traverses a long newline tail. The second pass writes 765
bytes into a section sized for 512 bytes.

## 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 target was an AddressSanitizer build. The race input occupies
approximately 600 MB.

## 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 `gen_ihex.py` and `race_modifier.py` in a writable directory and
create the initial input:

   ```sh
   export WORK="$PWD/poc-work"
   mkdir -p "$WORK"
   python3 gen_ihex.py "$WORK/sample.hex" 64 600000000
   ```

4. Start the reader, alter the already-scanned record, and wait for it:

   ```sh
   ASAN_OPTIONS=detect_leaks=0:abort_on_error=1 \\
     "$BUILD/binutils/objdump" -s -I ihex "$WORK/sample.hex" \\
     >"$WORK/asan.txt" 2>&1 &
   pid=$!
   python3 race_modifier.py "$pid" "$WORK/sample.hex"
   wait "$pid"
   ```

## Sanitizer report

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

```text
=================================================================
==29231==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x516000000290 at pc 0x5dbaf0c26047 bp 0x7ffe3b7eea20 sp 0x7ffe3b7eea10
WRITE of size 1 at 0x516000000290 thread T0
    #0 0x5dbaf0c26046 in ihex_read_section ../../bfd/ihex.c:597
    #1 0x5dbaf0c2639d in ihex_get_section_contents ../../bfd/ihex.c:640
    #2 0x5dbaf0c1a174 in bfd_get_section_contents ../../bfd/section.c:1615
    #3 0x5dbaf0c070f0 in bfd_is_section_compressed_info
../../bfd/compress.c:901
    #4 0x5dbaf0c0752f in bfd_is_section_compressed ../../bfd/compress.c:959
    #5 0x5dbaf0ab471e in dump_section ../../binutils/objdump.c:5193
    #6 0x5dbaf0c19924 in bfd_map_over_sections ../../bfd/section.c:1369
    #7 0x5dbaf0ab4f70 in dump_data ../../binutils/objdump.c:5276
    #8 0x5dbaf0ab809c in dump_bfd ../../binutils/objdump.c:5908
    #9 0x5dbaf0ab8374 in display_object_bfd ../../binutils/objdump.c:5971
    #10 0x5dbaf0ab8696 in display_any_bfd ../../binutils/objdump.c:6050
    #11 0x5dbaf0ab8706 in display_file ../../binutils/objdump.c:6071
    #12 0x5dbaf0aba222 in main ../../binutils/objdump.c:6494
    #13 0x704f1ce2a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #14 0x704f1ce2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #15 0x5dbaf0a9d374 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)

0x516000000290 is located 0 bytes after 528-byte region
[0x516000000080,0x516000000290)
allocated by thread T0 here:
    #0 0x704f1d2fd9c7 in malloc
../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x5dbaf0ec2858 in _objalloc_alloc ../../libiberty/objalloc.c:143
    #2 0x5dbaf0c0f501 in bfd_alloc ../../bfd/libbfd.c:453
    #3 0x5dbaf0c2630b in ihex_get_section_contents ../../bfd/ihex.c:637
    #4 0x5dbaf0c1a174 in bfd_get_section_contents ../../bfd/section.c:1615
    #5 0x5dbaf0c070f0 in bfd_is_section_compressed_info
../../bfd/compress.c:901
    #6 0x5dbaf0c0752f in bfd_is_section_compressed ../../bfd/compress.c:959
    #7 0x5dbaf0ab471e in dump_section ../../binutils/objdump.c:5193
    #8 0x5dbaf0c19924 in bfd_map_over_sections ../../bfd/section.c:1369
    #9 0x5dbaf0ab4f70 in dump_data ../../binutils/objdump.c:5276
    #10 0x5dbaf0ab809c in dump_bfd ../../binutils/objdump.c:5908
    #11 0x5dbaf0ab8374 in display_object_bfd ../../binutils/objdump.c:5971
    #12 0x5dbaf0ab8696 in display_any_bfd ../../binutils/objdump.c:6050
    #13 0x5dbaf0ab8706 in display_file ../../binutils/objdump.c:6071
    #14 0x5dbaf0aba222 in main ../../binutils/objdump.c:6494
    #15 0x704f1ce2a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #16 0x704f1ce2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #17 0x5dbaf0a9d374 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/ihex.c:597 in
ihex_read_section
Shadow bytes around the buggy address:
  0x516000000000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x516000000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x516000000100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x516000000180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x516000000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x516000000280: 00 00[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x516000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x516000000380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x516000000400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x516000000480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x516000000500: 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
==29231==ABORTING
```

## Potential fix

Treat the second pass as parsing untrusted current input: validate record
syntax and reject a record whose decoded length exceeds `section->size - (p -
contents)` before its first output byte.

```diff
diff --git a/bfd/ihex.c b/bfd/ihex.c
@@
-  BFD_ASSERT (c == ':');
+  if (c != ':')
+    goto error_return;
@@
+  if (len > section->size - (bfd_size_type) (p - contents))
+    { bfd_set_error (bfd_error_bad_value); goto error_return; }
   for (i = 0; i < len; i++)
     *p++ = HEX2 (buf + 2 * i);
```

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