Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 3b619e2

Browse files
ardbiesheuvelwilldeacon
authored andcommitted
arm64: implement dynamic shadow call stack for Clang
Implement dynamic shadow call stack support on Clang, by parsing the unwind tables at init time to locate all occurrences of PACIASP/AUTIASP instructions, and replacing them with the shadow call stack push and pop instructions, respectively. This is useful because the overhead of the shadow call stack is difficult to justify on hardware that implements pointer authentication (PAC), and given that the PAC instructions are executed as NOPs on hardware that doesn't, we can just replace them without breaking anything. As PACIASP/AUTIASP are guaranteed to be paired with respect to manipulations of the return address, replacing them 1:1 with shadow call stack pushes and pops is guaranteed to result in the desired behavior. Signed-off-by: Ard Biesheuvel <[email protected]> Reviewed-by: Sami Tolvanen <[email protected]> Tested-by: Sami Tolvanen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 9beccca commit 3b619e2

File tree

10 files changed

+342
-4
lines changed

10 files changed

+342
-4
lines changed

arch/arm64/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,15 @@ config ARCH_NR_GPIO
21602160

21612161
If unsure, leave the default value.
21622162

2163+
config UNWIND_PATCH_PAC_INTO_SCS
2164+
bool "Enable shadow call stack dynamically using code patching"
2165+
# needs Clang with https://reviews.llvm.org/D111780 incorporated
2166+
depends on CC_IS_CLANG && CLANG_VERSION >= 150000
2167+
depends on ARM64_PTR_AUTH_KERNEL && CC_HAS_BRANCH_PROT_PAC_RET
2168+
depends on SHADOW_CALL_STACK
2169+
select UNWIND_TABLES
2170+
select DYNAMIC_SCS
2171+
21632172
endmenu # "Kernel Features"
21642173

21652174
menu "Boot options"

arch/arm64/Makefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,16 @@ branch-prot-flags-$(CONFIG_CC_HAS_SIGN_RETURN_ADDRESS) := -msign-return-address=
7777
# We enable additional protection for leaf functions as there is some
7878
# narrow potential for ROP protection benefits and no substantial
7979
# performance impact has been observed.
80+
PACRET-y := pac-ret+leaf
81+
82+
# Using a shadow call stack in leaf functions is too costly, so avoid PAC there
83+
# as well when we may be patching PAC into SCS
84+
PACRET-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) := pac-ret
85+
8086
ifeq ($(CONFIG_ARM64_BTI_KERNEL),y)
81-
branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI) := -mbranch-protection=pac-ret+leaf+bti
87+
branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI) := -mbranch-protection=$(PACRET-y)+bti
8288
else
83-
branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET) := -mbranch-protection=pac-ret+leaf
89+
branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET) := -mbranch-protection=$(PACRET-y)
8490
endif
8591
# -march=armv8.3-a enables the non-nops instructions for PAC, to avoid the
8692
# compiler to generate them and consequently to break the single image contract

arch/arm64/include/asm/scs.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifdef __ASSEMBLY__
66

77
#include <asm/asm-offsets.h>
8+
#include <asm/sysreg.h>
89

910
#ifdef CONFIG_SHADOW_CALL_STACK
1011
scs_sp .req x18
@@ -24,6 +25,54 @@
2425
.endm
2526
#endif /* CONFIG_SHADOW_CALL_STACK */
2627

28+
29+
#else
30+
31+
#include <linux/scs.h>
32+
#include <asm/cpufeature.h>
33+
34+
#ifdef CONFIG_UNWIND_PATCH_PAC_INTO_SCS
35+
static inline bool should_patch_pac_into_scs(void)
36+
{
37+
u64 reg;
38+
39+
/*
40+
* We only enable the shadow call stack dynamically if we are running
41+
* on a system that does not implement PAC or BTI. PAC and SCS provide
42+
* roughly the same level of protection, and BTI relies on the PACIASP
43+
* instructions serving as landing pads, preventing us from patching
44+
* those instructions into something else.
45+
*/
46+
reg = read_sysreg_s(SYS_ID_AA64ISAR1_EL1);
47+
if (SYS_FIELD_GET(ID_AA64ISAR1_EL1, APA, reg) |
48+
SYS_FIELD_GET(ID_AA64ISAR1_EL1, API, reg))
49+
return false;
50+
51+
reg = read_sysreg_s(SYS_ID_AA64ISAR2_EL1);
52+
if (SYS_FIELD_GET(ID_AA64ISAR2_EL1, APA3, reg))
53+
return false;
54+
55+
if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) {
56+
reg = read_sysreg_s(SYS_ID_AA64PFR1_EL1);
57+
if (reg & (0xf << ID_AA64PFR1_EL1_BT_SHIFT))
58+
return false;
59+
}
60+
return true;
61+
}
62+
63+
static inline void dynamic_scs_init(void)
64+
{
65+
if (should_patch_pac_into_scs()) {
66+
pr_info("Enabling dynamic shadow call stack\n");
67+
static_branch_enable(&dynamic_scs_enabled);
68+
}
69+
}
70+
#else
71+
static inline void dynamic_scs_init(void) {}
72+
#endif
73+
74+
int scs_patch(const u8 eh_frame[], int size);
75+
2776
#endif /* __ASSEMBLY __ */
2877

2978
#endif /* _ASM_SCS_H */

arch/arm64/kernel/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o
8080
obj-$(CONFIG_ARM64_MTE) += mte.o
8181
obj-y += vdso-wrap.o
8282
obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o
83+
obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.o
84+
CFLAGS_patch-scs.o += -mbranch-protection=none
8385

8486
# Force dependency (vdso*-wrap.S includes vdso.so through incbin)
8587
$(obj)/vdso-wrap.o: $(obj)/vdso/vdso.so

arch/arm64/kernel/head.S

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,9 @@ SYM_FUNC_START_LOCAL(__primary_switched)
462462
bl early_fdt_map // Try mapping the FDT early
463463
mov x0, x20 // pass the full boot status
464464
bl init_feature_override // Parse cpu feature overrides
465+
#ifdef CONFIG_UNWIND_PATCH_PAC_INTO_SCS
466+
bl scs_patch_vmlinux
467+
#endif
465468
mov x0, x20
466469
bl finalise_el2 // Prefer VHE if possible
467470
ldp x29, x30, [sp], #16

arch/arm64/kernel/irq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static void init_irq_scs(void)
4141
{
4242
int cpu;
4343

44-
if (!IS_ENABLED(CONFIG_SHADOW_CALL_STACK))
44+
if (!scs_is_enabled())
4545
return;
4646

4747
for_each_possible_cpu(cpu)

arch/arm64/kernel/module.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
#include <linux/kernel.h>
1616
#include <linux/mm.h>
1717
#include <linux/moduleloader.h>
18+
#include <linux/scs.h>
1819
#include <linux/vmalloc.h>
1920
#include <asm/alternative.h>
2021
#include <asm/insn.h>
22+
#include <asm/scs.h>
2123
#include <asm/sections.h>
2224

2325
void *module_alloc(unsigned long size)
@@ -514,5 +516,11 @@ int module_finalize(const Elf_Ehdr *hdr,
514516
if (s)
515517
apply_alternatives_module((void *)s->sh_addr, s->sh_size);
516518

519+
if (scs_is_dynamic()) {
520+
s = find_section(hdr, sechdrs, ".init.eh_frame");
521+
if (s)
522+
scs_patch((void *)s->sh_addr, s->sh_size);
523+
}
524+
517525
return module_init_ftrace_plt(hdr, sechdrs, me);
518526
}

0 commit comments

Comments
 (0)