[RFC v2][PATCH 04/11] x86: Implement __arch_rare_write_begin/unmap()

From: Kees Cook
Date: Wed Mar 29 2017 - 14:19:38 EST


Based on PaX's x86 pax_{open,close}_kernel() implementation, this
allows HAVE_ARCH_RARE_WRITE to work on x86.

There is missing work to sort out some header file issues where preempt.h
is missing, though it can't be included in pg_table.h unconditionally...
some other solution will be needed, perhaps an entirely separate header
file for rare_write()-related defines...

This patch is also missing paravirt support.

Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/pgtable.h | 31 +++++++++++++++++++++++++++++++
2 files changed, 32 insertions(+)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index cc98d5a294ee..2d1d707aa036 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -106,6 +106,7 @@ config X86
select HAVE_ARCH_KMEMCHECK
select HAVE_ARCH_MMAP_RND_BITS if MMU
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
+ select HAVE_ARCH_RARE_WRITE
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
select HAVE_ARCH_TRANSPARENT_HUGEPAGE
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 1cfb36b8c024..2e6bf661bb84 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -91,6 +91,37 @@ extern struct mm_struct *pgd_page_get_mm(struct page *page);

#endif /* CONFIG_PARAVIRT */

+/* TODO: Bad hack to deal with preempt macros being missing sometimes. */
+#ifndef preempt_disable
+#include <linux/preempt.h>
+#endif
+
+static __always_inline unsigned long __arch_rare_write_begin(void)
+{
+ unsigned long cr0;
+
+ preempt_disable();
+ barrier();
+ cr0 = read_cr0() ^ X86_CR0_WP;
+ BUG_ON(cr0 & X86_CR0_WP);
+ write_cr0(cr0);
+ barrier();
+ return cr0 ^ X86_CR0_WP;
+}
+
+static __always_inline unsigned long __arch_rare_write_end(void)
+{
+ unsigned long cr0;
+
+ barrier();
+ cr0 = read_cr0() ^ X86_CR0_WP;
+ BUG_ON(!(cr0 & X86_CR0_WP));
+ write_cr0(cr0);
+ barrier();
+ preempt_enable_no_resched();
+ return cr0 ^ X86_CR0_WP;
+}
+
/*
* The following only work if pte_present() is true.
* Undefined behaviour if not..
--
2.7.4