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.

Reply via email to