https://sourceware.org/bugzilla/show_bug.cgi?id=34525
Bug ID: 34525
Summary: aarch64: ld: SIGSEGV in elfNN_aarch64_stub_name() when
BTI stub target section has no stub group
Product: binutils
Version: 2.47
Status: NEW
Severity: normal
Priority: P2
Component: ld
Assignee: unassigned at sourceware dot org
Reporter: jpoimboe at redhat dot com
Target Milestone: ---
Summary
=======
ld is crashing with a SIGSEGV when linking the Linux kernel with a text size of
~698MB (allyesconfig) and LDFLAGS_vmlinux="-z force-bti".
It crashes in elfNN_aarch64_stub_name() while sizing stubs, when it needs to
emit a BTI "double stub" (the landing-pad stub placed near the branch target)
and the target symbol's section has no entry in htab->stub_group[].
_bfd_aarch64_add_call_stub_entries() does
id_sec_bti = htab->stub_group[sym_sec->id].link_sec;
without validating either the index or the result, and passes it straight to
elfNN_aarch64_stub_name(), which dereferences it unconditionally.
Two distinct ways to reach this have been observed, both crashing identically:
1. sym_sec is an output section. stub_group[] is allocated with one entry per
*input* section id, so an output section's id is out of bounds and the read
returns garbage. This happens when the branch target is a symbol defined by
the linker script (PROVIDE, or a plain assignment) rather than by an input
object. The bogus pointer in the reproducer below is 0x100000040.
2. sym_sec is an input section that was never enrolled for stub grouping, so
link_sec is genuinely NULL. This happens when the branch target lives in a
section without SEC_CODE -- i.e. declared "a" rather than "ax".
The reproducer below covers both, selected by -DCASE=1 / -DCASE=2. They crash
at the same two lines, and one fix that validates the lookup covers both.
Note therefore that this is an out-of-bounds read in case 1 and a NULL
dereference in case 2.
Minimal reproducer
==================
repro.S:
.section .text.caller, "ax", @progbits
.global _start
_start:
bti c
#if CASE == 1
bl alias_target /* defined by PROVIDE() in repro.lds */
#else
bl far_target /* defined below, in a non-code section
*/
#endif
ret
#if CASE == 1
.section .far, "ax", @progbits
#else
.section .far, "a", @progbits /* alloc, NOT executable */
#endif
.global far_target
far_target:
ret /* deliberately no BTI landing pad */
repro.lds:
ENTRY(_start)
SECTIONS {
. = 0x1000;
.text.caller : { *(.text.caller) }
. = 0x1000 + 0x9000000; /* 144M: out of +/-128M branch range */
.text.far : { *(.far) }
}
/* Only takes effect in CASE 1; unreferenced in CASE 2, so it is dropped. */
PROVIDE(alias_target = far_target);
Build and link:
$ gcc -c -DCASE=1 repro.S -o repro1.o
$ ld -z force-bti -T repro.lds repro1.o -o repro1.elf
repro1.o: warning: BTI is required by -z force-bti, but this input object file
lacks the necessary property note.
Segmentation fault (core dumped)
$ gcc -c -DCASE=2 repro.S -o repro2.o
$ ld -z force-bti -T repro.lds repro2.o -o repro2.elf
repro2.o: warning: BTI is required by -z force-bti, but this input object file
lacks the necessary property note.
Segmentation fault (core dumped)
Marking the object with a GNU_PROPERTY_AARCH64_FEATURE_1_BTI note and linking
without the flag crashes identically, so the bug is not specific to -z
force-bti:
/* append to repro.S, then drop -z force-bti */
.section .note.gnu.property, "a"
.align 3
.long 2f - 1f
.long 6f - 3f
.long 5 /* NT_GNU_PROPERTY_TYPE_0 */
1: .string "GNU"
2:
.align 3
3: .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND
*/
.long 5f - 4f
4:
.long 3 /* BTI | PAC */
5:
.align 3
6:
Backtrace
=========
Minimal reproducer case 1, ld 2.47.20260726, with binutils debuginfo:
Program received signal SIGSEGV, Segmentation fault.
#0 0x0000fffff7e54574 in snprintf (__s=0xaaaaaad53cd0
"\230\017\331\367\377\377", __n=39,
__fmt=0xfffff7f4f5c8 "%08x_%s+%lx") at /usr/include/bits/stdio2.h:68
#1 elf64_aarch64_stub_name (input_section=0x100000040,
sym_sec=sym_sec@entry=0xaaaaaad3d108,
hash=hash@entry=0xaaaaaad3ea30, rel=rel@entry=0xaaaaaad57890)
at ../../bfd/elfnn-aarch64.c:3042
#2 0x0000fffff7e5c574 [PAC] in _bfd_aarch64_add_call_stub_entries (
stub_changed=<synthetic pointer>, output_bfd=<optimized out>,
info=<optimized out>)
at ../../bfd/elfnn-aarch64.c:4674
#3 elf64_aarch64_size_stubs (output_bfd=<optimized out>, stub_bfd=<optimized
out>,
info=info@entry=0xaaaaaad32fa8 <link_info>, group_size=<optimized out>,
add_stub_section=add_stub_section@entry=0xaaaaaaad29e0
<elf64_aarch64_add_stub_section>,
layout_sections_again=layout_sections_again@entry=0xaaaaaaad2f28
<gldaarch64_layout_sections_again>)
at ../../bfd/elfnn-aarch64.c:4839
#4 0x0000aaaaaaad31f8 [PAC] in gldaarch64linux_after_allocation () at
ld/eaarch64linux.c:310
#5 0x0000aaaaaaac51d0 [PAC] in ldemul_after_allocation () at
../../ld/ldemul.c:98
#6 lang_process () at ../../ld/ldlang.c:8927
#7 0x0000aaaaaaaa8eb8 [PAC] in main (argc=<optimized out>, argv=<optimized
out>)
at ../../ld/ldmain.c:971
Case 2, same crash site, with input_section a genuine NULL:
#1 elf64_aarch64_stub_name (input_section=0x0,
sym_sec=sym_sec@entry=0xaaaaaad683d0,
hash=hash@entry=0xaaaaaad3ea20, rel=rel@entry=0xaaaaaad57880)
at ../../bfd/elfnn-aarch64.c:3042
#2 0x0000fffff7e5c574 [PAC] in _bfd_aarch64_add_call_stub_entries (...)
at ../../bfd/elfnn-aarch64.c:4674
--
You are receiving this mail because:
You are on the CC list for the bug.