lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 19 Apr 2022 17:42:31 -0700
From:   joao@...rdrivepizza.com
To:     linux-kernel@...r.kernel.org, linux-hardening@...r.kernel.org
Cc:     joao@...rdrivepizza.com, peterz@...radead.org, jpoimboe@...hat.com,
        andrew.cooper3@...rix.com, keescook@...omium.org,
        samitolvanen@...gle.com, mark.rutland@....com, hjl.tools@...il.com,
        alyssa.milburn@...ux.intel.com, ndesaulniers@...gle.com,
        gabriel.gomes@...ux.intel.com, rick.p.edgecombe@...el.com
Subject: [RFC PATCH 01/11] x86: kernel FineIBT

From: Joao Moreira <joao@...rdrivepizza.com>

Make kernel code compatible to be compiled with FineIBT.
- Set FineIBT defines.
- Mark functions reached from assembly as coarse-grained.
- Add FineIBT handler function, which is invoked on policy violations.

Signed-off-by: Joao Moreira <joao@...rdrivepizza.com>
---
 arch/x86/entry/entry_64.S            |   1 +
 arch/x86/include/asm/ibt.h           |  16 ++++
 arch/x86/include/asm/setup.h         |  12 +--
 arch/x86/include/asm/special_insns.h |   4 +-
 arch/x86/kernel/cpu/common.c         |   2 +-
 arch/x86/kernel/fineibt.c            | 123 +++++++++++++++++++++++++++
 arch/x86/kernel/head64.c             |  12 +--
 arch/x86/kernel/smpboot.c            |   2 +-
 8 files changed, 156 insertions(+), 16 deletions(-)
 create mode 100644 arch/x86/kernel/fineibt.c

diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 4faac48ebec5..901b702fb16d 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -295,6 +295,7 @@ SYM_CODE_START(ret_from_fork)
 	/* kernel thread */
 	UNWIND_HINT_EMPTY
 	movq	%r12, %rdi
+	FINEIBT_HASH(0x89f22991)
 	CALL_NOSPEC rbx
 	/*
 	 * A kernel thread is allowed to return here after successfully
diff --git a/arch/x86/include/asm/ibt.h b/arch/x86/include/asm/ibt.h
index 689880eca9ba..580aa8b83bb2 100644
--- a/arch/x86/include/asm/ibt.h
+++ b/arch/x86/include/asm/ibt.h
@@ -21,6 +21,16 @@
 
 #define HAS_KERNEL_IBT	1
 
+#if defined(CONFIG_X86_KERNEL_FINEIBT) && !defined(BUILD_VDSO) && !defined(BUILD_VDSO32_64)
+#define HAS_KERNEL_FINEIBT  1
+#define FINEIBT_HASH(hash) mov $hash, %r11d
+#define __coarseendbr __attribute__((coarsecf_check))
+#else
+#define HAS_KERNEL_FINEIBT  0
+#define FINEIBT_HASH(hash)
+#define __coarseendbr
+#endif
+
 #ifndef __ASSEMBLY__
 
 #ifdef CONFIG_X86_64
@@ -29,6 +39,7 @@
 #define ASM_ENDBR	"endbr32\n\t"
 #endif
 
+#undef __noendbr
 #define __noendbr	__attribute__((nocf_check))
 
 static inline __attribute_const__ u32 gen_endbr(void)
@@ -80,12 +91,17 @@ extern __noendbr void ibt_restore(u64 save);
 #else /* !IBT */
 
 #define HAS_KERNEL_IBT	0
+#define HAS_KERNEL_FINEIBT 0
+#define FINEIBT_HASH(hash)
 
 #ifndef __ASSEMBLY__
 
 #define ASM_ENDBR
 
+#undef __noendbr
 #define __noendbr
+#undef __coarseendbr
+#define __coarseendbr
 
 static inline bool is_endbr(u32 val) { return false; }
 
diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h
index 896e48d45828..d2a2f6456403 100644
--- a/arch/x86/include/asm/setup.h
+++ b/arch/x86/include/asm/setup.h
@@ -49,10 +49,10 @@ extern unsigned long saved_video_mode;
 
 extern void reserve_standard_io_resources(void);
 extern void i386_reserve_resources(void);
-extern unsigned long __startup_64(unsigned long physaddr, struct boot_params *bp);
-extern unsigned long __startup_secondary_64(void);
-extern void startup_64_setup_env(unsigned long physbase);
-extern void early_setup_idt(void);
+extern unsigned long __coarseendbr __startup_64(unsigned long physaddr, struct boot_params *bp);
+extern unsigned long __coarseendbr __startup_secondary_64(void);
+extern void __coarseendbr startup_64_setup_env(unsigned long physbase);
+extern void __coarseendbr early_setup_idt(void);
 extern void __init do_early_exception(struct pt_regs *regs, int trapnr);
 
 #ifdef CONFIG_X86_INTEL_MID
@@ -137,8 +137,8 @@ extern void probe_roms(void);
 asmlinkage void __init i386_start_kernel(void);
 
 #else
-asmlinkage void __init x86_64_start_kernel(char *real_mode);
-asmlinkage void __init x86_64_start_reservations(char *real_mode_data);
+asmlinkage void __init __coarseendbr x86_64_start_kernel(char *real_mode);
+asmlinkage void __init __coarseendbr x86_64_start_reservations(char *real_mode_data);
 
 #endif /* __i386__ */
 #endif /* _SETUP */
diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h
index 68c257a3de0d..7f32ef8d23f0 100644
--- a/arch/x86/include/asm/special_insns.h
+++ b/arch/x86/include/asm/special_insns.h
@@ -49,7 +49,7 @@ static inline unsigned long __native_read_cr3(void)
 	return val;
 }
 
-static inline void native_write_cr3(unsigned long val)
+static inline void __coarseendbr native_write_cr3(unsigned long val)
 {
 	asm volatile("mov %0,%%cr3": : "r" (val) : "memory");
 }
@@ -74,7 +74,7 @@ static inline unsigned long native_read_cr4(void)
 	return val;
 }
 
-void native_write_cr4(unsigned long val);
+void __coarseendbr native_write_cr4(unsigned long val);
 
 #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
 static inline u32 rdpkru(void)
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index ed4417500700..70e94194ee2b 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -463,7 +463,7 @@ void native_write_cr0(unsigned long val)
 }
 EXPORT_SYMBOL(native_write_cr0);
 
-void __no_profile native_write_cr4(unsigned long val)
+void __no_profile __coarseendbr native_write_cr4(unsigned long val)
 {
 	unsigned long bits_changed = 0;
 
diff --git a/arch/x86/kernel/fineibt.c b/arch/x86/kernel/fineibt.c
new file mode 100644
index 000000000000..685e4308d86e
--- /dev/null
+++ b/arch/x86/kernel/fineibt.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file contains the FineIBT handler function.
+ */
+
+#include <linux/export.h>
+#include <linux/printk.h>
+#include <linux/kernel.h>
+#include<linux/spinlock.h>
+#include <asm/ibt.h>
+
+void __noendbr __fineibt_handler(void);
+
+void __fineibt_debug(void) {
+	asm volatile ("nop\n");
+	printk("fineibt debug\n");
+};
+EXPORT_SYMBOL(__fineibt_debug);
+
+#define FINEIBT_VADDR_LEN 4096
+#define DO_ALL_PUSHS    \
+	asm("nop\n\t"         \
+	    "push %rsi\n\t"   \
+	    "push %rdi\n\t"   \
+	    "push %rdx\n\t"   \
+	    "push %rcx\n\t"   \
+	    "push %rbx\n\t"   \
+	    "push %rax\n\t"   \
+	    "push %r8\n\t"    \
+	    "push %r9\n\t"    \
+	    "push %r10\n\t"   \
+	    "push %r11\n\t"   \
+	    "push %r12\n\t"   \
+	    "push %r13\n\t"   \
+	    "push %r14\n\t"   \
+	    "push %r15\n\t")
+
+#define DO_ALL_POPS    \
+	asm("nop\n\t"        \
+	    "pop %r15\n\t"   \
+	    "pop %r14\n\t"   \
+	    "pop %r13\n\t"   \
+	    "pop %r12\n\t"   \
+	    "pop %r11\n\t"   \
+	    "pop %r10\n\t"   \
+	    "pop %r9\n\t"    \
+	    "pop %r8\n\t"    \
+	    "pop %rax\n\t"   \
+	    "pop %rbx\n\t"   \
+	    "pop %rcx\n\t"   \
+	    "pop %rdx\n\t"   \
+	    "pop %rdi\n\t"   \
+	    "pop %rsi\n\t")
+
+struct fineibt_violation{
+	void * vaddr;
+	void * caddr;
+	bool printed;
+};
+
+typedef struct fineibt_violation fineibt_violation;
+
+static fineibt_violation vlts[FINEIBT_VADDR_LEN];
+static unsigned long vlts_next = 0;
+static bool vlts_initialize = true;
+static DEFINE_SPINLOCK(fineibt_lock);
+
+void __noendbr __fineibt_handler(void){
+	unsigned i;
+	unsigned long flags;
+	bool skip;
+	void * ret;
+	void * caller;
+
+	DO_ALL_PUSHS;
+
+	spin_lock_irqsave(&fineibt_lock, flags);
+	skip = false;
+
+	asm("\t movq 0x90(%%rsp),%0" : "=r"(ret));
+	asm("\t movq 0x98(%%rsp),%0" : "=r"(caller));
+
+	if(vlts_initialize){
+		for(i = 0; i < FINEIBT_VADDR_LEN; i++) {
+			vlts[i].vaddr = 0;
+			vlts[i].caddr = 0;
+			vlts[i].printed = 0;
+		}
+		vlts_initialize = false;
+	}
+
+	if(vlts_next >= FINEIBT_VADDR_LEN) {
+		if(vlts_next == FINEIBT_VADDR_LEN) {
+			printk("FineIBT reached max buffer\n");
+			vlts_next++;
+		}
+		skip = true;
+	}
+
+	for(i = 0; i < vlts_next; i++){
+		if(vlts[i].vaddr == ret && vlts[i].caddr == caller) {
+			skip = true;
+			break;
+		}
+	}
+
+	if(!skip) {
+		vlts[vlts_next].vaddr = ret;
+		vlts[vlts_next].caddr = caller;
+		vlts[vlts_next].printed = 0;
+		vlts_next = vlts_next + 1;
+	}
+
+	spin_unlock_irqrestore(&fineibt_lock, flags);
+
+	if(!skip) {
+		printk("FineIBT violation: %px:%px:%u\n", ret, caller,
+				vlts_next);
+	}
+	DO_ALL_POPS;
+}
+
+EXPORT_SYMBOL(__fineibt_handler);
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index 4f5ecbbaae77..f773d771e07d 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -162,7 +162,7 @@ static unsigned long __head sme_postprocess_startup(struct boot_params *bp, pmdv
  * boot-time crashes. To work around this problem, every global pointer must
  * be adjusted using fixup_pointer().
  */
-unsigned long __head __startup_64(unsigned long physaddr,
+unsigned long __head __coarseendbr __startup_64(unsigned long physaddr,
 				  struct boot_params *bp)
 {
 	unsigned long load_delta, *p;
@@ -308,7 +308,7 @@ unsigned long __head __startup_64(unsigned long physaddr,
 	return sme_postprocess_startup(bp, pmd);
 }
 
-unsigned long __startup_secondary_64(void)
+unsigned long __coarseendbr __startup_secondary_64(void)
 {
 	/*
 	 * Return the SME encryption mask (if SME is active) to be used as a
@@ -464,8 +464,8 @@ static void __init copy_bootdata(char *real_mode_data)
 	sme_unmap_bootdata(real_mode_data);
 }
 
-asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
-{
+asmlinkage __visible void __init __coarseendbr
+x86_64_start_kernel(char * real_mode_data) {
 	/*
 	 * Build-time sanity checks on the kernel image and module
 	 * area mappings. (these are purely build-time and produce no code)
@@ -597,7 +597,7 @@ static void startup_64_load_idt(unsigned long physbase)
 }
 
 /* This is used when running on kernel addresses */
-void early_setup_idt(void)
+void __coarseendbr early_setup_idt(void)
 {
 	/* VMM Communication Exception */
 	if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT))
@@ -610,7 +610,7 @@ void early_setup_idt(void)
 /*
  * Setup boot CPU state needed before kernel switches to virtual addresses.
  */
-void __head startup_64_setup_env(unsigned long physbase)
+void __head __coarseendbr startup_64_setup_env(unsigned long physbase)
 {
 	/* Load GDT */
 	startup_gdt_descr.address = (unsigned long)fixup_pointer(startup_gdt, physbase);
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 2ef14772dc04..5fa17d5716bb 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -214,7 +214,7 @@ static int enable_start_cpu0;
 /*
  * Activate a secondary processor.
  */
-static void notrace start_secondary(void *unused)
+static void notrace __coarseendbr start_secondary(void *unused)
 {
 	/*
 	 * Don't put *anything* except direct CPU state initialization
-- 
2.35.1

Powered by blists - more mailing lists