With CONFIG_CFI_CLANG, the compiler replaces function addresses in C code with jump table addresses. To avoid referring to jump tables in entry code with PTI, disable CFI for IDT and paravirt code, and use function_nocfi() to prevent jump table addresses from being added to the IDT or system call entry points.
Reported-by: Sedat Dilek <sedat.di...@gmail.com> Signed-off-by: Sami Tolvanen <samitolva...@google.com> Tested-by: Sedat Dilek <sedat.di...@gmail.com> --- arch/x86/include/asm/desc.h | 8 +++++++- arch/x86/kernel/Makefile | 3 +++ arch/x86/kernel/cpu/common.c | 8 ++++---- arch/x86/kernel/idt.c | 2 +- arch/x86/kernel/traps.c | 2 +- arch/x86/xen/Makefile | 2 ++ 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h index 476082a83d1c..96666256eec2 100644 --- a/arch/x86/include/asm/desc.h +++ b/arch/x86/include/asm/desc.h @@ -381,7 +381,13 @@ static inline void set_desc_limit(struct desc_struct *desc, unsigned long limit) desc->limit1 = (limit >> 16) & 0xf; } -void alloc_intr_gate(unsigned int n, const void *addr); +/* + * Use function_nocfi() to prevent the compiler from replacing function + * addresses with jump table addresses when CONFIG_CFI_CLANG is enabled. + */ +#define alloc_intr_gate(n, addr) __alloc_intr_gate(n, function_nocfi(addr)) + +void __alloc_intr_gate(unsigned int n, const void *addr); static inline void init_idt_data(struct idt_data *data, unsigned int n, const void *addr) diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 0704c2a94272..12a739eb208e 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -46,6 +46,9 @@ endif # non-deterministic coverage. KCOV_INSTRUMENT := n +CFLAGS_REMOVE_idt.o := $(CC_FLAGS_CFI) +CFLAGS_REMOVE_paravirt.o := $(CC_FLAGS_CFI) + CFLAGS_head$(BITS).o += -fno-stack-protector CFLAGS_irq.o := -I $(srctree)/$(src)/../include/asm/trace diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 6bdb69a9a7dc..e6cff98b182a 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1752,10 +1752,10 @@ DEFINE_PER_CPU(unsigned long, cpu_current_top_of_stack) = TOP_OF_INIT_STACK; void syscall_init(void) { wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS); - wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64); + wrmsrl(MSR_LSTAR, (unsigned long)function_nocfi(entry_SYSCALL_64)); #ifdef CONFIG_IA32_EMULATION - wrmsrl(MSR_CSTAR, (unsigned long)entry_SYSCALL_compat); + wrmsrl(MSR_CSTAR, (unsigned long)function_nocfi(entry_SYSCALL_compat)); /* * This only works on Intel CPUs. * On AMD CPUs these MSRs are 32-bit, CPU truncates MSR_IA32_SYSENTER_EIP. @@ -1765,9 +1765,9 @@ void syscall_init(void) wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS); wrmsrl_safe(MSR_IA32_SYSENTER_ESP, (unsigned long)(cpu_entry_stack(smp_processor_id()) + 1)); - wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)entry_SYSENTER_compat); + wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)function_nocfi(entry_SYSENTER_compat)); #else - wrmsrl(MSR_CSTAR, (unsigned long)ignore_sysret); + wrmsrl(MSR_CSTAR, (unsigned long)function_nocfi(ignore_sysret)); wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)GDT_ENTRY_INVALID_SEG); wrmsrl_safe(MSR_IA32_SYSENTER_ESP, 0ULL); wrmsrl_safe(MSR_IA32_SYSENTER_EIP, 0ULL); diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c index d552f177eca0..6574a742e373 100644 --- a/arch/x86/kernel/idt.c +++ b/arch/x86/kernel/idt.c @@ -340,7 +340,7 @@ void idt_invalidate(void *addr) load_idt(&idt); } -void __init alloc_intr_gate(unsigned int n, const void *addr) +void __init __alloc_intr_gate(unsigned int n, const void *addr) { if (WARN_ON(n < FIRST_SYSTEM_VECTOR)) return; diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 853ea7a80806..54616dc49116 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -403,7 +403,7 @@ DEFINE_IDTENTRY_DF(exc_double_fault) * which is what the stub expects, given that the faulting * RIP will be the IRET instruction. */ - regs->ip = (unsigned long)asm_exc_general_protection; + regs->ip = (unsigned long)function_nocfi(asm_exc_general_protection); regs->sp = (unsigned long)&gpregs->orig_ax; return; diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 40b5779fce21..61e2d9b2fa43 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -11,6 +11,8 @@ endif CFLAGS_enlighten_pv.o := -fno-stack-protector CFLAGS_mmu_pv.o := -fno-stack-protector +CFLAGS_REMOVE_enlighten_pv.o := $(CC_FLAGS_CFI) + obj-y += enlighten.o obj-y += mmu.o obj-y += time.o -- 2.31.1.368.gbe11c130af-goog