Linux Security Modules development
 help / color / mirror / Atom feed
* [PATCH v5 20/23] x86/kprobes: Use vmalloc special flag
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe, Masami Hiramatsu
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

From: Rick Edgecombe <rick.p.edgecombe@intel.com>

Use new flag VM_FLUSH_RESET_PERMS for handling freeing of special
permissioned memory in vmalloc and remove places where memory was set NX
and RW before freeing which is no longer needed.

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 arch/x86/kernel/kprobes/core.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 1591852d3ac4..136695e4434a 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -434,6 +434,7 @@ void *alloc_insn_page(void)
 	if (!page)
 		return NULL;
 
+	set_vm_flush_reset_perms(page);
 	/*
 	 * First make the page read-only, and only then make it executable to
 	 * prevent it from being W+X in between.
@@ -452,12 +453,6 @@ void *alloc_insn_page(void)
 /* Recover page to RW mode before releasing it */
 void free_insn_page(void *page)
 {
-	/*
-	 * First make the page non-executable, and only then make it writable to
-	 * prevent it from being W+X in between.
-	 */
-	set_memory_nx((unsigned long)page, 1);
-	set_memory_rw((unsigned long)page, 1);
 	module_memfree(page);
 }
 
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 21/23] x86/alternative: Comment about module removal races
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe, Nadav Amit, Masami Hiramatsu
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

Add a comment to clarify that users of text_poke() must ensure that
no races with module removal take place.

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Nadav Amit <namit@vmware.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 arch/x86/kernel/alternative.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 18f959975ea0..7b9b49dfc05a 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -810,6 +810,11 @@ static void *__text_poke(void *addr, const void *opcode, size_t len)
  * It means the size must be writable atomically and the address must be aligned
  * in a way that permits an atomic write. It also makes sure we fit on a single
  * page.
+ *
+ * Note that the caller must ensure that if the modified code is part of a
+ * module, the module would not be removed during poking. This can be achieved
+ * by registering a module notifier, and ordering module removal and patching
+ * trough a mutex.
  */
 void *text_poke(void *addr, const void *opcode, size_t len)
 {
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 23/23] bpf: Fail bpf_probe_write_user() while mm is switched
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe, Nadav Amit, Daniel Borkmann,
	Alexei Starovoitov
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

When using a temporary mm, bpf_probe_write_user() should not be able to
write to user memory, since user memory addresses may be used to map
kernel memory.  Detect these cases and fail bpf_probe_write_user() in
such cases.

Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Reported-by: Jann Horn <jannh@google.com>
Suggested-by: Jann Horn <jannh@google.com>
Signed-off-by: Nadav Amit <namit@vmware.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 kernel/trace/bpf_trace.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index d64c00afceb5..94b0e37d90ef 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -14,6 +14,8 @@
 #include <linux/syscalls.h>
 #include <linux/error-injection.h>
 
+#include <asm/tlb.h>
+
 #include "trace_probe.h"
 #include "trace.h"
 
@@ -163,6 +165,10 @@ BPF_CALL_3(bpf_probe_write_user, void *, unsafe_ptr, const void *, src,
 	 * access_ok() should prevent writing to non-user memory, but in
 	 * some situations (nommu, temporary switch, etc) access_ok() does
 	 * not provide enough validation, hence the check on KERNEL_DS.
+	 *
+	 * nmi_uaccess_okay() ensures the probe is not run in an interim
+	 * state, when the task or mm are switched. This is specifically
+	 * required to prevent the use of temporary mm.
 	 */
 
 	if (unlikely(in_interrupt() ||
@@ -170,6 +176,8 @@ BPF_CALL_3(bpf_probe_write_user, void *, unsafe_ptr, const void *, src,
 		return -EPERM;
 	if (unlikely(uaccess_kernel()))
 		return -EPERM;
+	if (unlikely(!nmi_uaccess_okay()))
+		return -EPERM;
 	if (!access_ok(unsafe_ptr, size))
 		return -EPERM;
 
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 12/23] x86/jump-label: Remove support for custom poker
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe, Nadav Amit, Kees Cook, Dave Hansen,
	Masami Hiramatsu
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

There are only two types of poking: early and breakpoint based. The use
of a function pointer to perform poking complicates the code and is
probably inefficient due to the use of indirect branches.

Cc: Andy Lutomirski <luto@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Nadav Amit <namit@vmware.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 arch/x86/kernel/jump_label.c | 26 ++++++++++----------------
 1 file changed, 10 insertions(+), 16 deletions(-)

diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c
index e7d8c636b228..e631c358f7f4 100644
--- a/arch/x86/kernel/jump_label.c
+++ b/arch/x86/kernel/jump_label.c
@@ -37,7 +37,6 @@ static void bug_at(unsigned char *ip, int line)
 
 static void __ref __jump_label_transform(struct jump_entry *entry,
 					 enum jump_label_type type,
-					 void *(*poker)(void *, const void *, size_t),
 					 int init)
 {
 	union jump_code_union jmp;
@@ -50,14 +49,6 @@ static void __ref __jump_label_transform(struct jump_entry *entry,
 	jmp.offset = jump_entry_target(entry) -
 		     (jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
 
-	/*
-	 * As long as only a single processor is running and the code is still
-	 * not marked as RO, text_poke_early() can be used; Checking that
-	 * system_state is SYSTEM_BOOTING guarantees it.
-	 */
-	if (system_state == SYSTEM_BOOTING)
-		poker = text_poke_early;
-
 	if (type == JUMP_LABEL_JMP) {
 		if (init) {
 			expect = default_nop; line = __LINE__;
@@ -80,16 +71,19 @@ static void __ref __jump_label_transform(struct jump_entry *entry,
 		bug_at((void *)jump_entry_code(entry), line);
 
 	/*
-	 * Make text_poke_bp() a default fallback poker.
+	 * As long as only a single processor is running and the code is still
+	 * not marked as RO, text_poke_early() can be used; Checking that
+	 * system_state is SYSTEM_BOOTING guarantees it. It will be set to
+	 * SYSTEM_SCHEDULING before other cores are awaken and before the
+	 * code is write-protected.
 	 *
 	 * At the time the change is being done, just ignore whether we
 	 * are doing nop -> jump or jump -> nop transition, and assume
 	 * always nop being the 'currently valid' instruction
-	 *
 	 */
-	if (poker) {
-		(*poker)((void *)jump_entry_code(entry), code,
-			 JUMP_LABEL_NOP_SIZE);
+	if (init || system_state == SYSTEM_BOOTING) {
+		text_poke_early((void *)jump_entry_code(entry), code,
+				JUMP_LABEL_NOP_SIZE);
 		return;
 	}
 
@@ -101,7 +95,7 @@ void arch_jump_label_transform(struct jump_entry *entry,
 			       enum jump_label_type type)
 {
 	mutex_lock(&text_mutex);
-	__jump_label_transform(entry, type, NULL, 0);
+	__jump_label_transform(entry, type, 0);
 	mutex_unlock(&text_mutex);
 }
 
@@ -131,5 +125,5 @@ __init_or_module void arch_jump_label_transform_static(struct jump_entry *entry,
 			jlstate = JL_STATE_NO_UPDATE;
 	}
 	if (jlstate == JL_STATE_UPDATE)
-		__jump_label_transform(entry, type, text_poke_early, 1);
+		__jump_label_transform(entry, type, 1);
 }
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 13/23] x86/alternative: Remove the return value of text_poke_*()
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe, Nadav Amit, Kees Cook, Dave Hansen,
	Masami Hiramatsu
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

The return value of text_poke_early() and text_poke_bp() is useless.
Remove it.

Cc: Andy Lutomirski <luto@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Nadav Amit <namit@vmware.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 arch/x86/include/asm/text-patching.h |  4 ++--
 arch/x86/kernel/alternative.c        | 11 ++++-------
 2 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/arch/x86/include/asm/text-patching.h b/arch/x86/include/asm/text-patching.h
index a75eed841eed..c90678fd391a 100644
--- a/arch/x86/include/asm/text-patching.h
+++ b/arch/x86/include/asm/text-patching.h
@@ -18,7 +18,7 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
 #define __parainstructions_end	NULL
 #endif
 
-extern void *text_poke_early(void *addr, const void *opcode, size_t len);
+extern void text_poke_early(void *addr, const void *opcode, size_t len);
 
 /*
  * Clear and restore the kernel write-protection flag on the local CPU.
@@ -37,7 +37,7 @@ extern void *text_poke_early(void *addr, const void *opcode, size_t len);
 extern void *text_poke(void *addr, const void *opcode, size_t len);
 extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
 extern int poke_int3_handler(struct pt_regs *regs);
-extern void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler);
+extern void text_poke_bp(void *addr, const void *opcode, size_t len, void *handler);
 extern int after_bootmem;
 extern __ro_after_init struct mm_struct *poking_mm;
 extern __ro_after_init unsigned long poking_addr;
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 3d2b6b6fb20c..18f959975ea0 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -265,7 +265,7 @@ static void __init_or_module add_nops(void *insns, unsigned int len)
 
 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
 extern s32 __smp_locks[], __smp_locks_end[];
-void *text_poke_early(void *addr, const void *opcode, size_t len);
+void text_poke_early(void *addr, const void *opcode, size_t len);
 
 /*
  * Are we looking at a near JMP with a 1 or 4-byte displacement.
@@ -667,8 +667,8 @@ void __init alternative_instructions(void)
  * instructions. And on the local CPU you need to be protected again NMI or MCE
  * handlers seeing an inconsistent instruction while you patch.
  */
-void *__init_or_module text_poke_early(void *addr, const void *opcode,
-				       size_t len)
+void __init_or_module text_poke_early(void *addr, const void *opcode,
+				      size_t len)
 {
 	unsigned long flags;
 
@@ -691,7 +691,6 @@ void *__init_or_module text_poke_early(void *addr, const void *opcode,
 		 * that causes hangs on some VIA CPUs.
 		 */
 	}
-	return addr;
 }
 
 __ro_after_init struct mm_struct *poking_mm;
@@ -893,7 +892,7 @@ NOKPROBE_SYMBOL(poke_int3_handler);
  *	  replacing opcode
  *	- sync cores
  */
-void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
+void text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
 {
 	unsigned char int3 = 0xcc;
 
@@ -935,7 +934,5 @@ void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
 	 * the writing of the new instruction.
 	 */
 	bp_patching_in_progress = false;
-
-	return addr;
 }
 
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 14/23] x86/mm/cpa: Add set_direct_map_ functions
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

From: Rick Edgecombe <rick.p.edgecombe@intel.com>

Add two new functions set_direct_map_default_noflush() and
set_direct_map_invalid_noflush() for setting the direct map alias for the
page to its default valid permissions and to an invalid state that cannot
be cached in a TLB, respectively. These functions do not flush the TLB.

Note, __kernel_map_pages() does something similar but flushes the TLB and
doesn't reset the permission bits to default on all architectures.

Also add an ARCH config ARCH_HAS_SET_DIRECT_MAP for specifying whether
these have an actual implementation or a default empty one.

Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 arch/Kconfig                      |  4 ++++
 arch/x86/Kconfig                  |  1 +
 arch/x86/include/asm/set_memory.h |  3 +++
 arch/x86/mm/pageattr.c            | 14 +++++++++++---
 include/linux/set_memory.h        | 11 +++++++++++
 5 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 3ab446bd12ef..5e43fcbad4ca 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -249,6 +249,10 @@ config ARCH_HAS_FORTIFY_SOURCE
 config ARCH_HAS_SET_MEMORY
 	bool
 
+# Select if arch has all set_direct_map_invalid/default() functions
+config ARCH_HAS_SET_DIRECT_MAP
+	bool
+
 # Select if arch init_task must go in the __init_task_data section
 config ARCH_TASK_STRUCT_ON_STACK
        bool
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 2ec5e850b807..45d788354376 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -66,6 +66,7 @@ config X86
 	select ARCH_HAS_UACCESS_FLUSHCACHE	if X86_64
 	select ARCH_HAS_UACCESS_MCSAFE		if X86_64 && X86_MCE
 	select ARCH_HAS_SET_MEMORY
+	select ARCH_HAS_SET_DIRECT_MAP
 	select ARCH_HAS_STRICT_KERNEL_RWX
 	select ARCH_HAS_STRICT_MODULE_RWX
 	select ARCH_HAS_SYNC_CORE_BEFORE_USERMODE
diff --git a/arch/x86/include/asm/set_memory.h b/arch/x86/include/asm/set_memory.h
index 07a25753e85c..ae7b909dc242 100644
--- a/arch/x86/include/asm/set_memory.h
+++ b/arch/x86/include/asm/set_memory.h
@@ -85,6 +85,9 @@ int set_pages_nx(struct page *page, int numpages);
 int set_pages_ro(struct page *page, int numpages);
 int set_pages_rw(struct page *page, int numpages);
 
+int set_direct_map_invalid_noflush(struct page *page);
+int set_direct_map_default_noflush(struct page *page);
+
 extern int kernel_set_to_readonly;
 void set_kernel_text_rw(void);
 void set_kernel_text_ro(void);
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 4c570612e24e..3574550192c6 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -2209,8 +2209,6 @@ int set_pages_rw(struct page *page, int numpages)
 	return set_memory_rw(addr, numpages);
 }
 
-#ifdef CONFIG_DEBUG_PAGEALLOC
-
 static int __set_pages_p(struct page *page, int numpages)
 {
 	unsigned long tempaddr = (unsigned long) page_address(page);
@@ -2249,6 +2247,17 @@ static int __set_pages_np(struct page *page, int numpages)
 	return __change_page_attr_set_clr(&cpa, 0);
 }
 
+int set_direct_map_invalid_noflush(struct page *page)
+{
+	return __set_pages_np(page, 1);
+}
+
+int set_direct_map_default_noflush(struct page *page)
+{
+	return __set_pages_p(page, 1);
+}
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
 void __kernel_map_pages(struct page *page, int numpages, int enable)
 {
 	if (PageHighMem(page))
@@ -2282,7 +2291,6 @@ void __kernel_map_pages(struct page *page, int numpages, int enable)
 }
 
 #ifdef CONFIG_HIBERNATION
-
 bool kernel_page_present(struct page *page)
 {
 	unsigned int level;
diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
index 2a986d282a97..b5071497b8cb 100644
--- a/include/linux/set_memory.h
+++ b/include/linux/set_memory.h
@@ -17,6 +17,17 @@ static inline int set_memory_x(unsigned long addr,  int numpages) { return 0; }
 static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; }
 #endif
 
+#ifndef CONFIG_ARCH_HAS_SET_DIRECT_MAP
+static inline int set_direct_map_invalid_noflush(struct page *page)
+{
+	return 0;
+}
+static inline int set_direct_map_default_noflush(struct page *page)
+{
+	return 0;
+}
+#endif
+
 #ifndef set_mce_nospec
 static inline int set_mce_nospec(unsigned long pfn)
 {
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 11/23] x86/module: Avoid breaking W^X while loading modules
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe, Nadav Amit, Kees Cook, Dave Hansen,
	Masami Hiramatsu, Jessica Yu
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

When modules and BPF filters are loaded, there is a time window in
which some memory is both writable and executable. An attacker that has
already found another vulnerability (e.g., a dangling pointer) might be
able to exploit this behavior to overwrite kernel code. Prevent having
writable executable PTEs in this stage.

In addition, avoiding having W+X mappings can also slightly simplify the
patching of modules code on initialization (e.g., by alternatives and
static-key), as would be done in the next patch. This was actually the
main motivation for this patch.

To avoid having W+X mappings, set them initially as RW (NX) and after
they are set as RO set them as X as well. Setting them as executable is
done as a separate step to avoid one core in which the old PTE is cached
(hence writable), and another which sees the updated PTE (executable),
which would break the W^X protection.

Cc: Kees Cook <keescook@chromium.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Jessica Yu <jeyu@kernel.org>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Suggested-by: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: Nadav Amit <namit@vmware.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 arch/x86/kernel/alternative.c | 28 +++++++++++++++++++++-------
 arch/x86/kernel/module.c      |  2 +-
 include/linux/filter.h        |  1 +
 kernel/module.c               |  5 +++++
 4 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 599203876c32..3d2b6b6fb20c 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -668,15 +668,29 @@ void __init alternative_instructions(void)
  * handlers seeing an inconsistent instruction while you patch.
  */
 void *__init_or_module text_poke_early(void *addr, const void *opcode,
-					      size_t len)
+				       size_t len)
 {
 	unsigned long flags;
-	local_irq_save(flags);
-	memcpy(addr, opcode, len);
-	local_irq_restore(flags);
-	sync_core();
-	/* Could also do a CLFLUSH here to speed up CPU recovery; but
-	   that causes hangs on some VIA CPUs. */
+
+	if (boot_cpu_has(X86_FEATURE_NX) &&
+	    is_module_text_address((unsigned long)addr)) {
+		/*
+		 * Modules text is marked initially as non-executable, so the
+		 * code cannot be running and speculative code-fetches are
+		 * prevented. Just change the code.
+		 */
+		memcpy(addr, opcode, len);
+	} else {
+		local_irq_save(flags);
+		memcpy(addr, opcode, len);
+		local_irq_restore(flags);
+		sync_core();
+
+		/*
+		 * Could also do a CLFLUSH here to speed up CPU recovery; but
+		 * that causes hangs on some VIA CPUs.
+		 */
+	}
 	return addr;
 }
 
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c
index b052e883dd8c..cfa3106faee4 100644
--- a/arch/x86/kernel/module.c
+++ b/arch/x86/kernel/module.c
@@ -87,7 +87,7 @@ void *module_alloc(unsigned long size)
 	p = __vmalloc_node_range(size, MODULE_ALIGN,
 				    MODULES_VADDR + get_module_load_offset(),
 				    MODULES_END, GFP_KERNEL,
-				    PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
+				    PAGE_KERNEL, 0, NUMA_NO_NODE,
 				    __builtin_return_address(0));
 	if (p && (kasan_module_alloc(p, size) < 0)) {
 		vfree(p);
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 6074aa064b54..14ec3bdad9a9 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -746,6 +746,7 @@ static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
 static inline void bpf_jit_binary_lock_ro(struct bpf_binary_header *hdr)
 {
 	set_memory_ro((unsigned long)hdr, hdr->pages);
+	set_memory_x((unsigned long)hdr, hdr->pages);
 }
 
 static inline void bpf_jit_binary_unlock_ro(struct bpf_binary_header *hdr)
diff --git a/kernel/module.c b/kernel/module.c
index 0b9aa8ab89f0..2b2845ae983e 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1950,8 +1950,13 @@ void module_enable_ro(const struct module *mod, bool after_init)
 		return;
 
 	frob_text(&mod->core_layout, set_memory_ro);
+	frob_text(&mod->core_layout, set_memory_x);
+
 	frob_rodata(&mod->core_layout, set_memory_ro);
+
 	frob_text(&mod->init_layout, set_memory_ro);
+	frob_text(&mod->init_layout, set_memory_x);
+
 	frob_rodata(&mod->init_layout, set_memory_ro);
 
 	if (after_init)
-- 
2.17.1


^ permalink raw reply related

* [PATCH v5 03/23] x86/mm: Introduce temporary mm structs
From: Nadav Amit @ 2019-04-26  0:11 UTC (permalink / raw)
  To: Peter Zijlstra, Borislav Petkov, Andy Lutomirski, Ingo Molnar
  Cc: linux-kernel, x86, hpa, Thomas Gleixner, Nadav Amit, Dave Hansen,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock, Rick Edgecombe, Kees Cook, Dave Hansen, Nadav Amit
In-Reply-To: <20190426001143.4983-1-namit@vmware.com>

From: Andy Lutomirski <luto@kernel.org>

Using a dedicated page-table for temporary PTEs prevents other cores
from using - even speculatively - these PTEs, thereby providing two
benefits:

(1) Security hardening: an attacker that gains kernel memory writing
abilities cannot easily overwrite sensitive data.

(2) Avoiding TLB shootdowns: the PTEs do not need to be flushed in
remote page-tables.

To do so a temporary mm_struct can be used. Mappings which are private
for this mm can be set in the userspace part of the address-space.
During the whole time in which the temporary mm is loaded, interrupts
must be disabled.

The first use-case for temporary mm struct, which will follow, is for
poking the kernel text.

[ Commit message was written by Nadav Amit ]

Cc: Kees Cook <keescook@chromium.org>
Cc: Dave Hansen <dave.hansen@intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org>
Tested-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Nadav Amit <namit@vmware.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
 arch/x86/include/asm/mmu_context.h | 33 ++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index 19d18fae6ec6..24dc3b810970 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -356,4 +356,37 @@ static inline unsigned long __get_current_cr3_fast(void)
 	return cr3;
 }
 
+typedef struct {
+	struct mm_struct *mm;
+} temp_mm_state_t;
+
+/*
+ * Using a temporary mm allows to set temporary mappings that are not accessible
+ * by other CPUs. Such mappings are needed to perform sensitive memory writes
+ * that override the kernel memory protections (e.g., W^X), without exposing the
+ * temporary page-table mappings that are required for these write operations to
+ * other CPUs. Using a temporary mm also allows to avoid TLB shootdowns when the
+ * mapping is torn down.
+ *
+ * Context: The temporary mm needs to be used exclusively by a single core. To
+ *          harden security IRQs must be disabled while the temporary mm is
+ *          loaded, thereby preventing interrupt handler bugs from overriding
+ *          the kernel memory protection.
+ */
+static inline temp_mm_state_t use_temporary_mm(struct mm_struct *mm)
+{
+	temp_mm_state_t temp_state;
+
+	lockdep_assert_irqs_disabled();
+	temp_state.mm = this_cpu_read(cpu_tlbstate.loaded_mm);
+	switch_mm_irqs_off(NULL, mm, current);
+	return temp_state;
+}
+
+static inline void unuse_temporary_mm(temp_mm_state_t prev_state)
+{
+	lockdep_assert_irqs_disabled();
+	switch_mm_irqs_off(NULL, prev_state.mm, current);
+}
+
 #endif /* _ASM_X86_MMU_CONTEXT_H */
-- 
2.17.1


^ permalink raw reply related

* Re: [RFC PATCH 0/7] x86: introduce system calls addess space isolation
From: Andy Lutomirski @ 2019-04-26  0:30 UTC (permalink / raw)
  To: Mike Rapoport
  Cc: LKML, Alexandre Chartre, Andy Lutomirski, Borislav Petkov,
	Dave Hansen, H. Peter Anvin, Ingo Molnar, James Bottomley,
	Jonathan Adams, Kees Cook, Paul Turner, Peter Zijlstra,
	Thomas Gleixner, Linux-MM, LSM List, X86 ML
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

On Thu, Apr 25, 2019 at 2:46 PM Mike Rapoport <rppt@linux.ibm.com> wrote:
>
> Hi,
>
> Address space isolation has been used to protect the kernel from the
> userspace and userspace programs from each other since the invention of the
> virtual memory.
>
> Assuming that kernel bugs and therefore vulnerabilities are inevitable it
> might be worth isolating parts of the kernel to minimize damage that these
> vulnerabilities can cause.
>
> The idea here is to allow an untrusted user access to a potentially
> vulnerable kernel in such a way that any kernel vulnerability they find to
> exploit is either prevented or the consequences confined to their isolated
> address space such that the compromise attempt has minimal impact on other
> tenants or the protected structures of the monolithic kernel.  Although we
> hope to prevent many classes of attack, the first target we're looking at
> is ROP gadget protection.
>
> These patches implement a "system call isolation (SCI)" mechanism that
> allows running system calls in an isolated address space with reduced page
> tables to prevent ROP attacks.
>
> ROP attacks involve corrupting the stack return address to repoint it to a
> segment of code you know exists in the kernel that can be used to perform
> the action you need to exploit the system.
>
> The idea behind the prevention is that if we fault in pages in the
> execution path, we can compare target address against the kernel symbol
> table.  So if we're in a function, we allow local jumps (and simply falling
> of the end of a page) but if we're jumping to a new function it must be to
> an external label in the symbol table.

That's quite an assumption.  The entry code at least uses .L labels.
Do you get that right?

As far as I can see, most of what's going on here has very little to
do with jumps and calls.  The benefit seems to come from making sure
that the RET instruction actually goes somewhere that's already been
faulted in.  Am I understanding right?

--Andy

^ permalink raw reply

* [RFC PATCH 2/7] x86/sci: add core implementation for system call isolation
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

When enabled, the system call isolation (SCI) would allow execution of the
system calls with reduced page tables. These page tables are almost
identical to the user page tables in PTI. The only addition is the code
page containing system call entry function that will continue exectution
after the context switch.

Unlike PTI page tables, there is no sharing at higher levels and all the
hierarchy for SCI page tables is cloned.

The SCI page tables are created when a system call that requires isolation
is executed for the first time.

Whenever a system call should be executed in the isolated environment, the
context is switched to the SCI page tables. Any further access to the
kernel memory will generate a page fault. The page fault handler can verify
that the access is safe and grant it or kill the task otherwise.

The initial SCI implementation allows access to any kernel data, but it
limits access to the code in the following way:
* calls and jumps to known code symbols without offset are allowed
* calls and jumps into a known symbol with offset are allowed only if that
symbol was already accessed and the offset is in the next page
* all other code access are blocked

After the isolated system call finishes, the mappings created during its
execution are cleared.

The entire SCI page table is lazily freed at task exit() time.

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
---
 arch/x86/include/asm/sci.h |  55 ++++
 arch/x86/mm/Makefile       |   1 +
 arch/x86/mm/init.c         |   2 +
 arch/x86/mm/sci.c          | 608 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/sched.h      |   5 +
 include/linux/sci.h        |  12 +
 6 files changed, 683 insertions(+)
 create mode 100644 arch/x86/include/asm/sci.h
 create mode 100644 arch/x86/mm/sci.c
 create mode 100644 include/linux/sci.h

diff --git a/arch/x86/include/asm/sci.h b/arch/x86/include/asm/sci.h
new file mode 100644
index 0000000..0b56200
--- /dev/null
+++ b/arch/x86/include/asm/sci.h
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef _ASM_X86_SCI_H
+#define _ASM_X86_SCI_H
+
+#ifdef CONFIG_SYSCALL_ISOLATION
+
+struct sci_task_data {
+	pgd_t		*pgd;
+	unsigned long	cr3_offset;
+	unsigned long	backtrace_size;
+	unsigned long	*backtrace;
+	unsigned long	ptes_count;
+	pte_t		**ptes;
+};
+
+struct sci_percpu_data {
+	unsigned long		sci_syscall;
+	unsigned long		sci_cr3_offset;
+};
+
+DECLARE_PER_CPU_PAGE_ALIGNED(struct sci_percpu_data, cpu_sci);
+
+void sci_check_boottime_disable(void);
+
+int sci_init(struct task_struct *tsk);
+void sci_exit(struct task_struct *tsk);
+
+bool sci_verify_and_map(struct pt_regs *regs, unsigned long addr,
+			unsigned long hw_error_code);
+void sci_clear_data(void);
+
+static inline void sci_switch_to(struct task_struct *next)
+{
+	this_cpu_write(cpu_sci.sci_syscall, next->in_isolated_syscall);
+	if (next->sci)
+		this_cpu_write(cpu_sci.sci_cr3_offset, next->sci->cr3_offset);
+}
+
+#else /* CONFIG_SYSCALL_ISOLATION */
+
+static inline void sci_check_boottime_disable(void) {}
+
+static inline bool sci_verify_and_map(struct pt_regs *regs,unsigned long addr,
+				      unsigned long hw_error_code)
+{
+	return true;
+}
+
+static inline void sci_clear_data(void) {}
+
+static inline void sci_switch_to(struct task_struct *next) {}
+
+#endif /* CONFIG_SYSCALL_ISOLATION */
+
+#endif /* _ASM_X86_SCI_H */
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index 4b101dd..9a728b7 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_X86_INTEL_MPX)			+= mpx.o
 obj-$(CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS)	+= pkeys.o
 obj-$(CONFIG_RANDOMIZE_MEMORY)			+= kaslr.o
 obj-$(CONFIG_PAGE_TABLE_ISOLATION)		+= pti.o
+obj-$(CONFIG_SYSCALL_ISOLATION)			+= sci.o
 
 obj-$(CONFIG_AMD_MEM_ENCRYPT)	+= mem_encrypt.o
 obj-$(CONFIG_AMD_MEM_ENCRYPT)	+= mem_encrypt_identity.o
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index f905a23..b6e2db4 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -22,6 +22,7 @@
 #include <asm/hypervisor.h>
 #include <asm/cpufeature.h>
 #include <asm/pti.h>
+#include <asm/sci.h>
 
 /*
  * We need to define the tracepoints somewhere, and tlb.c
@@ -648,6 +649,7 @@ void __init init_mem_mapping(void)
 	unsigned long end;
 
 	pti_check_boottime_disable();
+	sci_check_boottime_disable();
 	probe_page_size_mask();
 	setup_pcid();
 
diff --git a/arch/x86/mm/sci.c b/arch/x86/mm/sci.c
new file mode 100644
index 0000000..e7ddec1
--- /dev/null
+++ b/arch/x86/mm/sci.c
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(c) 2019 IBM Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * Author: Mike Rapoport <rppt@linux.ibm.com>
+ *
+ * This code is based on pti.c, see it for the original copyrights
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/kallsyms.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/sizes.h>
+#include <linux/sci.h>
+#include <linux/random.h>
+
+#include <asm/cpufeature.h>
+#include <asm/hypervisor.h>
+#include <asm/cmdline.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <asm/desc.h>
+#include <asm/sections.h>
+#include <asm/traps.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt)     "SCI: " fmt
+
+#define SCI_MAX_PTES 256
+#define SCI_MAX_BACKTRACE 64
+
+__visible DEFINE_PER_CPU_PAGE_ALIGNED(struct sci_percpu_data, cpu_sci);
+
+/*
+ * Walk the shadow copy of the page tables to PMD level (optionally)
+ * trying to allocate page table pages on the way down.
+ *
+ * Allocation failures are not handled here because the entire page
+ * table will be freed in sci_free_pagetable.
+ *
+ * Returns a pointer to a PMD on success, or NULL on failure.
+ */
+static pmd_t *sci_pagetable_walk_pmd(struct mm_struct *mm,
+				     pgd_t *pgd, unsigned long address)
+{
+	p4d_t *p4d;
+	pud_t *pud;
+
+	p4d = p4d_alloc(mm, pgd, address);
+	if (!p4d)
+		return NULL;
+
+	pud = pud_alloc(mm, p4d, address);
+	if (!pud)
+		return NULL;
+
+	return pmd_alloc(mm, pud, address);
+}
+
+/*
+ * Walk the shadow copy of the page tables to PTE level (optionally)
+ * trying to allocate page table pages on the way down.
+ *
+ * Returns a pointer to a PTE on success, or NULL on failure.
+ */
+static pte_t *sci_pagetable_walk_pte(struct mm_struct *mm,
+				     pgd_t *pgd, unsigned long address)
+{
+	pmd_t *pmd = sci_pagetable_walk_pmd(mm, pgd, address);
+
+	if (!pmd)
+		return NULL;
+
+	if (__pte_alloc(mm, pmd))
+		return NULL;
+
+	return pte_offset_kernel(pmd, address);
+}
+
+/*
+ * Clone a single page mapping
+ *
+ * The new mapping in the @target_pgdp is always created for base
+ * page. If the orinal page table has the page at @addr mapped at PMD
+ * level, we anyway create at PTE in the target page table and map
+ * only PAGE_SIZE.
+ */
+static pte_t *sci_clone_page(struct mm_struct *mm,
+			     pgd_t *pgdp, pgd_t *target_pgdp,
+			     unsigned long addr)
+{
+	pte_t *pte, *target_pte, ptev;
+	pgd_t *pgd, *target_pgd;
+	p4d_t *p4d;
+	pud_t *pud;
+	pmd_t *pmd;
+
+	pgd = pgd_offset_pgd(pgdp, addr);
+	if (pgd_none(*pgd))
+		return NULL;
+
+	p4d = p4d_offset(pgd, addr);
+	if (p4d_none(*p4d))
+		return NULL;
+
+	pud = pud_offset(p4d, addr);
+	if (pud_none(*pud))
+		return NULL;
+
+	pmd = pmd_offset(pud, addr);
+	if (pmd_none(*pmd))
+		return NULL;
+
+	target_pgd = pgd_offset_pgd(target_pgdp, addr);
+
+	if (pmd_large(*pmd)) {
+		pgprot_t flags;
+		unsigned long pfn;
+
+		/*
+		 * We map only PAGE_SIZE rather than the entire huge page.
+		 * The PTE will have the same pgprot bits as the origial PMD
+		 */
+		flags = pte_pgprot(pte_clrhuge(*(pte_t *)pmd));
+		pfn = pmd_pfn(*pmd) + pte_index(addr);
+		ptev = pfn_pte(pfn, flags);
+	} else {
+		pte = pte_offset_kernel(pmd, addr);
+		if (pte_none(*pte) || !(pte_flags(*pte) & _PAGE_PRESENT))
+			return NULL;
+
+		ptev = *pte;
+	}
+
+	target_pte = sci_pagetable_walk_pte(mm, target_pgd, addr);
+	if (!target_pte)
+		return NULL;
+
+	*target_pte = ptev;
+
+	return target_pte;
+}
+
+/*
+ * Clone a range keeping the same leaf mappings
+ *
+ * If the range has holes they are simply skipped
+ */
+static int sci_clone_range(struct mm_struct *mm,
+			   pgd_t *pgdp, pgd_t *target_pgdp,
+			   unsigned long start, unsigned long end)
+{
+	unsigned long addr;
+
+	/*
+	 * Clone the populated PMDs which cover start to end. These PMD areas
+	 * can have holes.
+	 */
+	for (addr = start; addr < end;) {
+		pte_t *pte, *target_pte;
+		pgd_t *pgd, *target_pgd;
+		pmd_t *pmd, *target_pmd;
+		p4d_t *p4d;
+		pud_t *pud;
+
+		/* Overflow check */
+		if (addr < start)
+			break;
+
+		pgd = pgd_offset_pgd(pgdp, addr);
+		if (pgd_none(*pgd))
+			return 0;
+
+		p4d = p4d_offset(pgd, addr);
+		if (p4d_none(*p4d))
+			return 0;
+
+		pud = pud_offset(p4d, addr);
+		if (pud_none(*pud)) {
+			addr += PUD_SIZE;
+			continue;
+		}
+
+		pmd = pmd_offset(pud, addr);
+		if (pmd_none(*pmd)) {
+			addr += PMD_SIZE;
+			continue;
+		}
+
+		target_pgd = pgd_offset_pgd(target_pgdp, addr);
+
+		if (pmd_large(*pmd)) {
+			target_pmd = sci_pagetable_walk_pmd(mm, target_pgd,
+							    addr);
+			if (!target_pmd)
+				return -ENOMEM;
+
+			*target_pmd = *pmd;
+
+			addr += PMD_SIZE;
+			continue;
+		} else {
+			pte = pte_offset_kernel(pmd, addr);
+			if (pte_none(*pte)) {
+				addr += PAGE_SIZE;
+				continue;
+			}
+
+			target_pte = sci_pagetable_walk_pte(mm, target_pgd,
+							    addr);
+			if (!target_pte)
+				return -ENOMEM;
+
+			*target_pte = *pte;
+
+			addr += PAGE_SIZE;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * we have to map the syscall entry because we'll fault there after
+ * CR3 switch and before the verifier is able to detect this as proper
+ * access
+ */
+extern void do_syscall_64(unsigned long nr, struct pt_regs *regs);
+unsigned long syscall_entry_addr = (unsigned long)do_syscall_64;
+
+static void sci_reset_backtrace(struct sci_task_data *sci)
+{
+	memset(sci->backtrace, 0, sci->backtrace_size);
+	sci->backtrace[0] = syscall_entry_addr;
+	sci->backtrace_size = 1;
+}
+
+static inline void sci_sync_user_pagetable(struct task_struct *tsk)
+{
+	pgd_t *u_pgd = kernel_to_user_pgdp(tsk->mm->pgd);
+	pgd_t *sci_pgd = tsk->sci->pgd;
+
+	down_write(&tsk->mm->mmap_sem);
+	memcpy(sci_pgd, u_pgd, PGD_KERNEL_START * sizeof(pgd_t));
+	up_write(&tsk->mm->mmap_sem);
+}
+
+static int sci_free_pte_range(struct mm_struct *mm, pmd_t *pmd)
+{
+	pte_t *ptep = pte_offset_kernel(pmd, 0);
+
+	pmd_clear(pmd);
+	pte_free(mm, virt_to_page(ptep));
+	mm_dec_nr_ptes(mm);
+
+	return 0;
+}
+
+static int sci_free_pmd_range(struct mm_struct *mm, pud_t *pud)
+{
+	pmd_t *pmd, *pmdp;
+	int i;
+
+	pmdp = pmd_offset(pud, 0);
+
+	for (i = 0, pmd = pmdp; i < PTRS_PER_PMD; i++, pmd++)
+		if (!pmd_none(*pmd) && !pmd_large(*pmd))
+			sci_free_pte_range(mm, pmd);
+
+	pud_clear(pud);
+	pmd_free(mm, pmdp);
+	mm_dec_nr_pmds(mm);
+
+	return 0;
+}
+
+static int sci_free_pud_range(struct mm_struct *mm, p4d_t *p4d)
+{
+	pud_t *pud, *pudp;
+	int i;
+
+	pudp = pud_offset(p4d, 0);
+
+	for (i = 0, pud = pudp; i < PTRS_PER_PUD; i++, pud++)
+		if (!pud_none(*pud))
+			sci_free_pmd_range(mm, pud);
+
+	p4d_clear(p4d);
+	pud_free(mm, pudp);
+	mm_dec_nr_puds(mm);
+
+	return 0;
+}
+
+static int sci_free_p4d_range(struct mm_struct *mm, pgd_t *pgd)
+{
+	p4d_t *p4d, *p4dp;
+	int i;
+
+	p4dp = p4d_offset(pgd, 0);
+
+	for (i = 0, p4d = p4dp; i < PTRS_PER_P4D; i++, p4d++)
+		if (!p4d_none(*p4d))
+			sci_free_pud_range(mm, p4d);
+
+	pgd_clear(pgd);
+	p4d_free(mm, p4dp);
+
+	return 0;
+}
+
+static int sci_free_pagetable(struct task_struct *tsk, pgd_t *sci_pgd)
+{
+	struct mm_struct *mm = tsk->mm;
+	pgd_t *pgd, *pgdp = sci_pgd;
+
+#ifdef SCI_SHARED_PAGE_TABLES
+	int i;
+
+	for (i = KERNEL_PGD_BOUNDARY; i < PTRS_PER_PGD; i++) {
+		if (i >= pgd_index(VMALLOC_START) &&
+		    i < pgd_index(__START_KERNEL_map))
+			continue;
+		pgd = pgdp + i;
+		sci_free_p4d_range(mm, pgd);
+	}
+#else
+	for (pgd = pgdp + KERNEL_PGD_BOUNDARY; pgd < pgdp + PTRS_PER_PGD; pgd++)
+		if (!pgd_none(*pgd))
+			sci_free_p4d_range(mm, pgd);
+#endif
+
+
+	return 0;
+}
+
+static int sci_pagetable_init(struct task_struct *tsk, pgd_t *sci_pgd)
+{
+	struct mm_struct *mm = tsk->mm;
+	pgd_t *k_pgd = mm->pgd;
+	pgd_t *u_pgd = kernel_to_user_pgdp(k_pgd);
+	unsigned long stack = (unsigned long)tsk->stack;
+	unsigned long addr;
+	unsigned int cpu;
+	pte_t *pte;
+	int ret;
+
+	/* copy the kernel part of user visible page table */
+	ret = sci_clone_range(mm, u_pgd, sci_pgd, CPU_ENTRY_AREA_BASE,
+			      CPU_ENTRY_AREA_BASE + CPU_ENTRY_AREA_MAP_SIZE);
+	if (ret)
+		goto err_free_pagetable;
+
+	ret = sci_clone_range(mm, u_pgd, sci_pgd,
+			      (unsigned long) __entry_text_start,
+			      (unsigned long) __irqentry_text_end);
+	if (ret)
+		goto err_free_pagetable;
+
+	ret = sci_clone_range(mm, mm->pgd, sci_pgd,
+			      stack, stack + THREAD_SIZE);
+	if (ret)
+		goto err_free_pagetable;
+
+	ret = -ENOMEM;
+	for_each_possible_cpu(cpu) {
+		addr = (unsigned long)&per_cpu(cpu_sci, cpu);
+		pte = sci_clone_page(mm, k_pgd, sci_pgd, addr);
+		if (!pte)
+			goto err_free_pagetable;
+	}
+
+	/* plus do_syscall_64 */
+	pte = sci_clone_page(mm, k_pgd, sci_pgd, syscall_entry_addr);
+	if (!pte)
+		goto err_free_pagetable;
+
+	return 0;
+
+err_free_pagetable:
+	sci_free_pagetable(tsk, sci_pgd);
+	return ret;
+}
+
+static int sci_alloc(struct task_struct *tsk)
+{
+	struct sci_task_data *sci;
+	int err = -ENOMEM;
+
+	if (!static_cpu_has(X86_FEATURE_SCI))
+		return 0;
+
+	if (tsk->sci)
+		return 0;
+
+	sci = kzalloc(sizeof(*sci), GFP_KERNEL);
+	if (!sci)
+		return err;
+
+	sci->ptes = kcalloc(SCI_MAX_PTES, sizeof(*sci->ptes), GFP_KERNEL);
+	if (!sci->ptes)
+		goto free_sci;
+
+	sci->backtrace = kcalloc(SCI_MAX_BACKTRACE, sizeof(*sci->backtrace),
+				  GFP_KERNEL);
+	if (!sci->backtrace)
+		goto free_ptes;
+
+	sci->pgd = (pgd_t *)get_zeroed_page(GFP_KERNEL);
+	if (!sci->pgd)
+		goto free_backtrace;
+
+	err = sci_pagetable_init(tsk, sci->pgd);
+	if (err)
+		goto free_pgd;
+
+	sci_reset_backtrace(sci);
+
+	tsk->sci = sci;
+
+	return 0;
+
+free_pgd:
+	free_page((unsigned long)sci->pgd);
+free_backtrace:
+	kfree(sci->backtrace);
+free_ptes:
+	kfree(sci->ptes);
+free_sci:
+	kfree(sci);
+	return err;
+}
+
+int sci_init(struct task_struct *tsk)
+{
+	if (!tsk->sci) {
+		int err = sci_alloc(tsk);
+
+		if (err)
+			return err;
+	}
+
+	sci_sync_user_pagetable(tsk);
+
+	return 0;
+}
+
+void sci_exit(struct task_struct *tsk)
+{
+	struct sci_task_data *sci = tsk->sci;
+
+	if (!static_cpu_has(X86_FEATURE_SCI))
+		return;
+
+	if (!sci)
+		return;
+
+	sci_free_pagetable(tsk, tsk->sci->pgd);
+	free_page((unsigned long)sci->pgd);
+	kfree(sci->backtrace);
+	kfree(sci->ptes);
+	kfree(sci);
+}
+
+void sci_clear_data(void)
+{
+	struct sci_task_data *sci = current->sci;
+	int i;
+
+	if (WARN_ON(!sci))
+		return;
+
+	for (i = 0; i < sci->ptes_count; i++)
+		pte_clear(NULL, 0, sci->ptes[i]);
+
+	memset(sci->ptes, 0, sci->ptes_count);
+	sci->ptes_count = 0;
+
+	sci_reset_backtrace(sci);
+}
+
+static void sci_add_pte(struct sci_task_data *sci, pte_t *pte)
+{
+	int i;
+
+	for (i = sci->ptes_count - 1; i >= 0; i--)
+		if (pte == sci->ptes[i])
+			return;
+	sci->ptes[sci->ptes_count++] = pte;
+}
+
+static void sci_add_rip(struct sci_task_data *sci, unsigned long rip)
+{
+	int i;
+
+	for (i = sci->backtrace_size - 1; i >= 0; i--)
+		if (rip == sci->backtrace[i])
+			return;
+
+	sci->backtrace[sci->backtrace_size++] = rip;
+}
+
+static bool sci_verify_code_access(struct sci_task_data *sci,
+				   struct pt_regs *regs, unsigned long addr)
+{
+	char namebuf[KSYM_NAME_LEN];
+	unsigned long offset, size;
+	const char *symbol;
+	char *modname;
+
+
+	/* instruction fetch outside kernel or module text */
+	if (!(is_kernel_text(addr) || is_module_text_address(addr)))
+		return false;
+
+	/* no symbol matches the address */
+	symbol = kallsyms_lookup(addr, &size, &offset, &modname, namebuf);
+	if (!symbol)
+		return false;
+
+	/* BPF or ftrace? */
+	if (symbol != namebuf)
+		return false;
+
+	/* access in the middle of a function */
+	if (offset) {
+		int i = 0;
+
+		for (i = sci->backtrace_size - 1; i >= 0; i--) {
+			unsigned long rip = sci->backtrace[i];
+
+			/* allow jumps to the next page of already mapped one */
+			if ((addr >> PAGE_SHIFT) == ((rip >> PAGE_SHIFT) + 1))
+				return true;
+		}
+
+		return false;
+	}
+
+	sci_add_rip(sci, regs->ip);
+
+	return true;
+}
+
+bool sci_verify_and_map(struct pt_regs *regs, unsigned long addr,
+			unsigned long hw_error_code)
+{
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+	struct sci_task_data *sci = tsk->sci;
+	pte_t *pte;
+
+	/* run out of room for metadata, can't grant access */
+	if (sci->ptes_count >= SCI_MAX_PTES ||
+	    sci->backtrace_size >= SCI_MAX_BACKTRACE)
+		return false;
+
+	/* only code access is checked */
+	if (hw_error_code & X86_PF_INSTR &&
+	    !sci_verify_code_access(sci, regs, addr))
+		return false;
+
+	pte = sci_clone_page(mm, mm->pgd, sci->pgd, addr);
+	if (!pte)
+		return false;
+
+	sci_add_pte(sci, pte);
+
+	return true;
+}
+
+void __init sci_check_boottime_disable(void)
+{
+	char arg[5];
+	int ret;
+
+	if (!cpu_feature_enabled(X86_FEATURE_PCID)) {
+		pr_info("System call isolation requires PCID\n");
+		return;
+	}
+
+	/* Assume SCI is disabled unless explicitly overridden. */
+	ret = cmdline_find_option(boot_command_line, "sci", arg, sizeof(arg));
+	if (ret == 2 && !strncmp(arg, "on", 2)) {
+		setup_force_cpu_cap(X86_FEATURE_SCI);
+		pr_info("System call isolation is enabled\n");
+		return;
+	}
+
+	pr_info("System call isolation is disabled\n");
+}
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f9b43c9..cdcdb07 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1202,6 +1202,11 @@ struct task_struct {
 	unsigned long			prev_lowest_stack;
 #endif
 
+#ifdef CONFIG_SYSCALL_ISOLATION
+	unsigned long			in_isolated_syscall;
+	struct sci_task_data		*sci;
+#endif
+
 	/*
 	 * New fields for task_struct should be added above here, so that
 	 * they are included in the randomized portion of task_struct.
diff --git a/include/linux/sci.h b/include/linux/sci.h
new file mode 100644
index 0000000..7a6beac
--- /dev/null
+++ b/include/linux/sci.h
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef _LINUX_SCI_H
+#define _LINUX_SCI_H
+
+#ifdef CONFIG_SYSCALL_ISOLATION
+#include <asm/sci.h>
+#else
+static inline int sci_init(struct task_struct *tsk) { return 0; }
+static inline void sci_exit(struct task_struct *tsk) {}
+#endif
+
+#endif /* _LINUX_SCI_H */
-- 
2.7.4


^ permalink raw reply related

* [RFC PATCH 3/7] x86/entry/64: add infrastructure for switching to isolated syscall context
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

The isolated system calls will use a separate page table that does not map
the entire kernel. Exception and interrupts entries should switch the
context to the full kernel page tables and then restore it back to continue
the execution of the isolated system call.

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
---
 arch/x86/entry/calling.h               | 65 ++++++++++++++++++++++++++++++++++
 arch/x86/entry/entry_64.S              | 13 +++++--
 arch/x86/include/asm/processor-flags.h |  8 +++++
 arch/x86/include/asm/tlbflush.h        |  8 ++++-
 arch/x86/kernel/asm-offsets.c          |  7 ++++
 5 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h
index efb0d1b..766e74e 100644
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -187,6 +187,56 @@ For 32-bit we have the following conventions - kernel is built with
 #endif
 .endm
 
+#ifdef CONFIG_SYSCALL_ISOLATION
+
+#define SCI_PCID_BIT		X86_CR3_SCI_PCID_BIT
+
+#define THIS_CPU_sci_syscall   \
+	PER_CPU_VAR(cpu_sci) + SCI_SYSCALL
+
+#define THIS_CPU_sci_cr3_offset   \
+	PER_CPU_VAR(cpu_sci) + SCI_CR3_OFFSET
+
+.macro SAVE_AND_SWITCH_SCI_TO_KERNEL_CR3 scratch_reg:req save_reg:req
+	ALTERNATIVE "jmp .Ldone_\@", "", X86_FEATURE_SCI
+	movq	THIS_CPU_sci_syscall, \scratch_reg
+	cmpq	$0, \scratch_reg
+	je	.Ldone_\@
+	movq	%cr3, \scratch_reg
+	bt	$SCI_PCID_BIT, \scratch_reg
+	jc	.Lsci_context_\@
+	xorq	\save_reg, \save_reg
+	jmp	.Ldone_\@
+.Lsci_context_\@:
+	movq	\scratch_reg, \save_reg
+	addq	THIS_CPU_sci_cr3_offset, \scratch_reg
+	movq	\scratch_reg, %cr3
+.Ldone_\@:
+.endm
+
+.macro RESTORE_SCI_CR3 scratch_reg:req save_reg:req
+	ALTERNATIVE "jmp .Ldone_\@", "", X86_FEATURE_SCI
+	movq	THIS_CPU_sci_syscall, \scratch_reg
+	cmpq	$0, \scratch_reg
+	je	.Ldone_\@
+	movq	\save_reg, \scratch_reg
+	cmpq	$0, \scratch_reg
+	je	.Ldone_\@
+	xorq	\save_reg, \save_reg
+	movq	\scratch_reg, %cr3
+.Ldone_\@:
+.endm
+
+#else /* CONFIG_SYSCALL_ISOLATION */
+
+.macro SAVE_AND_SWITCH_SCI_TO_KERNEL_CR3 scratch_reg:req save_reg:req
+.endm
+
+.macro RESTORE_SCI_CR3 scratch_reg:req save_reg:req
+.endm
+
+#endif /* CONFIG_SYSCALL_ISOLATION */
+
 #ifdef CONFIG_PAGE_TABLE_ISOLATION
 
 /*
@@ -264,6 +314,21 @@ For 32-bit we have the following conventions - kernel is built with
 	ALTERNATIVE "jmp .Ldone_\@", "", X86_FEATURE_PTI
 	movq	%cr3, \scratch_reg
 	movq	\scratch_reg, \save_reg
+
+#ifdef CONFIG_SYSCALL_ISOLATION
+	/*
+	 * Test the SCI PCID bit. If set, then the SCI page tables are
+	 * active. If clear CR3 has either the kernel or user page
+	 * table active.
+	 */
+	ALTERNATIVE "jmp .Lcheck_user_pt_\@", "", X86_FEATURE_SCI
+	bt	$SCI_PCID_BIT, \scratch_reg
+	jnc	.Lcheck_user_pt_\@
+	addq	THIS_CPU_sci_cr3_offset, \scratch_reg
+	movq	\scratch_reg, %cr3
+	jmp	.Ldone_\@
+.Lcheck_user_pt_\@:
+#endif
 	/*
 	 * Test the user pagetable bit. If set, then the user page tables
 	 * are active. If clear CR3 already has the kernel page table
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 1f0efdb..3cef67b 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -543,7 +543,7 @@ ENTRY(interrupt_entry)
 	ENCODE_FRAME_POINTER 8
 
 	testb	$3, CS+8(%rsp)
-	jz	1f
+	jz	.Linterrupt_entry_kernel
 
 	/*
 	 * IRQ from user mode.
@@ -559,12 +559,17 @@ ENTRY(interrupt_entry)
 
 	CALL_enter_from_user_mode
 
-1:
+.Linterrupt_entry_done:
 	ENTER_IRQ_STACK old_rsp=%rdi save_ret=1
 	/* We entered an interrupt context - irqs are off: */
 	TRACE_IRQS_OFF
 
 	ret
+
+.Linterrupt_entry_kernel:
+	SAVE_AND_SWITCH_SCI_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14
+	jmp	.Linterrupt_entry_done
+
 END(interrupt_entry)
 _ASM_NOKPROBE(interrupt_entry)
 
@@ -656,6 +661,8 @@ retint_kernel:
 	 */
 	TRACE_IRQS_IRETQ
 
+	RESTORE_SCI_CR3 scratch_reg=%rax save_reg=%r14
+
 GLOBAL(restore_regs_and_return_to_kernel)
 #ifdef CONFIG_DEBUG_ENTRY
 	/* Assert that pt_regs indicates kernel mode. */
@@ -1263,6 +1270,8 @@ ENTRY(error_entry)
 	 * for these here too.
 	 */
 .Lerror_kernelspace:
+	SAVE_AND_SWITCH_SCI_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14
+
 	leaq	native_irq_return_iret(%rip), %rcx
 	cmpq	%rcx, RIP+8(%rsp)
 	je	.Lerror_bad_iret
diff --git a/arch/x86/include/asm/processor-flags.h b/arch/x86/include/asm/processor-flags.h
index 02c2cbd..eca9e17 100644
--- a/arch/x86/include/asm/processor-flags.h
+++ b/arch/x86/include/asm/processor-flags.h
@@ -53,4 +53,12 @@
 # define X86_CR3_PTI_PCID_USER_BIT	11
 #endif
 
+#ifdef CONFIG_SYSCALL_ISOLATION
+# if defined(X86_CR3_PTI_PCID_USER_BIT)
+#  define X86_CR3_SCI_PCID_BIT		(X86_CR3_PTI_PCID_USER_BIT - 1)
+# else
+#  define X86_CR3_SCI_PCID_BIT		11
+# endif
+#endif
+
 #endif /* _ASM_X86_PROCESSOR_FLAGS_H */
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index f4204bf..dc69cc4 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -54,7 +54,13 @@
 # define PTI_CONSUMED_PCID_BITS	0
 #endif
 
-#define CR3_AVAIL_PCID_BITS (X86_CR3_PCID_BITS - PTI_CONSUMED_PCID_BITS)
+#ifdef CONFIG_SYSCALL_ISOLATION
+# define SCI_CONSUMED_PCID_BITS	1
+#else
+# define SCI_CONSUMED_PCID_BITS	0
+#endif
+
+#define CR3_AVAIL_PCID_BITS (X86_CR3_PCID_BITS - PTI_CONSUMED_PCID_BITS - SCI_CONSUMED_PCID_BITS)
 
 /*
  * ASIDs are zero-based: 0->MAX_AVAIL_ASID are valid.  -1 below to account
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 168543d..f2c9cd3f 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -18,6 +18,7 @@
 #include <asm/bootparam.h>
 #include <asm/suspend.h>
 #include <asm/tlbflush.h>
+#include <asm/sci.h>
 
 #ifdef CONFIG_XEN
 #include <xen/interface/xen.h>
@@ -105,4 +106,10 @@ static void __used common(void)
 	OFFSET(TSS_sp0, tss_struct, x86_tss.sp0);
 	OFFSET(TSS_sp1, tss_struct, x86_tss.sp1);
 	OFFSET(TSS_sp2, tss_struct, x86_tss.sp2);
+
+#ifdef CONFIG_SYSCALL_ISOLATION
+	/* system calls isolation */
+	OFFSET(SCI_SYSCALL, sci_percpu_data, sci_syscall);
+	OFFSET(SCI_CR3_OFFSET, sci_percpu_data, sci_cr3_offset);
+#endif
 }
-- 
2.7.4


^ permalink raw reply related

* [RFC PATCH 4/7] x86/sci: hook up isolated system call entry and exit
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

When a system call is required to run in an isolated context, the CR3 will
be switched to the SCI page table a per-cpu variable will contain and
offset from the original CR3. This offset is used to switch back to the
full kernel context when a trap occurs during isolated system call.

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
---
 arch/x86/entry/common.c      | 61 ++++++++++++++++++++++++++++++++++++++++++++
 arch/x86/kernel/process_64.c |  5 ++++
 kernel/exit.c                |  3 +++
 3 files changed, 69 insertions(+)

diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index 7bc105f..8f2a6fd 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -25,12 +25,14 @@
 #include <linux/uprobes.h>
 #include <linux/livepatch.h>
 #include <linux/syscalls.h>
+#include <linux/sci.h>
 
 #include <asm/desc.h>
 #include <asm/traps.h>
 #include <asm/vdso.h>
 #include <linux/uaccess.h>
 #include <asm/cpufeature.h>
+#include <asm/tlbflush.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/syscalls.h>
@@ -269,6 +271,50 @@ __visible inline void syscall_return_slowpath(struct pt_regs *regs)
 }
 
 #ifdef CONFIG_X86_64
+
+#ifdef CONFIG_SYSCALL_ISOLATION
+static inline bool sci_required(unsigned long nr)
+{
+	return false;
+}
+
+static inline unsigned long sci_syscall_enter(unsigned long nr)
+{
+	unsigned long sci_cr3, kernel_cr3;
+	unsigned long asid;
+
+	kernel_cr3 = __read_cr3();
+	asid = kernel_cr3 & ~PAGE_MASK;
+
+	sci_cr3 = build_cr3(current->sci->pgd, 0) & PAGE_MASK;
+	sci_cr3 |= (asid | (1 << X86_CR3_SCI_PCID_BIT));
+
+	current->in_isolated_syscall = 1;
+	current->sci->cr3_offset = kernel_cr3 - sci_cr3;
+
+	this_cpu_write(cpu_sci.sci_syscall, 1);
+	this_cpu_write(cpu_sci.sci_cr3_offset, current->sci->cr3_offset);
+
+	write_cr3(sci_cr3);
+
+	return kernel_cr3;
+}
+
+static inline void sci_syscall_exit(unsigned long cr3)
+{
+	if (cr3) {
+		write_cr3(cr3);
+		current->in_isolated_syscall = 0;
+		this_cpu_write(cpu_sci.sci_syscall, 0);
+		sci_clear_data();
+	}
+}
+#else
+static inline bool sci_required(unsigned long nr) { return false; }
+static inline unsigned long sci_syscall_enter(unsigned long nr) { return 0; }
+static inline void sci_syscall_exit(unsigned long cr3) {}
+#endif
+
 __visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)
 {
 	struct thread_info *ti;
@@ -286,10 +332,25 @@ __visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)
 	 */
 	nr &= __SYSCALL_MASK;
 	if (likely(nr < NR_syscalls)) {
+		unsigned long sci_cr3 = 0;
+
 		nr = array_index_nospec(nr, NR_syscalls);
+
+		if (sci_required(nr)) {
+			int err = sci_init(current);
+
+			if (err) {
+				regs->ax = err;
+				goto err_return_from_syscall;
+			}
+			sci_cr3 = sci_syscall_enter(nr);
+		}
+
 		regs->ax = sys_call_table[nr](regs);
+		sci_syscall_exit(sci_cr3);
 	}
 
+err_return_from_syscall:
 	syscall_return_slowpath(regs);
 }
 #endif
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 6a62f4a..b8aa624 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -55,6 +55,8 @@
 #include <asm/resctrl_sched.h>
 #include <asm/unistd.h>
 #include <asm/fsgsbase.h>
+#include <asm/sci.h>
+
 #ifdef CONFIG_IA32_EMULATION
 /* Not included via unistd.h */
 #include <asm/unistd_32_ia32.h>
@@ -581,6 +583,9 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
 
 	switch_to_extra(prev_p, next_p);
 
+	/* update syscall isolation per-cpu data */
+	sci_switch_to(next_p);
+
 #ifdef CONFIG_XEN_PV
 	/*
 	 * On Xen PV, IOPL bits in pt_regs->flags have no effect, and
diff --git a/kernel/exit.c b/kernel/exit.c
index 2639a30..8e81353 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -62,6 +62,7 @@
 #include <linux/random.h>
 #include <linux/rcuwait.h>
 #include <linux/compat.h>
+#include <linux/sci.h>
 
 #include <linux/uaccess.h>
 #include <asm/unistd.h>
@@ -859,6 +860,8 @@ void __noreturn do_exit(long code)
 	tsk->exit_code = code;
 	taskstats_exit(tsk, group_dead);
 
+	sci_exit(tsk);
+
 	exit_mm();
 
 	if (group_dead)
-- 
2.7.4


^ permalink raw reply related

* [RFC PATCH 5/7] x86/mm/fault: hook up SCI verification
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

If a system call runs in isolated context, it's accesses to kernel code and
data will be verified by SCI susbsytem.

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
---
 arch/x86/mm/fault.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 9d5c75f..baa2a2f 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -18,6 +18,7 @@
 #include <linux/uaccess.h>		/* faulthandler_disabled()	*/
 #include <linux/efi.h>			/* efi_recover_from_page_fault()*/
 #include <linux/mm_types.h>
+#include <linux/sci.h>			/* sci_verify_and_map()		*/
 
 #include <asm/cpufeature.h>		/* boot_cpu_has, ...		*/
 #include <asm/traps.h>			/* dotraplinkage, ...		*/
@@ -1254,6 +1255,30 @@ static int fault_in_kernel_space(unsigned long address)
 	return address >= TASK_SIZE_MAX;
 }
 
+#ifdef CONFIG_SYSCALL_ISOLATION
+static int sci_fault(struct pt_regs *regs, unsigned long hw_error_code,
+		     unsigned long address)
+{
+	struct task_struct *tsk = current;
+
+	if (!tsk->in_isolated_syscall)
+		return 0;
+
+	if (!sci_verify_and_map(regs, address, hw_error_code)) {
+		this_cpu_write(cpu_sci.sci_syscall, 0);
+		no_context(regs, hw_error_code, address, SIGKILL, 0);
+	}
+
+	return 1;
+}
+#else
+static inline int sci_fault(struct pt_regs *regs, unsigned long hw_error_code,
+			    unsigned long address)
+{
+	return 0;
+}
+#endif
+
 /*
  * Called for all faults where 'address' is part of the kernel address
  * space.  Might get called for faults that originate from *code* that
@@ -1301,6 +1326,9 @@ do_kern_addr_fault(struct pt_regs *regs, unsigned long hw_error_code,
 	if (kprobes_fault(regs))
 		return;
 
+	if (sci_fault(regs, hw_error_code, address))
+		return;
+
 	/*
 	 * Note, despite being a "bad area", there are quite a few
 	 * acceptable reasons to get here, such as erratum fixups
-- 
2.7.4


^ permalink raw reply related

* [RFC PATCH 7/7] sci: add example system calls to exercse SCI
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
---
 arch/x86/entry/common.c                |  6 +++-
 arch/x86/entry/syscalls/syscall_64.tbl |  3 ++
 kernel/Makefile                        |  2 +-
 kernel/sci-examples.c                  | 52 ++++++++++++++++++++++++++++++++++
 4 files changed, 61 insertions(+), 2 deletions(-)
 create mode 100644 kernel/sci-examples.c

diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index 8f2a6fd..be0e1a7 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -275,7 +275,11 @@ __visible inline void syscall_return_slowpath(struct pt_regs *regs)
 #ifdef CONFIG_SYSCALL_ISOLATION
 static inline bool sci_required(unsigned long nr)
 {
-	return false;
+	if (!static_cpu_has(X86_FEATURE_SCI))
+		return false;
+	if (nr < __NR_get_answer)
+		return false;
+	return true;
 }
 
 static inline unsigned long sci_syscall_enter(unsigned long nr)
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index f0b1709..a25e838 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -343,6 +343,9 @@
 332	common	statx			__x64_sys_statx
 333	common	io_pgetevents		__x64_sys_io_pgetevents
 334	common	rseq			__x64_sys_rseq
+335	64	get_answer		__x64_sys_get_answer
+336	64	sci_write_dmesg		__x64_sys_sci_write_dmesg
+337	64	sci_write_dmesg_bad	__x64_sys_sci_write_dmesg_bad
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/kernel/Makefile b/kernel/Makefile
index 6aa7543..d6441d0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@ obj-y     = fork.o exec_domain.o panic.o \
 	    extable.o params.o \
 	    kthread.o sys_ni.o nsproxy.o \
 	    notifier.o ksysfs.o cred.o reboot.o \
-	    async.o range.o smpboot.o ucount.o
+	    async.o range.o smpboot.o ucount.o sci-examples.o
 
 obj-$(CONFIG_MODULES) += kmod.o
 obj-$(CONFIG_MULTIUSER) += groups.o
diff --git a/kernel/sci-examples.c b/kernel/sci-examples.c
new file mode 100644
index 0000000..9bfaad0
--- /dev/null
+++ b/kernel/sci-examples.c
@@ -0,0 +1,52 @@
+#include <linux/kernel.h>
+#include <linux/pid.h>
+#include <linux/syscalls.h>
+#include <linux/hugetlb.h>
+#include <asm/special_insns.h>
+
+SYSCALL_DEFINE0(get_answer)
+{
+	return 42;
+}
+
+#define BUF_SIZE 1024
+
+typedef void (*foo)(void);
+
+SYSCALL_DEFINE2(sci_write_dmesg, const char __user *, ubuf, size_t, count)
+{
+	char buf[BUF_SIZE];
+
+	if (!ubuf || count >= BUF_SIZE)
+		return -EINVAL;
+
+	buf[count] = '\0';
+	if (copy_from_user(buf, ubuf, count))
+		return -EFAULT;
+
+	printk("%s\n", buf);
+
+	return count;
+}
+
+SYSCALL_DEFINE2(sci_write_dmesg_bad, const char __user *, ubuf, size_t, count)
+{
+	unsigned long addr = (unsigned long)(void *)hugetlb_reserve_pages;
+	char buf[BUF_SIZE];
+	foo func1;
+
+	addr += 0xc5;
+	func1 = (foo)(void *)addr;
+	func1();
+
+	if (!ubuf || count >= BUF_SIZE)
+		return -EINVAL;
+
+	buf[count] = '\0';
+	if (copy_from_user(buf, ubuf, count))
+		return -EFAULT;
+
+	printk("%s\n", buf);
+
+	return count;
+}
-- 
2.7.4


^ permalink raw reply related

* [RFC PATCH 6/7] security: enable system call isolation in kernel config
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

Add SYSCALL_ISOLATION Kconfig option to enable build of SCI infrastructure.

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
---
 security/Kconfig | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/security/Kconfig b/security/Kconfig
index e4fe2f3..0c6929a 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -65,6 +65,16 @@ config PAGE_TABLE_ISOLATION
 
 	  See Documentation/x86/pti.txt for more details.
 
+config SYSCALL_ISOLATION
+	bool "System call isolation"
+	default n
+	depends on PAGE_TABLE_ISOLATION && !X86_PAE
+	help
+	  This is an experimental feature to allow executing system
+	  calls in an isolated address space.
+
+	  If you are unsure how to answer this question, answer N.
+
 config SECURITY_INFINIBAND
 	bool "Infiniband Security Hooks"
 	depends on SECURITY && INFINIBAND
-- 
2.7.4


^ permalink raw reply related

* [RFC PATCH 1/7] x86/cpufeatures: add X86_FEATURE_SCI
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport
In-Reply-To: <1556228754-12996-1-git-send-email-rppt@linux.ibm.com>

The X86_FEATURE_SCI will be set when system call isolation is enabled.

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
---
 arch/x86/include/asm/cpufeatures.h       | 1 +
 arch/x86/include/asm/disabled-features.h | 8 +++++++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 6d61225..a01c6dd 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -221,6 +221,7 @@
 #define X86_FEATURE_ZEN			( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */
 #define X86_FEATURE_L1TF_PTEINV		( 7*32+29) /* "" L1TF workaround PTE inversion */
 #define X86_FEATURE_IBRS_ENHANCED	( 7*32+30) /* Enhanced IBRS */
+#define X86_FEATURE_SCI			( 7*32+31) /* "" System call isolation */
 
 /* Virtualization flags: Linux defined, word 8 */
 #define X86_FEATURE_TPR_SHADOW		( 8*32+ 0) /* Intel TPR Shadow */
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index a5ea841..79947f0 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -62,6 +62,12 @@
 # define DISABLE_PTI		(1 << (X86_FEATURE_PTI & 31))
 #endif
 
+#ifdef CONFIG_SYSCALL_ISOLATION
+# define DISABLE_SCI		0
+#else
+# define DISABLE_SCI		(1 << (X86_FEATURE_SCI & 31))
+#endif
+
 /*
  * Make sure to add features to the correct mask
  */
@@ -72,7 +78,7 @@
 #define DISABLED_MASK4	(DISABLE_PCID)
 #define DISABLED_MASK5	0
 #define DISABLED_MASK6	0
-#define DISABLED_MASK7	(DISABLE_PTI)
+#define DISABLED_MASK7	(DISABLE_PTI|DISABLE_SCI)
 #define DISABLED_MASK8	0
 #define DISABLED_MASK9	(DISABLE_MPX|DISABLE_SMAP)
 #define DISABLED_MASK10	0
-- 
2.7.4


^ permalink raw reply related

* [RFC PATCH 0/7] x86: introduce system calls addess space isolation
From: Mike Rapoport @ 2019-04-25 21:45 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexandre Chartre, Andy Lutomirski, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, James Bottomley, Jonathan Adams,
	Kees Cook, Paul Turner, Peter Zijlstra, Thomas Gleixner, linux-mm,
	linux-security-module, x86, Mike Rapoport

Hi,

Address space isolation has been used to protect the kernel from the
userspace and userspace programs from each other since the invention of the
virtual memory.

Assuming that kernel bugs and therefore vulnerabilities are inevitable it
might be worth isolating parts of the kernel to minimize damage that these
vulnerabilities can cause.

The idea here is to allow an untrusted user access to a potentially
vulnerable kernel in such a way that any kernel vulnerability they find to
exploit is either prevented or the consequences confined to their isolated
address space such that the compromise attempt has minimal impact on other
tenants or the protected structures of the monolithic kernel.  Although we
hope to prevent many classes of attack, the first target we're looking at
is ROP gadget protection.

These patches implement a "system call isolation (SCI)" mechanism that
allows running system calls in an isolated address space with reduced page
tables to prevent ROP attacks.

ROP attacks involve corrupting the stack return address to repoint it to a
segment of code you know exists in the kernel that can be used to perform
the action you need to exploit the system.

The idea behind the prevention is that if we fault in pages in the
execution path, we can compare target address against the kernel symbol
table.  So if we're in a function, we allow local jumps (and simply falling
of the end of a page) but if we're jumping to a new function it must be to
an external label in the symbol table.  Since ROP attacks are all about
jumping to gadget code which is effectively in the middle of real
functions, the jumps they induce are to code that doesn't have an external
symbol, so it should mostly detect when they happen.

This is very early POC, it's able to run the simple dummy system calls and
a little bit beyond that, but it's not yet stable and robust enough to boot
a system with system call isolation enabled for all system calls. Still, we
wanted to get some feedback about the concept in general as early as
possible.
 
At this time we are not suggesting any API that will enable the system
calls isolation. Because of the overhead required for this, it should only
be activated for processes or containers we know should be untrusted. We
still have no actual numbers, but surely forcing page faults during system
call execution will not come for free.

One possible way is to create a namespace, and force the system calls
isolation on all the processes in that namespace. Another thing that came
to mind was to use a seccomp filter to allow fine grained control of this
feature.

The current implementation is pretty much x86-centric, but the general idea
can be used on other architectures.

A brief TOC of the set:
* patch 1 adds  definitions of X86_FEATURE_SCI
* patch 2 is the core implementation of system calls isolation (SCI)
* patches 3-5 add hooks to SCI at entry paths and in the page fault
  handler 
* patch 6 enables the SCI in Kconfig
* patch 7 includes example dummy system calls that are used to
  demonstrate the SCI in action.

Mike Rapoport (7):
  x86/cpufeatures: add X86_FEATURE_SCI
  x86/sci: add core implementation for system call isolation
  x86/entry/64: add infrastructure for switching to isolated syscall
    context
  x86/sci: hook up isolated system call entry and exit
  x86/mm/fault: hook up SCI verification
  security: enable system call isolation in kernel config
  sci: add example system calls to exercse SCI

 arch/x86/entry/calling.h                 |  65 ++++
 arch/x86/entry/common.c                  |  65 ++++
 arch/x86/entry/entry_64.S                |  13 +-
 arch/x86/entry/syscalls/syscall_64.tbl   |   3 +
 arch/x86/include/asm/cpufeatures.h       |   1 +
 arch/x86/include/asm/disabled-features.h |   8 +-
 arch/x86/include/asm/processor-flags.h   |   8 +
 arch/x86/include/asm/sci.h               |  55 +++
 arch/x86/include/asm/tlbflush.h          |   8 +-
 arch/x86/kernel/asm-offsets.c            |   7 +
 arch/x86/kernel/process_64.c             |   5 +
 arch/x86/mm/Makefile                     |   1 +
 arch/x86/mm/fault.c                      |  28 ++
 arch/x86/mm/init.c                       |   2 +
 arch/x86/mm/sci.c                        | 608 +++++++++++++++++++++++++++++++
 include/linux/sched.h                    |   5 +
 include/linux/sci.h                      |  12 +
 kernel/Makefile                          |   2 +-
 kernel/exit.c                            |   3 +
 kernel/sci-examples.c                    |  52 +++
 security/Kconfig                         |  10 +
 21 files changed, 956 insertions(+), 5 deletions(-)
 create mode 100644 arch/x86/include/asm/sci.h
 create mode 100644 arch/x86/mm/sci.c
 create mode 100644 include/linux/sci.h
 create mode 100644 kernel/sci-examples.c

-- 
2.7.4


^ permalink raw reply

* Re: [PATCH 26/27] debugfs: Restrict debugfs when the kernel is locked down
From: Matthew Garrett @ 2019-04-25 21:44 UTC (permalink / raw)
  To: Vasily Gorbik
  Cc: James Morris, LSM List, Linux Kernel Mailing List, David Howells
In-Reply-To: <your-ad-here.call-01556189354-ext-1943@work.hours>

On Thu, Apr 25, 2019 at 3:49 AM Vasily Gorbik <gor@linux.ibm.com> wrote:
> I would appreciate if you consider that change, possibly just
> squashing into yours.

Thanks, I've squashed that in.

^ permalink raw reply

* Re: [PATCH v4 16/23] vmalloc: Add flag for free of special permsissions
From: Edgecombe, Rick P @ 2019-04-25 21:22 UTC (permalink / raw)
  To: peterz@infradead.org
  Cc: linux-kernel@vger.kernel.org, linux-integrity@vger.kernel.org,
	ard.biesheuvel@linaro.org, Dock, Deneen T, tglx@linutronix.de,
	linux-mm@kvack.org, nadav.amit@gmail.com,
	dave.hansen@linux.intel.com,
	linux-security-module@vger.kernel.org, x86@kernel.org,
	akpm@linux-foundation.org, hpa@zytor.com, kristen@linux.intel.com,
	mingo@redhat.com, linux_dti@icloud.com, luto@kernel.org,
	will.deacon@arm.com, bp@alien8.de,
	kernel-hardening@lists.openwall.com
In-Reply-To: <20190425203845.GA12232@hirez.programming.kicks-ass.net>

On Thu, 2019-04-25 at 22:38 +0200, Peter Zijlstra wrote:
> On Mon, Apr 22, 2019 at 11:57:58AM -0700, Rick Edgecombe wrote:
> > Add a new flag VM_FLUSH_RESET_PERMS, for enabling vfree operations to
> > immediately clear executable TLB entries before freeing pages, and handle
> > resetting permissions on the directmap. This flag is useful for any kind
> > of memory with elevated permissions, or where there can be related
> > permissions changes on the directmap. Today this is RO+X and RO memory.
> > 
> > Although this enables directly vfreeing non-writeable memory now,
> > non-writable memory cannot be freed in an interrupt because the allocation
> > itself is used as a node on deferred free list. So when RO memory needs to
> > be freed in an interrupt the code doing the vfree needs to have its own
> > work queue, as was the case before the deferred vfree list was added to
> > vmalloc.
> > 
> > For architectures with set_direct_map_ implementations this whole operation
> > can be done with one TLB flush when centralized like this. For others with
> > directmap permissions, currently only arm64, a backup method using
> > set_memory functions is used to reset the directmap. When arm64 adds
> > set_direct_map_ functions, this backup can be removed.
> > 
> > When the TLB is flushed to both remove TLB entries for the vmalloc range
> > mapping and the direct map permissions, the lazy purge operation could be
> > done to try to save a TLB flush later. However today vm_unmap_aliases
> > could flush a TLB range that does not include the directmap. So a helper
> > is added with extra parameters that can allow both the vmalloc address and
> > the direct mapping to be flushed during this operation. The behavior of the
> > normal vm_unmap_aliases function is unchanged.
> > +static inline void set_vm_flush_reset_perms(void *addr)
> > +{
> > +	struct vm_struct *vm = find_vm_area(addr);
> > +
> > +	if (vm)
> > +		vm->flags |= VM_FLUSH_RESET_PERMS;
> > +}
> 
> So, previously in the series we added NX to module_alloc() and fixed up
> all the usage site. And now we're going through those very same sites to
> add set_vm_flush_reset_perms().
> 
> Why isn't module_alloc() calling the above function and avoid sprinkling
> it all over the place again?

Yea, that could make it more automatic, but there are some advantages to how it
is currently.

One is that most arch's have their own module_alloc(), and so calling
set_vm_flush_reset_perms() in kernel/module.c catches all architectures.
Otherwise it would be added in each arch which would be more sites.

The other reason is that the flush isn't actually needed until after the memory
is made executable, so we don't bother flushing if the allocation never gets set
executable. When that happens is only known by the callers of module_alloc().

Thanks,

Rick

^ permalink raw reply

* Re: [PATCH v4 00/23] Merge text_poke fixes and executable lockdowns
From: Peter Zijlstra @ 2019-04-25 20:49 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: Borislav Petkov, Andy Lutomirski, Ingo Molnar, linux-kernel, x86,
	hpa, Thomas Gleixner, Nadav Amit, Dave Hansen, linux_dti,
	linux-integrity, linux-security-module, akpm, kernel-hardening,
	linux-mm, will.deacon, ard.biesheuvel, kristen, deneen.t.dock
In-Reply-To: <20190425204820.GB12232@hirez.programming.kicks-ass.net>

On Thu, Apr 25, 2019 at 10:48:20PM +0200, Peter Zijlstra wrote:
> On Mon, Apr 22, 2019 at 11:57:42AM -0700, Rick Edgecombe wrote:
> > Andy Lutomirski (1):
> >   x86/mm: Introduce temporary mm structs
> > 
> > Nadav Amit (15):
> >   Fix "x86/alternatives: Lockdep-enforce text_mutex in text_poke*()"
> >   x86/jump_label: Use text_poke_early() during early init
> >   x86/mm: Save DRs when loading a temporary mm
> >   fork: Provide a function for copying init_mm
> >   x86/alternative: Initialize temporary mm for patching
> >   x86/alternative: Use temporary mm for text poking
> >   x86/kgdb: Avoid redundant comparison of patched code
> >   x86/ftrace: Set trampoline pages as executable
> >   x86/kprobes: Set instruction page as executable
> >   x86/module: Avoid breaking W^X while loading modules
> >   x86/jump-label: Remove support for custom poker
> >   x86/alternative: Remove the return value of text_poke_*()
> >   x86/alternative: Comment about module removal races
> >   tlb: provide default nmi_uaccess_okay()
> >   bpf: Fail bpf_probe_write_user() while mm is switched
> > 
> > Rick Edgecombe (7):
> >   x86/mm/cpa: Add set_direct_map_ functions
> >   mm: Make hibernate handle unmapped pages
> >   vmalloc: Add flag for free of special permsissions
> >   modules: Use vmalloc special flag
> >   bpf: Use vmalloc special flag
> >   x86/ftrace: Use vmalloc special flag
> >   x86/kprobes: Use vmalloc special flag
> 
> This all looks good to me, I'll queue them tomorrow when I'm awake
> again. I'll move the last two patches to early in the series, since it
> appears to me they're fixes and should be in place before we make the
> situation worse with the temporary mm swizzling for text_poke.
> 
> If you want to post a new version of patch 4 before then, that'd be
> awesome, otherwise I'll see if I can do those few edits myself.

Patch 3 that was, see, I can't even count straight atm :-)

^ permalink raw reply

* Re: [PATCH v4 00/23] Merge text_poke fixes and executable lockdowns
From: Peter Zijlstra @ 2019-04-25 20:48 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: Borislav Petkov, Andy Lutomirski, Ingo Molnar, linux-kernel, x86,
	hpa, Thomas Gleixner, Nadav Amit, Dave Hansen, linux_dti,
	linux-integrity, linux-security-module, akpm, kernel-hardening,
	linux-mm, will.deacon, ard.biesheuvel, kristen, deneen.t.dock
In-Reply-To: <20190422185805.1169-1-rick.p.edgecombe@intel.com>

On Mon, Apr 22, 2019 at 11:57:42AM -0700, Rick Edgecombe wrote:
> Andy Lutomirski (1):
>   x86/mm: Introduce temporary mm structs
> 
> Nadav Amit (15):
>   Fix "x86/alternatives: Lockdep-enforce text_mutex in text_poke*()"
>   x86/jump_label: Use text_poke_early() during early init
>   x86/mm: Save DRs when loading a temporary mm
>   fork: Provide a function for copying init_mm
>   x86/alternative: Initialize temporary mm for patching
>   x86/alternative: Use temporary mm for text poking
>   x86/kgdb: Avoid redundant comparison of patched code
>   x86/ftrace: Set trampoline pages as executable
>   x86/kprobes: Set instruction page as executable
>   x86/module: Avoid breaking W^X while loading modules
>   x86/jump-label: Remove support for custom poker
>   x86/alternative: Remove the return value of text_poke_*()
>   x86/alternative: Comment about module removal races
>   tlb: provide default nmi_uaccess_okay()
>   bpf: Fail bpf_probe_write_user() while mm is switched
> 
> Rick Edgecombe (7):
>   x86/mm/cpa: Add set_direct_map_ functions
>   mm: Make hibernate handle unmapped pages
>   vmalloc: Add flag for free of special permsissions
>   modules: Use vmalloc special flag
>   bpf: Use vmalloc special flag
>   x86/ftrace: Use vmalloc special flag
>   x86/kprobes: Use vmalloc special flag

This all looks good to me, I'll queue them tomorrow when I'm awake
again. I'll move the last two patches to early in the series, since it
appears to me they're fixes and should be in place before we make the
situation worse with the temporary mm swizzling for text_poke.

If you want to post a new version of patch 4 before then, that'd be
awesome, otherwise I'll see if I can do those few edits myself.

^ permalink raw reply

* Re: [PATCH v4 16/23] vmalloc: Add flag for free of special permsissions
From: Peter Zijlstra @ 2019-04-25 20:38 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: Borislav Petkov, Andy Lutomirski, Ingo Molnar, linux-kernel, x86,
	hpa, Thomas Gleixner, Nadav Amit, Dave Hansen, linux_dti,
	linux-integrity, linux-security-module, akpm, kernel-hardening,
	linux-mm, will.deacon, ard.biesheuvel, kristen, deneen.t.dock
In-Reply-To: <20190422185805.1169-17-rick.p.edgecombe@intel.com>

On Mon, Apr 22, 2019 at 11:57:58AM -0700, Rick Edgecombe wrote:
> Add a new flag VM_FLUSH_RESET_PERMS, for enabling vfree operations to
> immediately clear executable TLB entries before freeing pages, and handle
> resetting permissions on the directmap. This flag is useful for any kind
> of memory with elevated permissions, or where there can be related
> permissions changes on the directmap. Today this is RO+X and RO memory.
> 
> Although this enables directly vfreeing non-writeable memory now,
> non-writable memory cannot be freed in an interrupt because the allocation
> itself is used as a node on deferred free list. So when RO memory needs to
> be freed in an interrupt the code doing the vfree needs to have its own
> work queue, as was the case before the deferred vfree list was added to
> vmalloc.
> 
> For architectures with set_direct_map_ implementations this whole operation
> can be done with one TLB flush when centralized like this. For others with
> directmap permissions, currently only arm64, a backup method using
> set_memory functions is used to reset the directmap. When arm64 adds
> set_direct_map_ functions, this backup can be removed.
> 
> When the TLB is flushed to both remove TLB entries for the vmalloc range
> mapping and the direct map permissions, the lazy purge operation could be
> done to try to save a TLB flush later. However today vm_unmap_aliases
> could flush a TLB range that does not include the directmap. So a helper
> is added with extra parameters that can allow both the vmalloc address and
> the direct mapping to be flushed during this operation. The behavior of the
> normal vm_unmap_aliases function is unchanged.

> +static inline void set_vm_flush_reset_perms(void *addr)
> +{
> +	struct vm_struct *vm = find_vm_area(addr);
> +
> +	if (vm)
> +		vm->flags |= VM_FLUSH_RESET_PERMS;
> +}

So, previously in the series we added NX to module_alloc() and fixed up
all the usage site. And now we're going through those very same sites to
add set_vm_flush_reset_perms().

Why isn't module_alloc() calling the above function and avoid sprinkling
it all over the place again?

^ permalink raw reply

* Re: smack ( on host ) + apparmor ( on docker ) - possible ?
From: John Johansen @ 2019-04-25 19:22 UTC (permalink / raw)
  To: Casey Schaufler, shrawan kumar, Casey Schaufler,
	smack-discuss-owner
  Cc: Linux Security Module list
In-Reply-To: <8234adc6-18c6-feab-ca04-d4cfbc64373f@schaufler-ca.com>

[-- Attachment #1: Type: text/plain, Size: 4926 bytes --]

On 4/25/19 8:52 AM, Casey Schaufler wrote:
> On 4/24/2019 9:37 PM, shrawan kumar wrote:
>> Dear Casey ,
>>
>> For one of my embedded project ,?? the requirement is to run a set of process under *Docker* and each process inside Docker needs to be sandboxed using *AppArmour*. However, the host from where Docker is launched is *Smack* enabled . We are using *smack* as default security on host .
>>
>> Is the above combination possible ?
> 
> With the current upstream kernel, no. You can't run more
> than one "major" security module at a time. As of 5.1 you
> will have more flexibility, but still not enough for Smack
> and AppArmor to coexist. Development is underway for the
> next phase of module stacking, which will be proposed for
> 5.3 and can be found:
> 
> git://github.com/cschaufler/lsm-stacking.git#stack-5.1-rc2-apparmor
> 
> With this patch set you can run Smack and AppArmor together.
> What I don't know is how you would configure AppArmor so that
> you can sub-configure your containers. I've added John Johansen
> to the thread. He is the AppArmor expert who has been working
> on AppArmor namespaces.
> 
> To the best of my knowledge no one has done what you want,
> but supporting your configuration is an explicit goal. We
> would be more than happy to help you in your efforts.
> 

As Casey says not possible with the current upstream kernel.
You do have a few options currently.

You can use the patchset Casey pointed you at or you can
cherry-pick a reduced size stacking variant from the Ubuntu 18.04
or 19.04 kernels. The patchsets are quite different but they are
effectively equivalent. They cutout patches that are not needed
to stack smack/selinux with apparmor.


Once you have a kernel that supports the stacking patchset. The
setup is fairly easy.

You need to boot with both smack and apparmor enabled. Under the
current LSM stacking and 19.04 kernels your kernel parameter is

lsm="yama,loadpin,safesetid,integrity,smack,apparmor"

or config

CONFIG_LSM="yama,loadpin,safesetid,integrity,smack,apparmor"


and the 18.04 version

security=smack,apparmor

there is a config option as well, but I don't remember it off the
top of my head.


Once you have this you can achieve what you want. You don't need
apparmor policy on the host, and you might not even need an
apparmor userspace, as long as each docker container has its own
apparmor userspace and policy.


For each container you are going to do three things.
0. Make sure securityfs is mounted (default location would be
   /sys/kernel/security/)
1. Create an apparmor namespace
2. Switch the display LSM (this does not exist in current upstream
LSM stacking, but hopefully 5.3)
3. Put the root container task into the apparmor namespace.

there is flexibility in the ordering but if you stick to the above
ordering you avoid some of the potential problems.

1. Creating an apparmor namespace.

AppArmor actually provides two ways for this to happen. Through its
fs interface, and through policy. I am going to assume you want to
skip policy on the host.

if your task is unconfined by apparmor (it will be if you don't
have policy on the host) and it has cap mac_admin (root). Then
you can do

  mkdir /sys/kernel/security/apparmor/policy/namespaces/$(NS_NAME)

where $(NS_NAME) is basically limited to alphanum with the first
character being alpha. And unfortunately there is no way to auto
reap apparmor policy namespaces so when your container dies.

  rmdir /sys/kernel/security/apparmor/policy/namespaces/$(NS_NAME)


2. Switch the display LSM, you basically have to write
  "apparmor" to /proc/current/attr/display

   I've attached a basic utility program (lsm-exec), I use to do this.
   You can rip the code you need from it. Basic usage is

   lsm-exec -l apparmor -- bash

   where bash can be replaced with any executable

3. Put the root task into the apparmor namespace. You can either
   use aa-exec from the apparmor userspace project
   https://gitlab.com/apparmor/apparmor/blob/master/binutils/aa_exec.c

   with basic usage of
   aa-exec -p ":$(NS_NAME):unconfined" -- bash

   where again you can replace bash

   Alternately you can skip aa-exec by writing

      "exec :$(NS_NAME):unconfined" to /proc/self/attr/exec

   The profile transition to the new namespace will happen at the next
   exec and that task and its children will inherit confinement in
   the policy namespace.

The task is now in you apparmor policy namespace, and assuming it
has cap mac_admin can load the containers policy.

I should note apparmor audit messages go to the audit subsystem which
currently isn't namespaced.

It is possible to have the host load the policy to the namespace for
the container if that is what you are looking for. And it is also
possible to have apparmor policy on the host along with smack if you
need to do that and some other interesting combinations, but I won't
bore you with details unless you ask.



[-- Attachment #2: lsm_exec.c --]
[-- Type: text/x-csrc, Size: 4498 bytes --]

/*
 *   Copyright (c) 2015
 *   Canonical, Ltd. (All rights reserved)
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of version 2 of the GNU General Public
 *   License published by the Free Software Foundation.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, contact Novell, Inc. or Canonical
 *   Ltd.
 */

#include <errno.h>
#include <getopt.h>
#include <libintl.h>
#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/apparmor.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define _(s) gettext(s)

static const char *opt_lsm = NULL;
static bool opt_debug = false;
static bool opt_verbose = false;
static pid_t pid = 0;

static void usage(const char *name, bool error)
{
	FILE *stream = stdout;
	int status = EXIT_SUCCESS;

	if (error) {
		stream = stderr;
		status = EXIT_FAILURE;
	}

	fprintf(stream,
		_("USAGE: %s [OPTIONS] <prog> <args>\n"
		"\n"
		"Set <prog> to have the specified display LSM.\n"
		"\n"
		"OPTIONS:\n"
		"  -l lsm, --lsm=LSM		LSM that should be displayed\n"
		"  -d, --debug			show messages with debugging information\n"
		"  -v, --verbose       		show messages with stats\n"
		"  -h, --help			display this help\n"
		"\n"), name);
	exit(status);
}

#define error(fmt, args...) _error(_("[%ld] lsm-exec: ERROR: " fmt "\n"), (long)pid, ## args)
static void _error(const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	va_end(args);
	exit(EXIT_FAILURE);
}

#define debug(fmt, args...) _debug(_("[%ld] lsm-exec: DEBUG: " fmt "\n"), (long)pid, ## args)
static void _debug(const char *fmt, ...)
{
	va_list args;

	if (!opt_debug)
		return;

	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	va_end(args);
}

#define verbose(fmt, args...) _verbose(_("[%ld] " fmt "\n"), (long)pid, ## args)
static void _verbose(const char *fmt, ...)
{
	va_list args;

	if (!opt_verbose)
		return;

	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	va_end(args);
}

static void verbose_print_argv(char **argv)
{
	if (!opt_verbose)
		return;

	fprintf(stderr, _("[%ld] exec"), (long)pid);
	for (; *argv; argv++)
		fprintf(stderr, " %s", *argv);
	fprintf(stderr, "\n");
}

static char **parse_args(int argc, char **argv)
{
	int opt;
	struct option long_opts[] = {
		{"debug", no_argument, 0, 'd'},
		{"help", no_argument, 0, 'h'},
		{"lsm", required_argument, 0, 'l'},
		{"verbose", no_argument, 0, 'v'},
	};

	while ((opt = getopt_long(argc, argv, "+dhl:v", long_opts, NULL)) != -1) {
		switch (opt) {
		case 'd':
			opt_debug = true;
			break;
		case 'h':
			usage(argv[0], false);
			break;
		case 'l':
			opt_lsm = optarg;
			break;
		case 'v':
			opt_verbose = true;
			break;
		default:
			usage(argv[0], true);
			break;
		}
	}

	if (optind >= argc)
		usage(argv[0], true);

	return argv + optind;
}

static void print_display_lsm(const char *msg)
{
	char buffer[PATH_MAX];
	int fd = -1;
	int size;

	if (!opt_verbose)
		return;

	fd = open("/proc/self/attr/display", O_RDONLY);
	if (fd == -1)
		error("could not open display lsm");
	size = read(fd, buffer, sizeof(buffer)-1);
	if (size == -1)
		error("could not read display lsm");
	buffer[size] = 0;
	verbose("%s: '%s'", msg, buffer);

	close(fd);
}

int main(int argc, char **argv)
{
	char name[PATH_MAX];
	int rc = 0;

	/* IMPORTANT: pid must be initialized before doing anything else since
	 * it is used in a global context when printing messages
	 */
	pid = getpid();

	argv = parse_args(argc, argv);

	print_display_lsm("display lsm before");
	if (opt_lsm) {
		int fd, size;
		fd = open("/proc/self/attr/display", O_CLOEXEC | O_WRONLY);
		if (fd == -1)
			error("could not open /proc/self/attr/display");
		size = write(fd, opt_lsm, strlen(opt_lsm));
		close(fd);
		if (size == -1)
			error("failed write to /proc/self/attr/display");
		else if (size != strlen(opt_lsm))
			error("failed write to /proc/self/attr/display, size %d  != %d", size, strlen(opt_lsm));
		print_display_lsm("display lsm after");
	}

	verbose_print_argv(argv);
	execvp(argv[0], argv);
	error("Failed to execute \"%s\": %m", argv[0]);
}

^ permalink raw reply

* Re: [PATCH v4 19/23] x86/ftrace: Use vmalloc special flag
From: Edgecombe, Rick P @ 2019-04-25 19:19 UTC (permalink / raw)
  To: rostedt@goodmis.org
  Cc: linux-kernel@vger.kernel.org, peterz@infradead.org,
	linux-integrity@vger.kernel.org, ard.biesheuvel@linaro.org,
	tglx@linutronix.de, linux-mm@kvack.org, nadav.amit@gmail.com,
	dave.hansen@linux.intel.com, Dock, Deneen T,
	linux-security-module@vger.kernel.org, x86@kernel.org,
	akpm@linux-foundation.org, hpa@zytor.com, kristen@linux.intel.com,
	mingo@redhat.com, linux_dti@icloud.com, luto@kernel.org,
	will.deacon@arm.com, bp@alien8.de,
	kernel-hardening@lists.openwall.com
In-Reply-To: <20190425142803.4f2e354a@gandalf.local.home>

On Thu, 2019-04-25 at 14:28 -0400, Steven Rostedt wrote:
> On Mon, 22 Apr 2019 11:58:01 -0700
> Rick Edgecombe <rick.p.edgecombe@intel.com> wrote:
> 
> > Use new flag VM_FLUSH_RESET_PERMS for handling freeing of special
> > permissioned memory in vmalloc and remove places where memory was set NX
> > and RW before freeing which is no longer needed.
> > 
> > Cc: Steven Rostedt <rostedt@goodmis.org>
> > Acked-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> > Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> 
> Tested-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> Acked-by: Steven Rostedt (VMware) <rostedt@godomis.org>
> 
> -- Steve

Thanks!

Rick

^ permalink raw reply

* Re: [PATCH v4 19/23] x86/ftrace: Use vmalloc special flag
From: Steven Rostedt @ 2019-04-25 18:28 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: Borislav Petkov, Andy Lutomirski, Ingo Molnar, linux-kernel, x86,
	hpa, Thomas Gleixner, Nadav Amit, Dave Hansen, Peter Zijlstra,
	linux_dti, linux-integrity, linux-security-module, akpm,
	kernel-hardening, linux-mm, will.deacon, ard.biesheuvel, kristen,
	deneen.t.dock
In-Reply-To: <20190422185805.1169-20-rick.p.edgecombe@intel.com>

On Mon, 22 Apr 2019 11:58:01 -0700
Rick Edgecombe <rick.p.edgecombe@intel.com> wrote:

> Use new flag VM_FLUSH_RESET_PERMS for handling freeing of special
> permissioned memory in vmalloc and remove places where memory was set NX
> and RW before freeing which is no longer needed.
> 
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Acked-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>

Tested-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Acked-by: Steven Rostedt (VMware) <rostedt@godomis.org>

-- Steve

> ---
>  arch/x86/kernel/ftrace.c | 14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
> index 53ba1aa3a01f..0caf8122d680 100644
> --- a/arch/x86/kernel/ftrace.c
> +++ b/arch/x86/kernel/ftrace.c
> @@ -678,12 +678,8 @@ static inline void *alloc_tramp(unsigned long size)
>  {
>  	return module_alloc(size);
>  }
> -static inline void tramp_free(void *tramp, int size)
> +static inline void tramp_free(void *tramp)
>  {
> -	int npages = PAGE_ALIGN(size) >> PAGE_SHIFT;
> -
> -	set_memory_nx((unsigned long)tramp, npages);
> -	set_memory_rw((unsigned long)tramp, npages);
>  	module_memfree(tramp);
>  }
>  #else
> @@ -692,7 +688,7 @@ static inline void *alloc_tramp(unsigned long size)
>  {
>  	return NULL;
>  }
> -static inline void tramp_free(void *tramp, int size) { }
> +static inline void tramp_free(void *tramp) { }
>  #endif
>  
>  /* Defined as markers to the end of the ftrace default trampolines */
> @@ -808,6 +804,8 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
>  	/* ALLOC_TRAMP flags lets us know we created it */
>  	ops->flags |= FTRACE_OPS_FL_ALLOC_TRAMP;
>  
> +	set_vm_flush_reset_perms(trampoline);
> +
>  	/*
>  	 * Module allocation needs to be completed by making the page
>  	 * executable. The page is still writable, which is a security hazard,
> @@ -816,7 +814,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
>  	set_memory_x((unsigned long)trampoline, npages);
>  	return (unsigned long)trampoline;
>  fail:
> -	tramp_free(trampoline, *tramp_size);
> +	tramp_free(trampoline);
>  	return 0;
>  }
>  
> @@ -947,7 +945,7 @@ void arch_ftrace_trampoline_free(struct ftrace_ops *ops)
>  	if (!ops || !(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
>  		return;
>  
> -	tramp_free((void *)ops->trampoline, ops->trampoline_size);
> +	tramp_free((void *)ops->trampoline);
>  	ops->trampoline = 0;
>  }
>  


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox