On Tue, 31 Mar 2026, at 13:04, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <[email protected]>
>
> Implement arm64 support for the 'unoptimized' static call variety, which
> routes all calls through a trampoline that performs a tail call to the
> chosen function, and wire it up for use when kCFI is enabled. This works
> around an issue with kCFI and generic static calls, where the prototypes
> of default handlers such as __static_call_nop() and __static_call_ret0()
> don't match the expected prototype of the call site, resulting in kCFI
> false positives [0].
>
> Since static call targets may be located in modules loaded out of direct
> branching range, this needs a ADRP/ADD pair to load the branch target

Sashiko correctly points out that this should say ADRP/LDR rather than
ADRP/ADD, and this means that the sequence is in fact different from the
one used by modules.

> into R16 and a branch-to-register (BR) instruction to perform an
> indirect call. This is the exact code sequence that is used by modules
> when the call target is out of direct branching range.
>

... so please drop this last sentence when applying.


> Unlike on x86, there is no pressing need on arm64 to avoid indirect
> calls at all cost, but hiding it from the compiler as is done here does
> have some benefits:
> - the literal is located in .rodata, which gives us the same robustness
>   advantage that code patching does;
> - no D-cache pollution from fetching hash values from .text sections.
>
> From an execution speed PoV, this is unlikely to make any difference at
> all.
>
> [0] https://lore.kernel.org/all/[email protected]/
>
> Cc: Carlos Llamas <[email protected]>
> Cc: Sami Tolvanen <[email protected]>
> Cc: Sean Christopherson <[email protected]>
> Cc: Kees Cook <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Cc: Will McVicker <[email protected]>
> Signed-off-by: Ard Biesheuvel <[email protected]>
> ---
> v8: Simplify the trampoline by combining the NULL and RET0 cases, and
>     dropping the conditional branch and return
> v7: https://lore.kernel.org/all/[email protected]/
>
>  arch/arm64/Kconfig                   |  1 +
>  arch/arm64/include/asm/static_call.h | 31 ++++++++++++++++++++
>  arch/arm64/kernel/Makefile           |  1 +
>  arch/arm64/kernel/static_call.c      | 23 +++++++++++++++
>  arch/arm64/kernel/vmlinux.lds.S      |  1 +
>  5 files changed, 57 insertions(+)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 38dba5f7e4d2..9ea19b74b6c3 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -252,6 +252,7 @@ config ARM64
>       select HAVE_RSEQ
>       select HAVE_RUST if RUSTC_SUPPORTS_ARM64
>       select HAVE_STACKPROTECTOR
> +     select HAVE_STATIC_CALL if CFI
>       select HAVE_SYSCALL_TRACEPOINTS
>       select HAVE_KPROBES
>       select HAVE_KRETPROBES
> diff --git a/arch/arm64/include/asm/static_call.h 
> b/arch/arm64/include/asm/static_call.h
> new file mode 100644
> index 000000000000..b73960c949e4
> --- /dev/null
> +++ b/arch/arm64/include/asm/static_call.h
> @@ -0,0 +1,31 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_STATIC_CALL_H
> +#define _ASM_STATIC_CALL_H
> +
> +#define __ARCH_DEFINE_STATIC_CALL_TRAMP(name, target)                    \
> +     asm("   .pushsection .static_call.text, \"ax\"          \n" \
> +         "   .align  4                                       \n" \
> +         "   .globl  " name "                                \n" \
> +         name ":                                             \n" \
> +         "   hint    34      /* BTI C */                     \n" \
> +         "   adrp    x16, 1f                                 \n" \
> +         "   ldr     x16, [x16, :lo12:1f]                    \n" \
> +         "   br      x16                                     \n" \
> +         "   .type   " name ", %function                     \n" \
> +         "   .size   " name ", . - " name "                  \n" \
> +         "   .popsection                                     \n" \
> +         "   .pushsection .rodata, \"a\"                     \n" \
> +         "   .align  3                                       \n" \
> +         "1: .quad   " #target "                             \n" \
> +         "   .popsection                                     \n")
> +
> +#define ARCH_DEFINE_STATIC_CALL_TRAMP(name, func)                    \
> +     __ARCH_DEFINE_STATIC_CALL_TRAMP(STATIC_CALL_TRAMP_STR(name), #func)
> +
> +#define ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name)                     \
> +     ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0)
> +
> +#define ARCH_DEFINE_STATIC_CALL_RET0_TRAMP(name)                     \
> +     ARCH_DEFINE_STATIC_CALL_TRAMP(name, __static_call_return0)
> +
> +#endif /* _ASM_STATIC_CALL_H */
> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index 76f32e424065..fe627100d199 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -46,6 +46,7 @@ obj-$(CONFIG_MODULES)                       += module.o 
> module-plts.o
>  obj-$(CONFIG_PERF_EVENTS)            += perf_regs.o perf_callchain.o
>  obj-$(CONFIG_HARDLOCKUP_DETECTOR_PERF)       += watchdog_hld.o
>  obj-$(CONFIG_HAVE_HW_BREAKPOINT)     += hw_breakpoint.o
> +obj-$(CONFIG_HAVE_STATIC_CALL)               += static_call.o
>  obj-$(CONFIG_CPU_PM)                 += sleep.o suspend.o
>  obj-$(CONFIG_KGDB)                   += kgdb.o
>  obj-$(CONFIG_EFI)                    += efi.o efi-rt-wrapper.o
> diff --git a/arch/arm64/kernel/static_call.c 
> b/arch/arm64/kernel/static_call.c
> new file mode 100644
> index 000000000000..8b3a19e10871
> --- /dev/null
> +++ b/arch/arm64/kernel/static_call.c
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/static_call.h>
> +#include <linux/memory.h>
> +#include <asm/text-patching.h>
> +
> +void arch_static_call_transform(void *site, void *tramp, void *func, 
> bool tail)
> +{
> +     u64 literal;
> +     int ret;
> +
> +     if (!func)
> +             func = __static_call_return0;
> +
> +     /* decode the instructions to discover the literal address */
> +     literal = ALIGN_DOWN((u64)tramp + 4, SZ_4K) +
> +               aarch64_insn_adrp_get_offset(le32_to_cpup(tramp + 4)) +
> +               8 * aarch64_insn_decode_immediate(AARCH64_INSN_IMM_12,
> +                                                 le32_to_cpup(tramp + 8));
> +
> +     ret = aarch64_insn_write_literal_u64((void *)literal, (u64)func);
> +     WARN_ON_ONCE(ret);
> +}
> +EXPORT_SYMBOL_GPL(arch_static_call_transform);
> diff --git a/arch/arm64/kernel/vmlinux.lds.S 
> b/arch/arm64/kernel/vmlinux.lds.S
> index 2964aad0362e..2d1e75263f03 100644
> --- a/arch/arm64/kernel/vmlinux.lds.S
> +++ b/arch/arm64/kernel/vmlinux.lds.S
> @@ -191,6 +191,7 @@ SECTIONS
>                       LOCK_TEXT
>                       KPROBES_TEXT
>                       HYPERVISOR_TEXT
> +                     STATIC_CALL_TEXT
>                       *(.gnu.warning)
>       }
> 
> -- 
> 2.53.0.1018.g2bb0e51243-goog

Reply via email to