x86/ibt: Disable IBT around firmware
authorPeter Zijlstra <peterz@infradead.org>
Tue, 8 Mar 2022 15:30:38 +0000 (16:30 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Tue, 15 Mar 2022 09:32:40 +0000 (10:32 +0100)
Assume firmware isn't IBT clean and disable it across calls.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20220308154318.759989383@infradead.org
arch/x86/include/asm/efi.h
arch/x86/include/asm/ibt.h
arch/x86/kernel/apm_32.c
arch/x86/kernel/cpu/common.c

index 03cb127..98938a6 100644 (file)
@@ -7,6 +7,7 @@
 #include <asm/tlb.h>
 #include <asm/nospec-branch.h>
 #include <asm/mmu_context.h>
+#include <asm/ibt.h>
 #include <linux/build_bug.h>
 #include <linux/kernel.h>
 #include <linux/pgtable.h>
@@ -120,8 +121,12 @@ extern asmlinkage u64 __efi_call(void *fp, ...);
        efi_enter_mm();                                                 \
 })
 
-#define arch_efi_call_virt(p, f, args...)                              \
-       efi_call((void *)p->f, args)                                    \
+#define arch_efi_call_virt(p, f, args...) ({                           \
+       u64 ret, ibt = ibt_save();                                      \
+       ret = efi_call((void *)p->f, args);                             \
+       ibt_restore(ibt);                                               \
+       ret;                                                            \
+})
 
 #define arch_efi_call_virt_teardown()                                  \
 ({                                                                     \
index fcaf6a4..52fb05d 100644 (file)
@@ -52,6 +52,9 @@ static inline bool is_endbr(u32 val)
        return val == gen_endbr();
 }
 
+extern __noendbr u64 ibt_save(void);
+extern __noendbr void ibt_restore(u64 save);
+
 #else /* __ASSEMBLY__ */
 
 #ifdef CONFIG_X86_64
@@ -74,6 +77,9 @@ static inline bool is_endbr(u32 val)
 
 static inline bool is_endbr(u32 val) { return false; }
 
+static inline u64 ibt_save(void) { return 0; }
+static inline void ibt_restore(u64 save) { }
+
 #else /* __ASSEMBLY__ */
 
 #define ENDBR
index 241dda6..60e330c 100644 (file)
 #include <asm/paravirt.h>
 #include <asm/reboot.h>
 #include <asm/nospec-branch.h>
+#include <asm/ibt.h>
 
 #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 extern int (*console_blank_hook)(int);
@@ -598,6 +599,7 @@ static long __apm_bios_call(void *_call)
        struct desc_struct      save_desc_40;
        struct desc_struct      *gdt;
        struct apm_bios_call    *call = _call;
+       u64                     ibt;
 
        cpu = get_cpu();
        BUG_ON(cpu != 0);
@@ -607,11 +609,13 @@ static long __apm_bios_call(void *_call)
 
        apm_irq_save(flags);
        firmware_restrict_branch_speculation_start();
+       ibt = ibt_save();
        APM_DO_SAVE_SEGS;
        apm_bios_call_asm(call->func, call->ebx, call->ecx,
                          &call->eax, &call->ebx, &call->ecx, &call->edx,
                          &call->esi);
        APM_DO_RESTORE_SEGS;
+       ibt_restore(ibt);
        firmware_restrict_branch_speculation_end();
        apm_irq_restore(flags);
        gdt[0x40 / 8] = save_desc_40;
@@ -676,6 +680,7 @@ static long __apm_bios_call_simple(void *_call)
        struct desc_struct      save_desc_40;
        struct desc_struct      *gdt;
        struct apm_bios_call    *call = _call;
+       u64                     ibt;
 
        cpu = get_cpu();
        BUG_ON(cpu != 0);
@@ -685,10 +690,12 @@ static long __apm_bios_call_simple(void *_call)
 
        apm_irq_save(flags);
        firmware_restrict_branch_speculation_start();
+       ibt = ibt_save();
        APM_DO_SAVE_SEGS;
        error = apm_bios_call_simple_asm(call->func, call->ebx, call->ecx,
                                         &call->eax);
        APM_DO_RESTORE_SEGS;
+       ibt_restore(ibt);
        firmware_restrict_branch_speculation_end();
        apm_irq_restore(flags);
        gdt[0x40 / 8] = save_desc_40;
index 709acab..03bd73f 100644 (file)
@@ -517,6 +517,34 @@ static __init int setup_disable_pku(char *arg)
 __setup("nopku", setup_disable_pku);
 #endif /* CONFIG_X86_64 */
 
+#ifdef CONFIG_X86_KERNEL_IBT
+
+__noendbr u64 ibt_save(void)
+{
+       u64 msr = 0;
+
+       if (cpu_feature_enabled(X86_FEATURE_IBT)) {
+               rdmsrl(MSR_IA32_S_CET, msr);
+               wrmsrl(MSR_IA32_S_CET, msr & ~CET_ENDBR_EN);
+       }
+
+       return msr;
+}
+
+__noendbr void ibt_restore(u64 save)
+{
+       u64 msr;
+
+       if (cpu_feature_enabled(X86_FEATURE_IBT)) {
+               rdmsrl(MSR_IA32_S_CET, msr);
+               msr &= ~CET_ENDBR_EN;
+               msr |= (save & CET_ENDBR_EN);
+               wrmsrl(MSR_IA32_S_CET, msr);
+       }
+}
+
+#endif
+
 static __always_inline void setup_cet(struct cpuinfo_x86 *c)
 {
        u64 msr = CET_ENDBR_EN;