Public bug reported:
## Summary
GNU ld 2.47 crashes with SIGSEGV when an object with many global symbols is
placed between `--start-lib` and `--end-lib`. This is a use-after-free in
libbfd. It is fixed on upstream master but the fix is neither on
`binutils-2_47-branch` nor in the Ubuntu package.
Build systems now pass `--start-lib`/`--end-lib` to GNU ld because ld 2.47
supports these options. For example, Bazel's auto-configured C++ toolchain
enables its `supports_start_end_lib` feature. Many C++ projects therefore
fail to link (in my case, building `gawk` through Bazel/rules_cc_autoconf).
## Reproducer
```sh
seq -f 'int sym_%05g_with_a_reasonably_long_name(void) { return 0; }' 0 1999 >
lib.c
echo 'int lib_entry(void) { return 0; }' >> lib.c
echo 'int lib_entry(void); int main(void) { return lib_entry(); }' >
main.c
gcc -c lib.c main.c
gcc main.o -Wl,--start-lib lib.o -Wl,--end-lib -o t
```
Result:
```
collect2: fatal error: ld terminated with signal 11 [Segmentation fault]
```
Linking `main.o lib.o` without `--start-lib`/`--end-lib` works.
Under valgrind:
```
Invalid read of size 8
at 0x...: ??? (libbfd-2.47-system.so) <- elf_link_add_archive_symbols
Address ... is 24 bytes inside a block of size 32,032 free'd
at free
by objalloc_free_block
by ... <- _bfd_compute_and_push_armap
```
## Analysis
For each `--start-lib` group, ld builds a fake archive and its symbol map via
`_bfd_make_armap()` -> `_bfd_compute_and_push_armap()` (bfd/archive.c):
1. `_bfd_compute_and_push_armap()` allocates a marker:
`first_name = bfd_alloc (arch, 1)`.
2. It calls the `push_armap` callback, `_bfd_load_armap()`. This callback
allocates `ardata->symdefs` and the symbol names on the same `arch`
objalloc, after the marker.
3. On return it calls `bfd_release (arch, first_name)`, which frees
everything allocated after the marker, including the symbol map it just
built.
4. `elf_link_add_archive_symbols()` (bfd/elflink.c) then iterates over the
freed `symdefs`. Once the memory is reused, `symdef->name` holds garbage
(e.g. `0x922`) and ld crashes in
`bfd_hash_lookup` <- `bfd_link_hash_lookup` <-
`_bfd_elf_archive_symbol_lookup`.
The crash only shows up when the symbol map is large enough to sit in its
own objalloc chunk, which is actually returned to malloc and reused. Small
test cases usually link fine (but still fail under valgrind).
## Why 2000 symbols
Valgrind reports the use-after-free with as few as ~40 symbols; an actual
segfault needs the freed chunk to be reused, hence 2000 symbols in the
reproducer.
This follows from how objalloc (libiberty/objalloc.c) works. Requests below
`BIG_REQUEST` (512 bytes) are carved out of a shared ~4 KiB chunk, and
releasing them only rewinds a pointer, so the data stays readable. Larger
requests get a dedicated malloc'd chunk, which `bfd_release` actually frees.
`symdefs` takes 16 bytes per symbol:
| Global symbols | `symdefs` size | Link | Valgrind |
|----------------|----------------|----------|----------------|
| 31 | 496 B | OK | clean |
| 41 | 656 B | OK | use-after-free |
| 101 - 1001 | 1.6 - 16 KiB | OK | use-after-free |
| 2001 | 32 KiB | SIGSEGV | use-after-free |
Between ~32 and ~1000 symbols the link succeeds by luck, because the freed
chunk still holds its old contents. Only when malloc hands it back for the
object's symbol/relocation buffers does the symbol map get overwritten.
Real-world C++ objects easily reach this size.
## Upstream fix
Commit d9ab6e42e739fe826b02a5f868586132ea2d893a (Alan Modra, 2026-08-15):
"PR ld/24600: BFD: Fix use-after-free from `_bfd_load_armap'"
https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=d9ab6e42e739fe826b02a5f868586132ea2d893a
It fixes commit e34fd4bfa6d7 ("PR ld/24600: BFD: Add general linker support
for mapless archives"), which is part of 2.47. The commit applies cleanly on
top of `binutils-2_47-branch` (checked with `git merge-tree`).
The commit message says it was found in the context of CVE-2026-19548.
However, that CVE describes a different use-after-free (in
`plugin_maybe_claim`).
## Workaround
Avoid `--start-lib`/`--end-lib`, e.g. with Bazel:
```
--features=-supports_start_end_lib --host_features=-supports_start_end_lib
```
## Environment
- Ubuntu 26.10 (stonking), amd64
- binutils 2.47-6ubuntu1
- gcc 15.3.0-4ubuntu1
** Affects: binutils (Ubuntu)
Importance: Undecided
Status: New
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2172236
Title:
ld segfaults with --start-lib/--end-lib (use-after-free in
_bfd_load_armap), please backport upstream fix d9ab6e42
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/binutils/+bug/2172236/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs