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

            Bug ID: 34663
           Summary: Heap-buffer-overflow write in STABS base-class parsing
                    (`binutils/stabs.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 17022
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17022&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

`parse_stab_baseclasses` parses a potentially wide STABS numeric base-class
count and truncates it to `unsigned int`. The allocation expression then
evaluates `(c + 1) * sizeof (*classes)` after wraparound. A count of `-1` is
converted to `UINT_MAX`, yielding a tiny allocation; the parser writes
base-class pointers at indexes derived from the original count.

```c
c = (unsigned int) parse_number (...);
classes = debug_xalloc (dhandle, (c + 1) * sizeof (*classes));
...
classes[i] = debug_make_baseclass (...);
```

## 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.

## 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. Either use the supplied `stabpoc.o`, or place `gen_stab.py` in a writable
directory and regenerate it:

   ```sh
   export WORK="$PWD/poc-work"
   mkdir -p "$WORK"
   python3 gen_stab.py 2 "$WORK/stabpoc.o"
   ```

4. Trigger the fault:

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

## Sanitizer report

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

```text
=================================================================
==1799166==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x5210000088e0 at pc 0x56da2a40f32b bp 0x7fff0eef45b0 sp 0x7fff0eef45a0
WRITE of size 8 at 0x5210000088e0 thread T0
    #0 0x56da2a40f32a in parse_stab_baseclasses ../../binutils/stabs.c:2288
    #1 0x56da2a40ea3d in parse_stab_struct_type ../../binutils/stabs.c:2149
    #2 0x56da2a40c879 in parse_stab_type ../../binutils/stabs.c:1632
    #3 0x56da2a409c46 in parse_stab_string ../../binutils/stabs.c:922
    #4 0x56da2a408c67 in parse_stab ../../binutils/stabs.c:688
    #5 0x56da2a3f7044 in read_section_stabs_debugging_info
../../binutils/rddbg.c:244
    #6 0x56da2a3f60ae in read_debugging_info ../../binutils/rddbg.c:59
    #7 0x56da2a3850e6 in dump_bfd ../../binutils/objdump.c:5917
    #8 0x56da2a385374 in display_object_bfd ../../binutils/objdump.c:5971
    #9 0x56da2a385696 in display_any_bfd ../../binutils/objdump.c:6050
    #10 0x56da2a385706 in display_file ../../binutils/objdump.c:6071
    #11 0x56da2a387222 in main ../../binutils/objdump.c:6494
    #12 0x710eaf62a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #13 0x710eaf62a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #14 0x56da2a36a374 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)

0x5210000088e0 is located 0 bytes after 4064-byte region
[0x521000007900,0x5210000088e0)
allocated by thread T0 here:
    #0 0x710eafafd9c7 in malloc
../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x56da2a78f942 in _objalloc_alloc ../../libiberty/objalloc.c:159
    #2 0x56da2a4dc501 in bfd_alloc ../../bfd/libbfd.c:453
    #3 0x56da2a55080e in bfd_elf64_object_p ../../bfd/elfcode.h:717
    #4 0x56da2a4d7ae8 in bfd_check_format_matches ../../bfd/format.c:547
    #5 0x56da2a38535c in display_object_bfd ../../binutils/objdump.c:5969
    #6 0x56da2a385696 in display_any_bfd ../../binutils/objdump.c:6050
    #7 0x56da2a385706 in display_file ../../binutils/objdump.c:6071
    #8 0x56da2a387222 in main ../../binutils/objdump.c:6494
    #9 0x710eaf62a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
    #10 0x710eaf62a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #11 0x56da2a36a374 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/stabs.c:2288 in
parse_stab_baseclasses
Shadow bytes around the buggy address:
  0x521000008600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x521000008680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x521000008700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x521000008780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x521000008800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x521000008880: 00 00 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa
  0x521000008900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x521000008980: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x521000008a00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x521000008a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x521000008b00: 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
==1799166==ABORTING
```

## Potential fix

Keep the parsed value wide until it has been checked against both the `unsigned
int` destination and the allocation's multiplication/addition bounds. Reject
invalid STABS instead of truncating the count.

```diff
diff --git a/binutils/stabs.c b/binutils/stabs.c
@@
-  c = (unsigned int) parse_number (...);
+  bfd_vma count = parse_number (...);
+  if (count >= UINT_MAX
+      || count > ((size_t) -1 / sizeof (*classes)) - 1)
+    { bad_stab (orig); return false; }
+  c = (unsigned int) count;
```

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