Skip to content

[AArch64] KASAN asan.module_ctor does not inherit "frame-pointer"="all" or uwtable, emits str x30 instead of stp x29, x30 #188234

@sburdka

Description

@sburdka

When targeting aarch64-unknown-none with -Zsanitizer=kernel-address, -Cforce-frame-pointers=yes, and -Cforce-unwind-tables=y, all user functions correctly receive "frame-pointer"="all" + uwtable attributes and emit stp x29, x30 prologues.

However, asan.module_ctor and asan.module_dtor synthesised by LLVM's missing both "frame-pointer" and uwtable — and emit str x30 (LR-only, no frame record).

Reproducing Issue

minimal_repro.rs

#![no_std]
#![allow(missing_docs)]
use core::sync::atomic::{AtomicUsize, Ordering};

#[panic_handler]
fn panic(_: &core::panic::PanicInfo<'_>) -> ! { loop {} }

static COUNTER: AtomicUsize = AtomicUsize::new(0);

#[no_mangle]
pub extern "C" fn use_global() -> usize {
    COUNTER.fetch_add(1, Ordering::Relaxed)
}

Build

rustc --edition=2021 --crate-type=lib --target=aarch64-unknown-none \
  -Cpanic=abort -Copt-level=2 -Crelocation-model=static \
  -Cembed-bitcode=n -Clto=n -Ccodegen-units=1 \
  -Csymbol-mangling-version=v0 -Zfunction-sections=n \
  -Ctarget-feature="-neon" \
  -Cforce-unwind-tables=y -Zuse-sync-unwind=n \
  -Zbranch-protection=pac-ret -Zfixed-x18 \
  -Cforce-frame-pointers=yes \
  -Zsanitizer=kernel-address \
  -o repro.o --emit=obj minimal_repro.rs

Check:

llvm-objdump --mattr=+v8.3a -d repro.o

Actual disassembly output

Full disassembly attached as c.

use_global — CORRECT (stp x29, x30, full frame record):

0000000000000010 <use_global>:
      10: 3f 23 03 d5   paciasp
      14: fd 7b bf a9   stp     x29, x30, [sp, #-16]!   ← CORRECT
      18: fd 03 00 91   mov     x29, sp
      1c: 08 00 00 90   adrp    x8, 0x0
      20: 08 01 00 91   add     x8, x8, #0
      24: 00 7d 5f c8   ldxr    x0, [x8]
      28: 09 04 00 91   add     x9, x0, #1
      2c: 09 7d 0a c8   stxr    w10, x9, [x8]
      30: aa ff ff 35   cbnz    w10, 0x24
      34: fd 7b c1 a8   ldp     x29, x30, [sp], #16
      38: bf 23 03 d5   autiasp
      3c: c0 03 5f d6   ret

asan.module_ctor (str x30, no frame record):

0000000000000000 <asan.module_ctor>:
       0: 3f 23 03 d5   paciasp
       4: fe 0f 1f f8   str     x30, [sp, #-16]!        ← Frame pointed omitted
       8: 00 00 00 90   adrp    x0, 0x0
       c: 00 00 00 91   add     x0, x0, #0
      10: 21 00 80 52   mov     w1, #1
      14: 00 00 00 94   bl      __asan_register_globals
      18: fe 07 41 f8   ldr     x30, [sp], #16
      1c: bf 23 03 d5   autiasp
      20: c0 03 5f d6   ret

asan.module_dtor

0000000000000000 <asan.module_dtor>:
       0: 3f 23 03 d5   paciasp
       4: fe 0f 1f f8   str     x30, [sp, #-16]!        ← Frame pointed omitted
       8: 00 00 00 90   adrp    x0, 0x0
       c: 00 00 00 91   add     x0, x0, #0
      10: 21 00 80 52   mov     w1, #1
      14: 00 00 00 94   bl      __asan_unregister_globals
      18: fe 07 41 f8   ldr     x30, [sp], #16
      1c: bf 23 03 d5   autiasp
      20: c0 03 5f d6   ret

disasm.s: disasm.txt
repro.o: repro_obj.txt

LLVM IR: the attribute mismatch

Comamnd used

rustc --edition=2021 --crate-type=lib --target=aarch64-unknown-none \ 
-Cpanic=abort -Copt-level=2 -Crelocation-model=static -Cembed-bitcode=n \
-Clto=n -Ccodegen-units=1 -Csymbol-mangling-version=v0 -Zfunction-sections=n \
-Ctarget-feature=-neon -Cforce-unwind-tables=y -Zuse-sync-unwind=n \
-Zbranch-protection=pac-ret -Zfixed-x18 -Cforce-frame-pointers=yes \
-Zsanitizer=kernel-address --emit=llvm-ir -o /tmp/asan-fp-repro/repro.ll \
./minimal_repro.rs

Full IR attached as repro.ll.

User functions use attribute groups #0 and #1, which have "frame-pointer"="all" and uwtable. The synthesised asan.module_ctor / asan.module_dtor use attribute group #2 which has only nounwind:

; asan.module_ctor — uses #2:
define internal void @asan.module_ctor() #2 {
  call void @__asan_register_globals(i64 ptrtoint (ptr @0 to i64), i64 1)
  ret void
}

; asan.module_dtor — uses #2:
define internal void @asan.module_dtor() #2 {
  call void @__asan_unregister_globals(i64 ptrtoint (ptr @0 to i64), i64 1)
  ret void
}

; User function attributes — CORRECT (has frame-pointer + uwtable):
attributes #0 = { nofree norecurse noredzone noreturn nosync nounwind
  sanitize_address memory(none) uwtable "frame-pointer"="all"
  "probe-stack"="inline-asm" "target-cpu"="generic"
  "target-features"="+v8a,+strict-align,+neon,+fp-armv8,-neon,-fp-armv8,+reserve-x18" }

attributes #1 = { mustprogress nofree norecurse noredzone nounwind
  sanitize_address willreturn
  memory(readwrite, argmem: none, inaccessiblemem: none) uwtable
  "frame-pointer"="all" "probe-stack"="inline-asm"
  "target-cpu"="generic"
  "target-features"="+v8a,+strict-align,+neon,+fp-armv8,-neon,-fp-armv8,+reserve-x18" }

; asan.module_ctor/dtor attributes — BUG (no frame-pointer, no uwtable):
attributes #2 = { nounwind }

repro.ll: repro_ll.txt

Since asan.module_ctor and asan.module_dtor lack the uwtable attribute (their attribute group 2 = { nounwind } has neither uwtable nor "frame-pointer"="all"), Backend does not emit any .eh_frame FDE (Frame Description Entry) for these functions. This means no Call Frame Information (CFI) directives are generated at all — including, critically, no DW_CFA_AARCH64_negate_ra_state.

Dwarf generated for repro.o (asan.module_ctor/asan.module_dtor)

00000014 0000001c 00000018 FDE cie=00000000 pc=00000000...00000010
  Format:       DWARF32
  DW_CFA_advance_loc: 4 to 0x4
  DW_CFA_AARCH64_negate_ra_state:
  DW_CFA_advance_loc: 4 to 0x8
  DW_CFA_def_cfa_offset: +16
  DW_CFA_advance_loc: 4 to 0xc
  DW_CFA_def_cfa: W29 +16
  DW_CFA_offset: W30 -8
  DW_CFA_offset: W29 -16
  DW_CFA_nop:
  DW_CFA_nop:

  0x0: CFA=WSP
  0x4: CFA=WSP: reg34=1
  0x8: CFA=WSP+16: reg34=1
  0xc: CFA=W29+16: W29=[CFA-16], W30=[CFA-8], reg34=1

dwarf_dump.txt

How can we force asan_ctor to use a frame pointer and generate proper unwind tables so that we get DW_CFA_AARCH64_negate_ra_state?

@vitalybuka Could you take look at this?
This issue actually breaks arm64 shadow call patching and PAC aware unwinding in Linux kernel.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions