https://sourceware.org/bugzilla/show_bug.cgi?id=34659
Bug ID: 34659
Summary: Heap-buffer-overflow read in native SFrame FDE
decoding (`libsframe/sframe.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 17018
--> https://sourceware.org/bugzilla/attachment.cgi?id=17018&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
The native-endian SFrame decoder validates only broad header relationships
before treating header offsets and counts as pointers into the input buffer. In
version 3, `sframe_fde_tbl_init` reads an FDE index and then an attribute
structure at an attacker-controlled FRE-relative offset, without checking
either range.
```c
const sframe_func_desc_idx_v3 *fdep =
(sframe_func_desc_idx_v3 *) fde_buf + i;
const sframe_func_desc_attr_v3 *fattr =
(sframe_func_desc_attr_v3 *) (fre_buf + fdep->sfdi_func_start_fre_off);
fde_tbl->entry[i].func_num_fres = fattr->sfda_func_num_fres;
```
A 41-byte `.sframe` section supplies an FDE/FRE layout that passes the header
check but positions the referenced FDE attribute outside the input 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 used 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. Place the supplied `gen_sframe_fde_oob.py` in a writable directory and
generate the ELF file:
```sh
export WORK="$PWD/poc-work"
mkdir -p "$WORK"
python3 gen_sframe_fde_oob.py "$WORK/sframe_fde_oob.elf"
```
4. Trigger the fault:
```sh
ASAN_OPTIONS=detect_leaks=0:abort_on_error=1 \\
"$BUILD/binutils/objdump" --sframe "$WORK/sframe_fde_oob.elf"
```
## Sanitizer report
The following is the complete, unmodified contents of `sanitizer_report.txt`.
```text
=================================================================
==446226==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x50400000074c at pc 0x5c5b1fb0739d bp 0x7ffe9998e2d0 sp 0x7ffe9998e2c0
READ of size 8 at 0x50400000074c thread T0
#0 0x5c5b1fb0739c in sframe_fde_tbl_init ../../libsframe/sframe.c:153
#1 0x5c5b1fb0e7e5 in sframe_decode ../../libsframe/sframe.c:1493
#2 0x5c5b1f6fbb38 in display_sframe ../../binutils/dwarf.c:8986
#3 0x5c5b1f6b8e07 in dump_sframe_section ../../binutils/objdump.c:5071
#4 0x5c5b1f6bd01c in dump_bfd ../../binutils/objdump.c:5900
#5 0x5c5b1f6bd374 in display_object_bfd ../../binutils/objdump.c:5971
#6 0x5c5b1f6bd696 in display_any_bfd ../../binutils/objdump.c:6050
#7 0x5c5b1f6bd706 in display_file ../../binutils/objdump.c:6071
#8 0x5c5b1f6bf222 in main ../../binutils/objdump.c:6494
#9 0x7e7a5702a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
#10 0x7e7a5702a28a in __libc_start_main_impl ../csu/libc-start.c:360
#11 0x5c5b1f6a2374 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)
0x50400000074c is located 19 bytes after 41-byte region
[0x504000000710,0x504000000739)
allocated by thread T0 here:
#0 0x7e7a574fd9c7 in malloc
../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x5c5b1fad2864 in xmalloc ../../libiberty/xmalloc.c:149
#2 0x5c5b1f6b52ae in load_specific_debug_section
../../binutils/objdump.c:4334
#3 0x5c5b1f6b8d89 in dump_sframe_section ../../binutils/objdump.c:5066
#4 0x5c5b1f6bd01c in dump_bfd ../../binutils/objdump.c:5900
#5 0x5c5b1f6bd374 in display_object_bfd ../../binutils/objdump.c:5971
#6 0x5c5b1f6bd696 in display_any_bfd ../../binutils/objdump.c:6050
#7 0x5c5b1f6bd706 in display_file ../../binutils/objdump.c:6071
#8 0x5c5b1f6bf222 in main ../../binutils/objdump.c:6494
#9 0x7e7a5702a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
#10 0x7e7a5702a28a in __libc_start_main_impl ../csu/libc-start.c:360
#11 0x5c5b1f6a2374 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 ../../libsframe/sframe.c:153 in
sframe_fde_tbl_init
Shadow bytes around the buggy address:
0x504000000480: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
0x504000000500: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
0x504000000580: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
0x504000000600: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fa
0x504000000680: fa fa 00 00 00 00 06 fa fa fa 00 00 00 00 01 fa
=>0x504000000700: fa fa 00 00 00 00 00 01 fa[fa]fa fa fa fa fa fa
0x504000000780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x504000000800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x504000000880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x504000000900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x504000000980: 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
==446226==ABORTING
```
## Potential fix
Validate the full on-disk SFrame layout before deriving FDE/FRE pointers, and
pass explicit FDE/FRE buffer lengths into the decoder. Each FDE's FRE-relative
attribute offset must fit in the FRE area.
```diff
diff --git a/libsframe/sframe.c b/libsframe/sframe.c
@@
- sframe_fde_tbl_init (fde_tbl, frame_buf + fdeoff, frame_buf + freoff,
- &fidx_size, num_fdes, ver);
+ if (!sframe_section_sanity_check_p (frame_buf, sf_size, dhp))
+ goto decode_fail_free;
+ sframe_fde_tbl_init (fde_tbl, fde_buf, fde_len, fre_buf, fre_len,
+ &fidx_size, num_fdes, ver);
@@
+ if (fre_offset > fre_buf_size
+ || sizeof (*fattr) > fre_buf_size - fre_offset)
+ return SFRAME_ERR;
```
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.