All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it
@ 2018-04-06 14:22 Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 1/6] gcc-plugins: Clean up the cgraph_create_edge* macros Alexander Popov
                   ` (7 more replies)
  0 siblings, 8 replies; 70+ messages in thread
From: Alexander Popov @ 2018-04-06 14:22 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel,
	alex.popov

This is the 11th version of the patch series introducing STACKLEAK to the
mainline kernel. The 9th version raised a fervent discussion[0].
The assembly code introduced by that version irritated the reviewers.

I've found the way to bypass the obstacles[1] of the C implementation.
So I dare come again. Let me ask you to look at this code without
preconception.

Motivation
==========

STACKLEAK (initially developed by PaX Team):

 1. reduces the information that can be revealed through kernel stack leak bugs.
    The idea of erasing the thread stack at the end of syscalls is similar to
    CONFIG_PAGE_POISONING and memzero_explicit() in kernel crypto, which all
    comply with FDP_RIP.2 (Full Residual Information Protection) of the
    Common Criteria standard.

 2. blocks some uninitialized stack variable attacks (e.g. CVE-2017-17712,
    CVE-2010-2963). That kind of bugs should be killed by improving C compilers
    in future, which might take a long time.

 3. blocks stack depth overflow caused by alloca (aka Stack Clash attack).
    That is orthogonal to the mainline kernel VLA cleanup and protects
    un-upstreamed code.

Performance impact
==================

Hardware: Intel Core i7-4770, 16 GB RAM

Test #1: building the Linux kernel on a single core
	0.91% slowdown

Test #2: hackbench -s 4096 -l 2000 -g 15 -f 25 -P
	4.2% slowdown

So the STACKLEAK description in Kconfig includes:
"The tradeoff is the performance impact: on a single CPU system kernel
compilation sees a 1% slowdown, other systems and workloads may vary and you are
advised to test this feature on your expected workload before deploying it".

Links
=====

[0] http://www.openwall.com/lists/kernel-hardening/2018/03/03/7
[1] http://www.openwall.com/lists/kernel-hardening/2018/03/21/4


Alexander Popov (6):
  gcc-plugins: Clean up the cgraph_create_edge* macros
  x86/entry: Add STACKLEAK erasing the kernel stack at the end of
    syscalls
  gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack
  lkdtm: Add a test for STACKLEAK
  fs/proc: Show STACKLEAK metrics in the /proc file system
  doc: self-protection: Add information about STACKLEAK feature

 Documentation/security/self-protection.rst |  23 +-
 Documentation/x86/x86_64/mm.txt            |   2 +
 arch/Kconfig                               |  53 ++++
 arch/x86/Kconfig                           |   1 +
 arch/x86/entry/Makefile                    |   3 +
 arch/x86/entry/calling.h                   |  14 +
 arch/x86/entry/entry_32.S                  |   7 +
 arch/x86/entry/entry_64.S                  |   3 +
 arch/x86/entry/entry_64_compat.S           |   5 +
 arch/x86/entry/erase.c                     |  58 ++++
 arch/x86/include/asm/processor.h           |   7 +
 arch/x86/kernel/dumpstack.c                |  19 ++
 arch/x86/kernel/process_32.c               |   8 +
 arch/x86/kernel/process_64.c               |   8 +
 drivers/misc/Makefile                      |   3 +
 drivers/misc/lkdtm.h                       |   4 +
 drivers/misc/lkdtm_core.c                  |   2 +
 drivers/misc/lkdtm_stackleak.c             | 141 +++++++++
 fs/proc/base.c                             |  18 ++
 include/linux/compiler.h                   |   4 +
 mm/util.c                                  |  33 ++
 scripts/Makefile.gcc-plugins               |   3 +
 scripts/gcc-plugins/gcc-common.h           |  26 +-
 scripts/gcc-plugins/stackleak_plugin.c     | 470 +++++++++++++++++++++++++++++
 24 files changed, 896 insertions(+), 19 deletions(-)
 create mode 100644 arch/x86/entry/erase.c
 create mode 100644 drivers/misc/lkdtm_stackleak.c
 create mode 100644 scripts/gcc-plugins/stackleak_plugin.c

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v11 1/6] gcc-plugins: Clean up the cgraph_create_edge* macros
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
@ 2018-04-06 14:22 ` Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls Alexander Popov
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-04-06 14:22 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel,
	alex.popov

Drop useless redefinitions of cgraph_create_edge* macros. Drop the unused
nest argument. Also support gcc-8, which doesn't have freq argument.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
---
 scripts/gcc-plugins/gcc-common.h | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index f467500..552d5ef 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -392,13 +392,6 @@ static inline struct cgraph_node *cgraph_alias_target(struct cgraph_node *n)
 }
 #endif
 
-#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION <= 4009
-#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \
-	cgraph_create_edge((caller), (callee), (call_stmt), (count), (freq))
-#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \
-	cgraph_create_edge_including_clones((caller), (callee), (old_call_stmt), (call_stmt), (count), (freq), (reason))
-#endif
-
 #if BUILDING_GCC_VERSION <= 4008
 #define ENTRY_BLOCK_PTR_FOR_FN(FN)	ENTRY_BLOCK_PTR_FOR_FUNCTION(FN)
 #define EXIT_BLOCK_PTR_FOR_FN(FN)	EXIT_BLOCK_PTR_FOR_FUNCTION(FN)
@@ -723,10 +716,23 @@ static inline const char *get_decl_section_name(const_tree decl)
 #define varpool_get_node(decl) varpool_node::get(decl)
 #define dump_varpool_node(file, node) (node)->dump(file)
 
-#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \
+#if BUILDING_GCC_VERSION >= 8000
+#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \
+	(caller)->create_edge((callee), (call_stmt), (count))
+
+#define cgraph_create_edge_including_clones(caller, callee,	\
+		old_call_stmt, call_stmt, count, freq, reason)	\
+	(caller)->create_edge_including_clones((callee),	\
+		(old_call_stmt), (call_stmt), (count), (reason))
+#else
+#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \
 	(caller)->create_edge((callee), (call_stmt), (count), (freq))
-#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \
-	(caller)->create_edge_including_clones((callee), (old_call_stmt), (call_stmt), (count), (freq), (reason))
+
+#define cgraph_create_edge_including_clones(caller, callee,	\
+		old_call_stmt, call_stmt, count, freq, reason)	\
+	(caller)->create_edge_including_clones((callee),	\
+		(old_call_stmt), (call_stmt), (count), (freq), (reason))
+#endif
 
 typedef struct cgraph_node *cgraph_node_ptr;
 typedef struct cgraph_edge *cgraph_edge_p;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 1/6] gcc-plugins: Clean up the cgraph_create_edge* macros Alexander Popov
@ 2018-04-06 14:22 ` Alexander Popov
  2018-04-16 18:29   ` Kees Cook
  2018-04-24  4:23   ` Dave Hansen
  2018-04-06 14:22 ` [PATCH v11 3/6] gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack Alexander Popov
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 70+ messages in thread
From: Alexander Popov @ 2018-04-06 14:22 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel,
	alex.popov

The STACKLEAK feature erases the kernel stack before returning from
syscalls. That reduces the information which kernel stack leak bugs can
reveal and blocks some uninitialized stack variable attacks. Moreover,
STACKLEAK provides runtime checks for kernel stack overflow detection.

This commit introduces the architecture-specific code filling the used
part of the kernel stack with a poison value before returning to the
userspace. Full STACKLEAK feature also contains the gcc plugin which
comes in a separate commit.

The STACKLEAK feature is ported from grsecurity/PaX. More information at:
  https://grsecurity.net/
  https://pax.grsecurity.net/

This code is modified from Brad Spengler/PaX Team's code in the last
public patch of grsecurity/PaX based on our understanding of the code.
Changes or omissions from the original code are ours and don't reflect
the original grsecurity/PaX code.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
---
 Documentation/x86/x86_64/mm.txt  |  2 ++
 arch/Kconfig                     | 27 ++++++++++++++++++++
 arch/x86/Kconfig                 |  1 +
 arch/x86/entry/Makefile          |  3 +++
 arch/x86/entry/calling.h         | 14 +++++++++++
 arch/x86/entry/entry_32.S        |  7 ++++++
 arch/x86/entry/entry_64.S        |  3 +++
 arch/x86/entry/entry_64_compat.S |  5 ++++
 arch/x86/entry/erase.c           | 54 ++++++++++++++++++++++++++++++++++++++++
 arch/x86/include/asm/processor.h |  4 +++
 arch/x86/kernel/process_32.c     |  5 ++++
 arch/x86/kernel/process_64.c     |  5 ++++
 include/linux/compiler.h         |  4 +++
 13 files changed, 134 insertions(+)
 create mode 100644 arch/x86/entry/erase.c

diff --git a/Documentation/x86/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt
index ea91cb6..21ee7c5 100644
--- a/Documentation/x86/x86_64/mm.txt
+++ b/Documentation/x86/x86_64/mm.txt
@@ -24,6 +24,7 @@ ffffffffa0000000 - [fixmap start]   (~1526 MB) module mapping space (variable)
 [fixmap start]   - ffffffffff5fffff kernel-internal fixmap range
 ffffffffff600000 - ffffffffff600fff (=4 kB) legacy vsyscall ABI
 ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole
+STACKLEAK_POISON value in this last hole: ffffffffffff4111
 
 Virtual memory map with 5 level page tables:
 
@@ -50,6 +51,7 @@ ffffffffa0000000 - fffffffffeffffff (1520 MB) module mapping space
 [fixmap start]   - ffffffffff5fffff kernel-internal fixmap range
 ffffffffff600000 - ffffffffff600fff (=4 kB) legacy vsyscall ABI
 ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole
+STACKLEAK_POISON value in this last hole: ffffffffffff4111
 
 Architecture defines a 64-bit virtual address. Implementations can support
 less. Currently supported are 48- and 57-bit virtual addresses. Bits 63
diff --git a/arch/Kconfig b/arch/Kconfig
index 76c0b54..368e2fb 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -401,6 +401,13 @@ config SECCOMP_FILTER
 
 	  See Documentation/prctl/seccomp_filter.txt for details.
 
+config HAVE_ARCH_STACKLEAK
+	bool
+	help
+	  An architecture should select this if it has the code which
+	  fills the used part of the kernel stack with the STACKLEAK_POISON
+	  value before returning from system calls.
+
 config HAVE_GCC_PLUGINS
 	bool
 	help
@@ -531,6 +538,26 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE
 	  in structures.  This reduces the performance hit of RANDSTRUCT
 	  at the cost of weakened randomization.
 
+config GCC_PLUGIN_STACKLEAK
+	bool "Erase the kernel stack before returning from syscalls"
+	depends on GCC_PLUGINS
+	depends on HAVE_ARCH_STACKLEAK
+	help
+	  This option makes the kernel erase the kernel stack before it
+	  returns from a system call. That reduces the information which
+	  kernel stack leak bugs can reveal and blocks some uninitialized
+	  stack variable attacks. This option also provides runtime checks
+	  for kernel stack overflow detection.
+
+	  The tradeoff is the performance impact: on a single CPU system kernel
+	  compilation sees a 1% slowdown, other systems and workloads may vary
+	  and you are advised to test this feature on your expected workload
+	  before deploying it.
+
+	  This plugin was ported from grsecurity/PaX. More information at:
+	   * https://grsecurity.net/
+	   * https://pax.grsecurity.net/
+
 config HAVE_CC_STACKPROTECTOR
 	bool
 	help
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0fa71a7..e700879 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -119,6 +119,7 @@ config X86
 	select HAVE_ARCH_COMPAT_MMAP_BASES	if MMU && COMPAT
 	select HAVE_ARCH_SECCOMP_FILTER
 	select HAVE_ARCH_THREAD_STRUCT_WHITELIST
+	select HAVE_ARCH_STACKLEAK
 	select HAVE_ARCH_TRACEHOOK
 	select HAVE_ARCH_TRANSPARENT_HUGEPAGE
 	select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64
diff --git a/arch/x86/entry/Makefile b/arch/x86/entry/Makefile
index 06fc70c..abe4d92 100644
--- a/arch/x86/entry/Makefile
+++ b/arch/x86/entry/Makefile
@@ -15,3 +15,6 @@ obj-y				+= vsyscall/
 
 obj-$(CONFIG_IA32_EMULATION)	+= entry_64_compat.o syscall_32.o
 
+obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
+KASAN_SANITIZE_erase.o		:= n
+
diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h
index be63330..a555712 100644
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -327,8 +327,22 @@ For 32-bit we have the following conventions - kernel is built with
 
 #endif
 
+.macro ERASE_KSTACK_NOCLOBBER
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	PUSH_AND_CLEAR_REGS
+	call erase_kstack
+	POP_REGS
+#endif
+.endm
+
 #endif /* CONFIG_X86_64 */
 
+.macro ERASE_KSTACK
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	call erase_kstack
+#endif
+.endm
+
 /*
  * This does 'call enter_from_user_mode' unless we can avoid it based on
  * kernel config or using the static jump infrastructure.
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index 6ad064c..733088e 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -46,6 +46,8 @@
 #include <asm/frame.h>
 #include <asm/nospec-branch.h>
 
+#include "calling.h"
+
 	.section .entry.text, "ax"
 
 /*
@@ -298,6 +300,7 @@ ENTRY(ret_from_fork)
 	/* When we fork, we trace the syscall return in the child, too. */
 	movl    %esp, %eax
 	call    syscall_return_slowpath
+	ERASE_KSTACK
 	jmp     restore_all
 
 	/* kernel thread */
@@ -458,6 +461,8 @@ ENTRY(entry_SYSENTER_32)
 	ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \
 		    "jmp .Lsyscall_32_done", X86_FEATURE_XENPV
 
+	ERASE_KSTACK
+
 /* Opportunistic SYSEXIT */
 	TRACE_IRQS_ON			/* User mode traces as IRQs on. */
 	movl	PT_EIP(%esp), %edx	/* pt_regs->ip */
@@ -544,6 +549,8 @@ ENTRY(entry_INT80_32)
 	call	do_int80_syscall_32
 .Lsyscall_32_done:
 
+	ERASE_KSTACK
+
 restore_all:
 	TRACE_IRQS_IRET
 .Lrestore_all_notrace:
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 18ed349..e267899 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -323,6 +323,8 @@ syscall_return_via_sysret:
 	 * We are on the trampoline stack.  All regs except RDI are live.
 	 * We can do future final exit work right here.
 	 */
+	ERASE_KSTACK_NOCLOBBER
+
 	SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
 
 	popq	%rdi
@@ -681,6 +683,7 @@ GLOBAL(swapgs_restore_regs_and_return_to_usermode)
 	 * We are on the trampoline stack.  All regs except RDI are live.
 	 * We can do future final exit work right here.
 	 */
+	ERASE_KSTACK_NOCLOBBER
 
 	SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
 
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index 08425c4..03d03d4 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -258,6 +258,11 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe)
 
 	/* Opportunistic SYSRET */
 sysret32_from_system_call:
+	/*
+	 * We are not going to return to the userspace from the trampoline
+	 * stack. So let's erase the thread stack right now.
+	 */
+	ERASE_KSTACK
 	TRACE_IRQS_ON			/* User mode traces as IRQs on. */
 	movq	RBX(%rsp), %rbx		/* pt_regs->rbx */
 	movq	RBP(%rsp), %rbp		/* pt_regs->rbp */
diff --git a/arch/x86/entry/erase.c b/arch/x86/entry/erase.c
new file mode 100644
index 0000000..4892335
--- /dev/null
+++ b/arch/x86/entry/erase.c
@@ -0,0 +1,54 @@
+#include <linux/bug.h>
+#include <linux/sched.h>
+#include <asm/current.h>
+#include <asm/linkage.h>
+#include <asm/processor.h>
+
+asmlinkage void erase_kstack(void)
+{
+	register unsigned long p = current->thread.lowest_stack;
+	register unsigned long boundary = p & ~(THREAD_SIZE - 1);
+	unsigned long poison = 0;
+	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
+							sizeof(unsigned long);
+
+	/*
+	 * Let's search for the poison value in the stack.
+	 * Start from the lowest_stack and go to the bottom.
+	 */
+	while (p > boundary && poison <= check_depth) {
+		if (*(unsigned long *)p == STACKLEAK_POISON)
+			poison++;
+		else
+			poison = 0;
+
+		p -= sizeof(unsigned long);
+	}
+
+	/*
+	 * One long int at the bottom of the thread stack is reserved and
+	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
+	 */
+	if (p == boundary)
+		p += sizeof(unsigned long);
+
+	/*
+	 * So let's write the poison value to the kernel stack.
+	 * Start from the address in p and move up till the new boundary.
+	 */
+	if (on_thread_stack())
+		boundary = current_stack_pointer;
+	else
+		boundary = current_top_of_stack();
+
+	BUG_ON(boundary - p >= THREAD_SIZE);
+
+	while (p < boundary) {
+		*(unsigned long *)p = STACKLEAK_POISON;
+		p += sizeof(unsigned long);
+	}
+
+	/* Reset the lowest_stack value for the next syscall */
+	current->thread.lowest_stack = current_top_of_stack() - 256;
+}
+
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index b0ccd48..0c87813 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -494,6 +494,10 @@ struct thread_struct {
 
 	mm_segment_t		addr_limit;
 
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	unsigned long		lowest_stack;
+#endif
+
 	unsigned int		sig_on_uaccess_err:1;
 	unsigned int		uaccess_err:1;	/* uaccess failed */
 
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 5224c60..1b0892e 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -136,6 +136,11 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
 	p->thread.sp0 = (unsigned long) (childregs+1);
 	memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
 
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	p->thread.lowest_stack = (unsigned long)task_stack_page(p) +
+						sizeof(unsigned long);
+#endif
+
 	if (unlikely(p->flags & PF_KTHREAD)) {
 		/* kernel thread */
 		memset(childregs, 0, sizeof(struct pt_regs));
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 9eb448c..82122af 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -281,6 +281,11 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
 	p->thread.sp = (unsigned long) fork_frame;
 	p->thread.io_bitmap_ptr = NULL;
 
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	p->thread.lowest_stack = (unsigned long)task_stack_page(p) +
+						sizeof(unsigned long);
+#endif
+
 	savesegment(gs, p->thread.gsindex);
 	p->thread.gsbase = p->thread.gsindex ? 0 : me->thread.gsbase;
 	savesegment(fs, p->thread.fsindex);
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index ab4711c..341b6cf8 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -342,4 +342,8 @@ unsigned long read_word_at_a_time(const void *addr)
 	compiletime_assert(__native_word(t),				\
 		"Need native word sized stores/loads for atomicity.")
 
+/* Poison value points to the unused hole in the virtual memory map */
+#define STACKLEAK_POISON -0xBEEF
+#define STACKLEAK_POISON_CHECK_DEPTH 128
+
 #endif /* __LINUX_COMPILER_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v11 3/6] gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 1/6] gcc-plugins: Clean up the cgraph_create_edge* macros Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls Alexander Popov
@ 2018-04-06 14:22 ` Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 4/6] lkdtm: Add a test for STACKLEAK Alexander Popov
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-04-06 14:22 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel,
	alex.popov

The STACKLEAK feature erases the kernel stack before returning from
syscalls. That reduces the information which kernel stack leak bugs can
reveal and blocks some uninitialized stack variable attacks. Moreover,
STACKLEAK provides runtime checks for kernel stack overflow detection.

This commit introduces the STACKLEAK gcc plugin. It is needed for:
 - tracking the lowest border of the kernel stack, which is important
    for the code erasing the used part of the kernel stack at the end
    of syscalls (comes in a separate commit);
 - checking that alloca calls don't cause stack overflow.

So this plugin instruments the kernel code inserting:
 - the check_alloca() call before alloca and the track_stack() call
    after it;
 - the track_stack() call for the functions with a stack frame size
    greater than or equal to CONFIG_STACKLEAK_TRACK_MIN_SIZE.

The STACKLEAK feature is ported from grsecurity/PaX. More information at:
  https://grsecurity.net/
  https://pax.grsecurity.net/

This code is modified from Brad Spengler/PaX Team's code in the last
public patch of grsecurity/PaX based on our understanding of the code.
Changes or omissions from the original code are ours and don't reflect
the original grsecurity/PaX code.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
---
 arch/Kconfig                           |  14 +
 arch/x86/kernel/dumpstack.c            |  19 ++
 mm/util.c                              |  33 +++
 scripts/Makefile.gcc-plugins           |   3 +
 scripts/gcc-plugins/stackleak_plugin.c | 470 +++++++++++++++++++++++++++++++++
 5 files changed, 539 insertions(+)
 create mode 100644 scripts/gcc-plugins/stackleak_plugin.c

diff --git a/arch/Kconfig b/arch/Kconfig
index 368e2fb..a4a8fba 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -542,6 +542,8 @@ config GCC_PLUGIN_STACKLEAK
 	bool "Erase the kernel stack before returning from syscalls"
 	depends on GCC_PLUGINS
 	depends on HAVE_ARCH_STACKLEAK
+	imply VMAP_STACK
+	imply SCHED_STACK_END_CHECK
 	help
 	  This option makes the kernel erase the kernel stack before it
 	  returns from a system call. That reduces the information which
@@ -558,6 +560,18 @@ config GCC_PLUGIN_STACKLEAK
 	   * https://grsecurity.net/
 	   * https://pax.grsecurity.net/
 
+config STACKLEAK_TRACK_MIN_SIZE
+	int "Minimum stack frame size of functions tracked by STACKLEAK"
+	default 100
+	range 0 4096
+	depends on GCC_PLUGIN_STACKLEAK
+	help
+	  The STACKLEAK gcc plugin instruments the kernel code for tracking
+	  the lowest border of the kernel stack (and for some other purposes).
+	  It inserts the track_stack() call for the functions with a stack
+	  frame size greater than or equal to this parameter.
+	  If unsure, leave the default value 100.
+
 config HAVE_CC_STACKPROTECTOR
 	bool
 	help
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index a2d8a39..97ed2a8 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -375,3 +375,22 @@ static int __init code_bytes_setup(char *s)
 	return 1;
 }
 __setup("code_bytes=", code_bytes_setup);
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+#define MIN_STACK_LEFT 256
+
+void __used check_alloca(unsigned long size)
+{
+	unsigned long sp = (unsigned long)&sp;
+	struct stack_info stack_info = {0};
+	unsigned long visit_mask = 0;
+	unsigned long stack_left;
+
+	BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask));
+
+	stack_left = sp - (unsigned long)stack_info.begin;
+	BUG_ON(stack_left < MIN_STACK_LEFT ||
+				size >= stack_left - MIN_STACK_LEFT);
+}
+EXPORT_SYMBOL(check_alloca);
+#endif
diff --git a/mm/util.c b/mm/util.c
index c125050..f846015 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -757,3 +757,36 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen)
 out:
 	return res;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used track_stack(void)
+{
+	/*
+	 * N.B. The arch-specific part of the STACKLEAK feature fills the
+	 * kernel stack with the poison value, which has the register width.
+	 * That code assumes that the value of thread.lowest_stack is aligned
+	 * on the register width boundary.
+	 *
+	 * That is true for x86 and x86_64 because of the kernel stack
+	 * alignment on these platforms (for details, see cc_stack_align in
+	 * arch/x86/Makefile). Take care of that when you port STACKLEAK to
+	 * new platforms.
+	 */
+	unsigned long sp = (unsigned long)&sp;
+
+	/*
+	 * Having CONFIG_STACKLEAK_TRACK_MIN_SIZE larger than
+	 * STACKLEAK_POISON_CHECK_DEPTH makes the poison search in
+	 * erase_kstack() unreliable. Let's prevent that.
+	 */
+	BUILD_BUG_ON(CONFIG_STACKLEAK_TRACK_MIN_SIZE >
+						STACKLEAK_POISON_CHECK_DEPTH);
+
+	if (sp < current->thread.lowest_stack &&
+	    sp >= (unsigned long)task_stack_page(current) +
+					sizeof(unsigned long)) {
+		current->thread.lowest_stack = sp;
+	}
+}
+EXPORT_SYMBOL(track_stack);
+#endif /* CONFIG_GCC_PLUGIN_STACKLEAK */
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index b2a95af..8d6070f 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -35,6 +35,9 @@ ifdef CONFIG_GCC_PLUGINS
   gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT)	+= -DRANDSTRUCT_PLUGIN
   gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE)	+= -fplugin-arg-randomize_layout_plugin-performance-mode
 
+  gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)	+= stackleak_plugin.so
+  gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)	+= -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
+
   GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
 
   export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c
new file mode 100644
index 0000000..6ac2a05
--- /dev/null
+++ b/scripts/gcc-plugins/stackleak_plugin.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2011-2017 by the PaX Team <pageexec@freemail.hu>
+ * Modified by Alexander Popov <alex.popov@linux.com>
+ * Licensed under the GPL v2
+ *
+ * Note: the choice of the license means that the compilation process is
+ * NOT 'eligible' as defined by gcc's library exception to the GPL v3,
+ * but for the kernel it doesn't matter since it doesn't link against
+ * any of the gcc libraries
+ *
+ * This gcc plugin is needed for tracking the lowest border of the kernel stack
+ * and checking that alloca calls don't cause stack overflow. It instruments
+ * the kernel code inserting:
+ *  - the check_alloca() call before alloca and the track_stack() call after it;
+ *  - the track_stack() call for the functions with a stack frame size greater
+ *     than or equal to the "track-min-size" plugin parameter.
+ *
+ * This plugin is ported from grsecurity/PaX. For more information see:
+ *   https://grsecurity.net/
+ *   https://pax.grsecurity.net/
+ *
+ * Debugging:
+ *  - use fprintf() to stderr, debug_generic_expr(), debug_gimple_stmt(),
+ *     print_rtl() and print_simple_rtl();
+ *  - add "-fdump-tree-all -fdump-rtl-all" to the plugin CFLAGS in
+ *     Makefile.gcc-plugins to see the verbose dumps of the gcc passes;
+ *  - use gcc -E to understand the preprocessing shenanigans;
+ *  - use gcc with enabled CFG/GIMPLE/SSA verification (--enable-checking).
+ */
+
+#include "gcc-common.h"
+
+__visible int plugin_is_GPL_compatible;
+
+static int track_frame_size = -1;
+static const char track_function[] = "track_stack";
+static const char check_function[] = "check_alloca";
+
+/*
+ * Mark these global variables (roots) for gcc garbage collector since
+ * they point to the garbage-collected memory.
+ */
+static GTY(()) tree track_function_decl;
+static GTY(()) tree check_function_decl;
+
+static struct plugin_info stackleak_plugin_info = {
+	.version = "201707101337",
+	.help = "track-min-size=nn\ttrack stack for functions with a stack frame size >= nn bytes\n"
+		"disable\t\tdo not activate the plugin\n"
+};
+
+static void stackleak_add_check_alloca(gimple_stmt_iterator *gsi)
+{
+	gimple stmt;
+	gcall *check_alloca;
+	tree alloca_size;
+	cgraph_node_ptr node;
+	int frequency;
+	basic_block bb;
+
+	/* Insert call to void check_alloca(unsigned long size) */
+	alloca_size = gimple_call_arg(gsi_stmt(*gsi), 0);
+	stmt = gimple_build_call(check_function_decl, 1, alloca_size);
+	check_alloca = as_a_gcall(stmt);
+	gsi_insert_before(gsi, check_alloca, GSI_SAME_STMT);
+
+	/* Update the cgraph */
+	bb = gimple_bb(check_alloca);
+	node = cgraph_get_create_node(check_function_decl);
+	gcc_assert(node);
+	frequency = compute_call_stmt_bb_frequency(current_function_decl, bb);
+	cgraph_create_edge(cgraph_get_node(current_function_decl), node,
+			check_alloca, bb->count, frequency);
+}
+
+static void stackleak_add_track_stack(gimple_stmt_iterator *gsi, bool after)
+{
+	gimple stmt;
+	gcall *track_stack;
+	cgraph_node_ptr node;
+	int frequency;
+	basic_block bb;
+
+	/* Insert call to void track_stack(void) */
+	stmt = gimple_build_call(track_function_decl, 0);
+	track_stack = as_a_gcall(stmt);
+	if (after)
+		gsi_insert_after(gsi, track_stack, GSI_CONTINUE_LINKING);
+	else
+		gsi_insert_before(gsi, track_stack, GSI_SAME_STMT);
+
+	/* Update the cgraph */
+	bb = gimple_bb(track_stack);
+	node = cgraph_get_create_node(track_function_decl);
+	gcc_assert(node);
+	frequency = compute_call_stmt_bb_frequency(current_function_decl, bb);
+	cgraph_create_edge(cgraph_get_node(current_function_decl), node,
+			track_stack, bb->count, frequency);
+}
+
+static bool is_alloca(gimple stmt)
+{
+	if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA))
+		return true;
+
+#if BUILDING_GCC_VERSION >= 4007
+	if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+		return true;
+#endif
+
+	return false;
+}
+
+/*
+ * Work with the GIMPLE representation of the code.
+ * Insert the check_alloca() call before alloca and track_stack() call after
+ * it. Also insert track_stack() call into the beginning of the function
+ * if it is not instrumented.
+ */
+static unsigned int stackleak_instrument_execute(void)
+{
+	basic_block bb, entry_bb;
+	bool prologue_instrumented = false, is_leaf = true;
+	gimple_stmt_iterator gsi;
+
+	/*
+	 * ENTRY_BLOCK_PTR is a basic block which represents possible entry
+	 * point of a function. This block does not contain any code and
+	 * has a CFG edge to its successor.
+	 */
+	gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+	entry_bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
+
+	/*
+	 * 1. Loop through the GIMPLE statements in each of cfun basic blocks.
+	 * cfun is a global variable which represents the function that is
+	 * currently processed.
+	 */
+	FOR_EACH_BB_FN(bb, cfun) {
+		for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+			gimple stmt;
+
+			stmt = gsi_stmt(gsi);
+
+			/* Leaf function is a function which makes no calls */
+			if (is_gimple_call(stmt))
+				is_leaf = false;
+
+			if (!is_alloca(stmt))
+				continue;
+
+			/* 2. Insert stack overflow check before alloca call */
+			stackleak_add_check_alloca(&gsi);
+
+			/* 3. Insert track_stack() call after alloca call */
+			stackleak_add_track_stack(&gsi, true);
+			if (bb == entry_bb)
+				prologue_instrumented = true;
+		}
+	}
+
+	if (prologue_instrumented)
+		return 0;
+
+	/*
+	 * Special cases to skip the instrumentation.
+	 *
+	 * Taking the address of static inline functions materializes them,
+	 * but we mustn't instrument some of them as the resulting stack
+	 * alignment required by the function call ABI will break other
+	 * assumptions regarding the expected (but not otherwise enforced)
+	 * register clobbering ABI.
+	 *
+	 * Case in point: native_save_fl on amd64 when optimized for size
+	 * clobbers rdx if it were instrumented here.
+	 *
+	 * TODO: any more special cases?
+	 */
+	if (is_leaf &&
+	    !TREE_PUBLIC(current_function_decl) &&
+	    DECL_DECLARED_INLINE_P(current_function_decl)) {
+		return 0;
+	}
+
+	if (is_leaf &&
+	    !strncmp(IDENTIFIER_POINTER(DECL_NAME(current_function_decl)),
+		     "_paravirt_", 10)) {
+		return 0;
+	}
+
+	/* 4. Insert track_stack() call at the function beginning */
+	bb = entry_bb;
+	if (!single_pred_p(bb)) {
+		/* gcc_assert(bb_loop_depth(bb) ||
+				(bb->flags & BB_IRREDUCIBLE_LOOP)); */
+		split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+		gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
+		bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
+	}
+	gsi = gsi_after_labels(bb);
+	stackleak_add_track_stack(&gsi, false);
+
+	return 0;
+}
+
+static bool large_stack_frame(void)
+{
+#if BUILDING_GCC_VERSION >= 8000
+	return maybe_ge(get_frame_size(), track_frame_size);
+#else
+	return (get_frame_size() >= track_frame_size);
+#endif
+}
+
+/*
+ * Work with the RTL representation of the code.
+ * Remove the unneeded track_stack() calls from the functions which don't
+ * call alloca and don't have a large enough stack frame size.
+ */
+static unsigned int stackleak_cleanup_execute(void)
+{
+	rtx_insn *insn, *next;
+
+	if (cfun->calls_alloca)
+		return 0;
+
+	if (large_stack_frame())
+		return 0;
+
+	/*
+	 * 1. Find track_stack() calls. Loop through the chain of insns,
+	 * which is an RTL representation of the code for a function.
+	 *
+	 * The example of a matching insn:
+	 *    (call_insn 8 4 10 2 (call (mem (symbol_ref ("track_stack")
+	 *    [flags 0x41] <function_decl 0x7f7cd3302a80 track_stack>)
+	 *    [0 track_stack S1 A8]) (0)) 675 {*call} (expr_list
+	 *    (symbol_ref ("track_stack") [flags 0x41] <function_decl
+	 *    0x7f7cd3302a80 track_stack>) (expr_list (0) (nil))) (nil))
+	 */
+	for (insn = get_insns(); insn; insn = next) {
+		rtx body;
+
+		next = NEXT_INSN(insn);
+
+		/* Check the expression code of the insn */
+		if (!CALL_P(insn))
+			continue;
+
+		/*
+		 * Check the expression code of the insn body, which is an RTL
+		 * Expression (RTX) describing the side effect performed by
+		 * that insn.
+		 */
+		body = PATTERN(insn);
+		if (GET_CODE(body) != CALL)
+			continue;
+
+		/*
+		 * Check the first operand of the call expression. It should
+		 * be a mem RTX describing the needed subroutine with a
+		 * symbol_ref RTX.
+		 */
+		body = XEXP(body, 0);
+		if (GET_CODE(body) != MEM)
+			continue;
+
+		body = XEXP(body, 0);
+		if (GET_CODE(body) != SYMBOL_REF)
+			continue;
+
+		if (SYMBOL_REF_DECL(body) != track_function_decl)
+			continue;
+
+		/* 2. Delete the track_stack() call */
+		delete_insn_and_edges(insn);
+#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION < 8000
+		if (GET_CODE(next) == NOTE &&
+		    NOTE_KIND(next) == NOTE_INSN_CALL_ARG_LOCATION) {
+			insn = next;
+			next = NEXT_INSN(insn);
+			delete_insn_and_edges(insn);
+		}
+#endif
+	}
+
+	return 0;
+}
+
+static bool stackleak_gate(void)
+{
+	tree section;
+
+	section = lookup_attribute("section",
+				   DECL_ATTRIBUTES(current_function_decl));
+	if (section && TREE_VALUE(section)) {
+		section = TREE_VALUE(TREE_VALUE(section));
+
+		if (!strncmp(TREE_STRING_POINTER(section), ".init.text", 10))
+			return false;
+		if (!strncmp(TREE_STRING_POINTER(section), ".devinit.text", 13))
+			return false;
+		if (!strncmp(TREE_STRING_POINTER(section), ".cpuinit.text", 13))
+			return false;
+		if (!strncmp(TREE_STRING_POINTER(section), ".meminit.text", 13))
+			return false;
+	}
+
+	return track_frame_size >= 0;
+}
+
+/* Build function declarations for track_stack() and check_alloca() */
+static void stackleak_start_unit(void *gcc_data __unused,
+				 void *user_data __unused)
+{
+	tree fntype;
+
+	/* void track_stack(void) */
+	fntype = build_function_type_list(void_type_node, NULL_TREE);
+	track_function_decl = build_fn_decl(track_function, fntype);
+	DECL_ASSEMBLER_NAME(track_function_decl); /* for LTO */
+	TREE_PUBLIC(track_function_decl) = 1;
+	TREE_USED(track_function_decl) = 1;
+	DECL_EXTERNAL(track_function_decl) = 1;
+	DECL_ARTIFICIAL(track_function_decl) = 1;
+	DECL_PRESERVE_P(track_function_decl) = 1;
+
+	/* void check_alloca(unsigned long) */
+	fntype = build_function_type_list(void_type_node,
+				long_unsigned_type_node, NULL_TREE);
+	check_function_decl = build_fn_decl(check_function, fntype);
+	DECL_ASSEMBLER_NAME(check_function_decl); /* for LTO */
+	TREE_PUBLIC(check_function_decl) = 1;
+	TREE_USED(check_function_decl) = 1;
+	DECL_EXTERNAL(check_function_decl) = 1;
+	DECL_ARTIFICIAL(check_function_decl) = 1;
+	DECL_PRESERVE_P(check_function_decl) = 1;
+}
+
+/*
+ * Pass gate function is a predicate function that gets executed before the
+ * corresponding pass. If the return value is 'true' the pass gets executed,
+ * otherwise, it is skipped.
+ */
+static bool stackleak_instrument_gate(void)
+{
+	return stackleak_gate();
+}
+
+#define PASS_NAME stackleak_instrument
+#define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg
+#define TODO_FLAGS_START TODO_verify_ssa | TODO_verify_flow | TODO_verify_stmts
+#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \
+			| TODO_update_ssa | TODO_rebuild_cgraph_edges
+#include "gcc-generate-gimple-pass.h"
+
+static bool stackleak_cleanup_gate(void)
+{
+	return stackleak_gate();
+}
+
+#define PASS_NAME stackleak_cleanup
+#define TODO_FLAGS_FINISH TODO_dump_func
+#include "gcc-generate-rtl-pass.h"
+
+/*
+ * Every gcc plugin exports a plugin_init() function that is called right
+ * after the plugin is loaded. This function is responsible for registering
+ * the plugin callbacks and doing other required initialization.
+ */
+__visible int plugin_init(struct plugin_name_args *plugin_info,
+			  struct plugin_gcc_version *version)
+{
+	const char * const plugin_name = plugin_info->base_name;
+	const int argc = plugin_info->argc;
+	const struct plugin_argument * const argv = plugin_info->argv;
+	int i = 0;
+
+	/* Extra GGC root tables describing our GTY-ed data */
+	static const struct ggc_root_tab gt_ggc_r_gt_stackleak[] = {
+		{
+			.base = &track_function_decl,
+			.nelt = 1,
+			.stride = sizeof(track_function_decl),
+			.cb = &gt_ggc_mx_tree_node,
+			.pchw = &gt_pch_nx_tree_node
+		},
+		{
+			.base = &check_function_decl,
+			.nelt = 1,
+			.stride = sizeof(check_function_decl),
+			.cb = &gt_ggc_mx_tree_node,
+			.pchw = &gt_pch_nx_tree_node
+		},
+		LAST_GGC_ROOT_TAB
+	};
+
+	/*
+	 * The stackleak_instrument pass should be executed before the
+	 * "optimized" pass, which is the control flow graph cleanup that is
+	 * performed just before expanding gcc trees to the RTL. In former
+	 * versions of the plugin this new pass was inserted before the
+	 * "tree_profile" pass, which is currently called "profile".
+	 */
+	PASS_INFO(stackleak_instrument, "optimized", 1,
+						PASS_POS_INSERT_BEFORE);
+
+	/*
+	 * The stackleak_cleanup pass should be executed after the
+	 * "reload" pass, when the stack frame size is final.
+	 */
+	PASS_INFO(stackleak_cleanup, "reload", 1, PASS_POS_INSERT_AFTER);
+
+	if (!plugin_default_version_check(version, &gcc_version)) {
+		error(G_("incompatible gcc/plugin versions"));
+		return 1;
+	}
+
+	/* Parse the plugin arguments */
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argv[i].key, "disable"))
+			return 0;
+
+		if (!strcmp(argv[i].key, "track-min-size")) {
+			if (!argv[i].value) {
+				error(G_("no value supplied for option '-fplugin-arg-%s-%s'"),
+					plugin_name, argv[i].key);
+				return 1;
+			}
+
+			track_frame_size = atoi(argv[i].value);
+			if (track_frame_size < 0) {
+				error(G_("invalid option argument '-fplugin-arg-%s-%s=%s'"),
+					plugin_name, argv[i].key, argv[i].value);
+				return 1;
+			}
+		} else {
+			error(G_("unknown option '-fplugin-arg-%s-%s'"),
+					plugin_name, argv[i].key);
+			return 1;
+		}
+	}
+
+	/* Give the information about the plugin */
+	register_callback(plugin_name, PLUGIN_INFO, NULL,
+						&stackleak_plugin_info);
+
+	/* Register to be called before processing a translation unit */
+	register_callback(plugin_name, PLUGIN_START_UNIT,
+					&stackleak_start_unit, NULL);
+
+	/* Register an extra GCC garbage collector (GGC) root table */
+	register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL,
+					(void *)&gt_ggc_r_gt_stackleak);
+
+	/*
+	 * Hook into the Pass Manager to register new gcc passes.
+	 *
+	 * The stack frame size info is available only at the last RTL pass,
+	 * when it's too late to insert complex code like a function call.
+	 * So we register two gcc passes to instrument every function at first
+	 * and remove the unneeded instrumentation later.
+	 */
+	register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+					&stackleak_instrument_pass_info);
+	register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+					&stackleak_cleanup_pass_info);
+
+	return 0;
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v11 4/6] lkdtm: Add a test for STACKLEAK
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
                   ` (2 preceding siblings ...)
  2018-04-06 14:22 ` [PATCH v11 3/6] gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack Alexander Popov
@ 2018-04-06 14:22 ` Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 5/6] fs/proc: Show STACKLEAK metrics in the /proc file system Alexander Popov
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-04-06 14:22 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel,
	alex.popov

Introduce two lkdtm tests for the STACKLEAK feature: STACKLEAK_ALLOCA
and STACKLEAK_DEEP_RECURSION. Both of them check that the current task
stack is properly erased (filled with STACKLEAK_POISON).

STACKLEAK_ALLOCA tests that:
 - check_alloca() allows alloca calls which don't exhaust the kernel stack;
 - alloca calls which exhaust/overflow the kernel stack hit BUG() in
    check_alloca().

STACKLEAK_DEEP_RECURSION tests that exhausting the current task stack
with a deep recursion is detected by CONFIG_VMAP_STACK (which is implied
by CONFIG_GCC_PLUGIN_STACKLEAK).

Signed-off-by: Tycho Andersen <tycho@tycho.ws>
Signed-off-by: Alexander Popov <alex.popov@linux.com>
---
 drivers/misc/Makefile          |   3 +
 drivers/misc/lkdtm.h           |   4 ++
 drivers/misc/lkdtm_core.c      |   2 +
 drivers/misc/lkdtm_stackleak.c | 141 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 150 insertions(+)
 create mode 100644 drivers/misc/lkdtm_stackleak.c

diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c3c8624..2b11823 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -65,6 +65,9 @@ lkdtm-$(CONFIG_LKDTM)		+= lkdtm_perms.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_refcount.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_rodata_objcopy.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_usercopy.o
+lkdtm-$(CONFIG_LKDTM)		+= lkdtm_stackleak.o
+
+KASAN_SANITIZE_lkdtm_stackleak.o := n
 
 KCOV_INSTRUMENT_lkdtm_rodata.o	:= n
 
diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h
index 9e513dc..4b2b8e3 100644
--- a/drivers/misc/lkdtm.h
+++ b/drivers/misc/lkdtm.h
@@ -83,4 +83,8 @@ void lkdtm_USERCOPY_STACK_FRAME_FROM(void);
 void lkdtm_USERCOPY_STACK_BEYOND(void);
 void lkdtm_USERCOPY_KERNEL(void);
 
+/* lkdtm_stackleak.c */
+void lkdtm_STACKLEAK_ALLOCA(void);
+void lkdtm_STACKLEAK_DEEP_RECURSION(void);
+
 #endif
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
index 2154d1b..c37fd85 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm_core.c
@@ -183,6 +183,8 @@ static const struct crashtype crashtypes[] = {
 	CRASHTYPE(USERCOPY_STACK_FRAME_FROM),
 	CRASHTYPE(USERCOPY_STACK_BEYOND),
 	CRASHTYPE(USERCOPY_KERNEL),
+	CRASHTYPE(STACKLEAK_ALLOCA),
+	CRASHTYPE(STACKLEAK_DEEP_RECURSION),
 };
 
 
diff --git a/drivers/misc/lkdtm_stackleak.c b/drivers/misc/lkdtm_stackleak.c
new file mode 100644
index 0000000..4706595
--- /dev/null
+++ b/drivers/misc/lkdtm_stackleak.c
@@ -0,0 +1,141 @@
+/*
+ * This code tests several aspects of the STACKLEAK feature:
+ *  - the current task stack is properly erased (filled with STACKLEAK_POISON);
+ *  - check_alloca() allows alloca calls which don't exhaust the kernel stack;
+ *  - alloca calls which exhaust/overflow the kernel stack hit BUG() in
+ *     check_alloca();
+ *  - exhausting the current task stack with a deep recursion is detected by
+ *     CONFIG_VMAP_STACK (which is implied by CONFIG_GCC_PLUGIN_STACKLEAK).
+ *
+ * Authors:
+ *   Tycho Andersen <tycho@tycho.ws>
+ *   Alexander Popov <alex.popov@linux.com>
+ */
+
+#include "lkdtm.h"
+#include <linux/sched.h>
+#include <linux/compiler.h>
+
+#ifndef CONFIG_STACKLEAK_TRACK_MIN_SIZE
+# define CONFIG_STACKLEAK_TRACK_MIN_SIZE 0
+#endif
+
+static noinline bool stack_is_erased(void)
+{
+	unsigned long *sp, left, found, i;
+	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
+							sizeof(unsigned long);
+
+	/*
+	 * For the details about the alignment of the poison values, see
+	 * the comment in track_stack().
+	 */
+	sp = PTR_ALIGN(&i, sizeof(unsigned long));
+
+	left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long);
+	sp--;
+
+	/*
+	 * One long int at the bottom of the thread stack is reserved
+	 * and not poisoned.
+	 */
+	if (left > 1)
+		left--;
+	else
+		return false;
+
+	pr_info("checking unused part of the thread stack (%lu bytes)...\n",
+					left * sizeof(unsigned long));
+
+	/*
+	 * Search for check_depth poison values in a row (just like
+	 * erase_kstack() does).
+	 */
+	for (i = 0, found = 0; i < left && found <= check_depth; i++) {
+		if (*(sp - i) == STACKLEAK_POISON)
+			found++;
+		else
+			found = 0;
+	}
+
+	if (found <= check_depth) {
+		pr_err("FAIL: thread stack is not erased (checked %lu bytes)\n",
+						i * sizeof(unsigned long));
+		return false;
+	}
+
+	pr_info("first %lu bytes are unpoisoned\n",
+				(i - found) * sizeof(unsigned long));
+
+	/* The rest of thread stack should be erased */
+	for (; i < left; i++) {
+		if (*(sp - i) != STACKLEAK_POISON) {
+			pr_err("FAIL: thread stack is NOT properly erased\n");
+			return false;
+		}
+	}
+
+	pr_info("the rest of the thread stack is properly erased\n");
+	return true;
+}
+
+static noinline void do_alloca(unsigned long size)
+{
+	char buf[size];
+
+	/* So this doesn't get inlined or optimized out */
+	snprintf(buf, size, "testing alloca...\n");
+}
+
+void lkdtm_STACKLEAK_ALLOCA(void)
+{
+	unsigned long left = (unsigned long)&left & (THREAD_SIZE - 1);
+
+	if (!stack_is_erased())
+		return;
+
+	/* Try a small alloca to see if it works */
+	pr_info("try a small alloca of 16 bytes...\n");
+	do_alloca(16);
+	pr_info("small alloca is successful\n");
+
+	/* Try to hit the BUG() in check_alloca() */
+	pr_info("try a large alloca of %lu bytes (stack overflow)...\n", left);
+	do_alloca(left);
+	pr_err("FAIL: large alloca overstepped the thread stack boundary\n");
+}
+
+/*
+ * The stack frame size of recursion() is bigger than the
+ * CONFIG_STACKLEAK_TRACK_MIN_SIZE, hence that function is instrumented
+ * by the STACKLEAK gcc plugin and it calls track_stack() at the beginning.
+ */
+static noinline unsigned long recursion(unsigned long prev_sp)
+{
+	char buf[CONFIG_STACKLEAK_TRACK_MIN_SIZE + 42];
+	unsigned long sp = (unsigned long)&sp;
+
+	snprintf(buf, sizeof(buf), "testing deep recursion...\n");
+
+	if (prev_sp < sp + THREAD_SIZE)
+		sp = recursion(prev_sp);
+
+	return sp;
+}
+
+void lkdtm_STACKLEAK_DEEP_RECURSION(void)
+{
+	unsigned long sp = (unsigned long)&sp;
+
+	if (!stack_is_erased())
+		return;
+
+	/*
+	 * Exhaust the thread stack with a deep recursion. It should hit the
+	 * guard page provided by CONFIG_VMAP_STACK (which is implied by
+	 * CONFIG_GCC_PLUGIN_STACKLEAK).
+	 */
+	pr_info("try to exhaust the thread stack with a deep recursion...\n");
+	pr_err("FAIL: thread stack exhaustion (%lu bytes) is not detected\n",
+							sp - recursion(sp));
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v11 5/6] fs/proc: Show STACKLEAK metrics in the /proc file system
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
                   ` (3 preceding siblings ...)
  2018-04-06 14:22 ` [PATCH v11 4/6] lkdtm: Add a test for STACKLEAK Alexander Popov
@ 2018-04-06 14:22 ` Alexander Popov
  2018-04-06 14:22 ` [PATCH v11 6/6] doc: self-protection: Add information about STACKLEAK feature Alexander Popov
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-04-06 14:22 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel,
	alex.popov

Introduce CONFIG_STACKLEAK_METRICS providing STACKLEAK information about
tasks via the /proc file system. In particular, /proc/<pid>/stack_depth
shows the maximum kernel stack consumption for the current and previous
syscalls. Although this information is not precise, it  can be useful for
estimating the STACKLEAK performance impact for your workloads.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
---
 arch/Kconfig                     | 12 ++++++++++++
 arch/x86/entry/erase.c           |  4 ++++
 arch/x86/include/asm/processor.h |  3 +++
 arch/x86/kernel/process_32.c     |  3 +++
 arch/x86/kernel/process_64.c     |  3 +++
 fs/proc/base.c                   | 18 ++++++++++++++++++
 6 files changed, 43 insertions(+)

diff --git a/arch/Kconfig b/arch/Kconfig
index a4a8fba..42ebfb9 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -572,6 +572,18 @@ config STACKLEAK_TRACK_MIN_SIZE
 	  frame size greater than or equal to this parameter.
 	  If unsure, leave the default value 100.
 
+config STACKLEAK_METRICS
+	bool "Show STACKLEAK metrics in the /proc file system"
+	depends on GCC_PLUGIN_STACKLEAK
+	depends on PROC_FS
+	help
+	  If this is set, STACKLEAK metrics for every task are available in
+	  the /proc file system. In particular, /proc/<pid>/stack_depth
+	  shows the maximum kernel stack consumption for the current and
+	  previous syscalls. Although this information is not precise, it
+	  can be useful for estimating the STACKLEAK performance impact for
+	  your workloads.
+
 config HAVE_CC_STACKPROTECTOR
 	bool
 	help
diff --git a/arch/x86/entry/erase.c b/arch/x86/entry/erase.c
index 4892335..be09cc6 100644
--- a/arch/x86/entry/erase.c
+++ b/arch/x86/entry/erase.c
@@ -32,6 +32,10 @@ asmlinkage void erase_kstack(void)
 	if (p == boundary)
 		p += sizeof(unsigned long);
 
+#ifdef CONFIG_STACKLEAK_METRICS
+	current->thread.prev_lowest_stack = p;
+#endif
+
 	/*
 	 * So let's write the poison value to the kernel stack.
 	 * Start from the address in p and move up till the new boundary.
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 0c87813..bca1074 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -496,6 +496,9 @@ struct thread_struct {
 
 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
 	unsigned long		lowest_stack;
+# ifdef CONFIG_STACKLEAK_METRICS
+	unsigned long		prev_lowest_stack;
+# endif
 #endif
 
 	unsigned int		sig_on_uaccess_err:1;
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 1b0892e..577c104 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -139,6 +139,9 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
 	p->thread.lowest_stack = (unsigned long)task_stack_page(p) +
 						sizeof(unsigned long);
+# ifdef CONFIG_STACKLEAK_METRICS
+	p->thread.prev_lowest_stack = p->thread.lowest_stack;
+# endif
 #endif
 
 	if (unlikely(p->flags & PF_KTHREAD)) {
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 82122af..7a82f0d 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -284,6 +284,9 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
 	p->thread.lowest_stack = (unsigned long)task_stack_page(p) +
 						sizeof(unsigned long);
+# ifdef CONFIG_STACKLEAK_METRICS
+	p->thread.prev_lowest_stack = p->thread.lowest_stack;
+# endif
 #endif
 
 	savesegment(gs, p->thread.gsindex);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 9298324..6a7f9bd 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2914,6 +2914,21 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns,
 }
 #endif /* CONFIG_LIVEPATCH */
 
+#ifdef CONFIG_STACKLEAK_METRICS
+static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns,
+				struct pid *pid, struct task_struct *task)
+{
+	unsigned long prev_depth = THREAD_SIZE -
+			(task->thread.prev_lowest_stack & (THREAD_SIZE - 1));
+	unsigned long depth = THREAD_SIZE -
+			(task->thread.lowest_stack & (THREAD_SIZE - 1));
+
+	seq_printf(m, "previous stack depth: %lu\nstack depth: %lu\n",
+							prev_depth, depth);
+	return 0;
+}
+#endif /* CONFIG_STACKLEAK_METRICS */
+
 /*
  * Thread groups
  */
@@ -3018,6 +3033,9 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_LIVEPATCH
 	ONE("patch_state",  S_IRUSR, proc_pid_patch_state),
 #endif
+#ifdef CONFIG_STACKLEAK_METRICS
+	ONE("stack_depth", S_IRUGO, proc_stack_depth),
+#endif
 };
 
 static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v11 6/6] doc: self-protection: Add information about STACKLEAK feature
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
                   ` (4 preceding siblings ...)
  2018-04-06 14:22 ` [PATCH v11 5/6] fs/proc: Show STACKLEAK metrics in the /proc file system Alexander Popov
@ 2018-04-06 14:22 ` Alexander Popov
  2018-05-02 20:33   ` Laura Abbott
  2018-05-14 18:55 ` [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Laura Abbott
  7 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-04-06 14:22 UTC (permalink / raw)
  To: kernel-hardening, Kees Cook, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel,
	alex.popov

Add information about STACKLEAK feature to "Stack depth overflow" and
"Memory poisoning" sections of self-protection.rst.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
---
 Documentation/security/self-protection.rst | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/Documentation/security/self-protection.rst b/Documentation/security/self-protection.rst
index 0f53826..b685f18 100644
--- a/Documentation/security/self-protection.rst
+++ b/Documentation/security/self-protection.rst
@@ -165,10 +165,15 @@ Stack depth overflow
 A less well understood attack is using a bug that triggers the
 kernel to consume stack memory with deep function calls or large stack
 allocations. With this attack it is possible to write beyond the end of
-the kernel's preallocated stack space and into sensitive structures. Two
-important changes need to be made for better protections: moving the
-sensitive thread_info structure elsewhere, and adding a faulting memory
-hole at the bottom of the stack to catch these overflows.
+the kernel's preallocated stack space and into sensitive structures.
+The combination of the following measures gives better protection:
+
+* moving the sensitive thread_info structure off the stack
+  (``CONFIG_THREAD_INFO_IN_TASK``);
+* adding a faulting memory hole at the bottom of the stack to catch
+  these overflows (``CONFIG_VMAP_STACK``);
+* runtime checking that alloca() calls don't overstep the stack boundary
+  (``CONFIG_GCC_PLUGIN_STACKLEAK``).
 
 Heap memory integrity
 ---------------------
@@ -302,11 +307,11 @@ sure structure holes are cleared.
 Memory poisoning
 ----------------
 
-When releasing memory, it is best to poison the contents (clear stack on
-syscall return, wipe heap memory on a free), to avoid reuse attacks that
-rely on the old contents of memory. This frustrates many uninitialized
-variable attacks, stack content exposures, heap content exposures, and
-use-after-free attacks.
+When releasing memory, it is best to poison the contents, to avoid reuse
+attacks that rely on the old contents of memory. E.g., clear stack on a
+syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a
+free. This frustrates many uninitialized variable attacks, stack content
+exposures, heap content exposures, and use-after-free attacks.
 
 Destination tracking
 --------------------
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-06 14:22 ` [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls Alexander Popov
@ 2018-04-16 18:29   ` Kees Cook
  2018-04-18 18:33     ` Laura Abbott
  2018-04-18 18:50     ` Dave Hansen
  2018-04-24  4:23   ` Dave Hansen
  1 sibling, 2 replies; 70+ messages in thread
From: Kees Cook @ 2018-04-16 18:29 UTC (permalink / raw)
  To: Alexander Popov, Dave Hansen, Ingo Molnar, Laura Abbott, Linus Torvalds
  Cc: Kernel Hardening, PaX Team, Brad Spengler, Andy Lutomirski,
	Tycho Andersen, Mark Rutland, Ard Biesheuvel, Borislav Petkov,
	Richard Sandiford, Thomas Gleixner, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Fri, Apr 6, 2018 at 7:22 AM, Alexander Popov <alex.popov@linux.com> wrote:
> This commit introduces the architecture-specific code filling the used
> part of the kernel stack with a poison value before returning to the
> userspace. Full STACKLEAK feature also contains the gcc plugin which
> comes in a separate commit.

Thanks for sending this again! And thanks for the updated reasoning
for why this remains a valuable addition:

https://lkml.kernel.org/r/1523024546-6150-1-git-send-email-alex.popov@linux.com

I, too, remain convinced this is a good protection to have, even as we
slowly remove VLAs and try to improve the compiler's initialization of
stack variables.

Dave, Ingo, Linus: how does this look? With the assembly rewritten
into C, the entry changes are very small:

>  arch/x86/entry/entry_32.S        |  7 ++++++
>  arch/x86/entry/entry_64.S        |  3 +++
>  arch/x86/entry/entry_64_compat.S |  5 ++++
>  arch/x86/entry/erase.c           | 54 ++++++++++++++++++++++++++++++++++++++++

I'd really like to get people's Ack/Review. :)

Laura, can this C version work for arm64 as well?

Thanks,

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-16 18:29   ` Kees Cook
@ 2018-04-18 18:33     ` Laura Abbott
  2018-04-18 18:50     ` Dave Hansen
  1 sibling, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-04-18 18:33 UTC (permalink / raw)
  To: Kees Cook, Alexander Popov, Dave Hansen, Ingo Molnar, Linus Torvalds
  Cc: Kernel Hardening, PaX Team, Brad Spengler, Andy Lutomirski,
	Tycho Andersen, Mark Rutland, Ard Biesheuvel, Borislav Petkov,
	Richard Sandiford, Thomas Gleixner, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On 04/16/2018 11:29 AM, Kees Cook wrote:
> On Fri, Apr 6, 2018 at 7:22 AM, Alexander Popov <alex.popov@linux.com> wrote:
>> This commit introduces the architecture-specific code filling the used
>> part of the kernel stack with a poison value before returning to the
>> userspace. Full STACKLEAK feature also contains the gcc plugin which
>> comes in a separate commit.
> 
> Thanks for sending this again! And thanks for the updated reasoning
> for why this remains a valuable addition:
> 
> https://lkml.kernel.org/r/1523024546-6150-1-git-send-email-alex.popov@linux.com
> 
> I, too, remain convinced this is a good protection to have, even as we
> slowly remove VLAs and try to improve the compiler's initialization of
> stack variables.
> 
> Dave, Ingo, Linus: how does this look? With the assembly rewritten
> into C, the entry changes are very small:
> 
>>   arch/x86/entry/entry_32.S        |  7 ++++++
>>   arch/x86/entry/entry_64.S        |  3 +++
>>   arch/x86/entry/entry_64_compat.S |  5 ++++
>>   arch/x86/entry/erase.c           | 54 ++++++++++++++++++++++++++++++++++++++++
> 
> I'd really like to get people's Ack/Review. :)
> 
> Laura, can this C version work for arm64 as well?
> 
> Thanks,
> 
> -Kees
> 

I did a quick port and it seems to work on a minimal system
(passes LKDTM tests). I'll clean it up and do a few more
tests to send out and see about give this series another
review.

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-16 18:29   ` Kees Cook
  2018-04-18 18:33     ` Laura Abbott
@ 2018-04-18 18:50     ` Dave Hansen
  2018-04-24  1:03       ` Kees Cook
  1 sibling, 1 reply; 70+ messages in thread
From: Dave Hansen @ 2018-04-18 18:50 UTC (permalink / raw)
  To: Kees Cook, Alexander Popov, Ingo Molnar, Laura Abbott, Linus Torvalds
  Cc: Kernel Hardening, PaX Team, Brad Spengler, Andy Lutomirski,
	Tycho Andersen, Mark Rutland, Ard Biesheuvel, Borislav Petkov,
	Richard Sandiford, Thomas Gleixner, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On 04/16/2018 11:29 AM, Kees Cook wrote:
> Dave, Ingo, Linus: how does this look? With the assembly rewritten
> into C, the entry changes are very small:

The assembly looks very nice to me now.  It is as minimally invasive as
it can get.  Definitely no objections from me on that part.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-18 18:50     ` Dave Hansen
@ 2018-04-24  1:03       ` Kees Cook
  0 siblings, 0 replies; 70+ messages in thread
From: Kees Cook @ 2018-04-24  1:03 UTC (permalink / raw)
  To: Dave Hansen
  Cc: Alexander Popov, Ingo Molnar, Laura Abbott, Linus Torvalds,
	Kernel Hardening, PaX Team, Brad Spengler, Andy Lutomirski,
	Tycho Andersen, Mark Rutland, Ard Biesheuvel, Borislav Petkov,
	Richard Sandiford, Thomas Gleixner, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Wed, Apr 18, 2018 at 11:50 AM, Dave Hansen
<dave.hansen@linux.intel.com> wrote:
> On 04/16/2018 11:29 AM, Kees Cook wrote:
>> Dave, Ingo, Linus: how does this look? With the assembly rewritten
>> into C, the entry changes are very small:
>
> The assembly looks very nice to me now.  It is as minimally invasive as
> it can get.  Definitely no objections from me on that part.

Can you give an Acked-by for the x86 parts? Or Ingo?

If this is workable, I'd like to carry it in -next to see if anything
else shakes out...

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-06 14:22 ` [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls Alexander Popov
  2018-04-16 18:29   ` Kees Cook
@ 2018-04-24  4:23   ` Dave Hansen
  2018-04-30 23:48     ` Kees Cook
  1 sibling, 1 reply; 70+ messages in thread
From: Dave Hansen @ 2018-04-24  4:23 UTC (permalink / raw)
  To: Alexander Popov, kernel-hardening, Kees Cook, PaX Team,
	Brad Spengler, Ingo Molnar, Andy Lutomirski, Tycho Andersen,
	Laura Abbott, Mark Rutland, Ard Biesheuvel, Borislav Petkov,
	Richard Sandiford, Thomas Gleixner, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Mathias Krause, Vikas Shivappa,
	Kyle Huey, Dmitry Safonov, Will Deacon, Arnd Bergmann,
	Florian Weimer, Boris Lukashev, x86, linux-kernel

Hi Alexander,

You can add:

Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>

for this patch if you like.  I haven't taken a super close look at the
rest, but this is certainly minimally invasive from my point of view for
the entry code.  Thanks, again for reworking it.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-24  4:23   ` Dave Hansen
@ 2018-04-30 23:48     ` Kees Cook
  2018-05-02  8:42       ` Thomas Gleixner
  0 siblings, 1 reply; 70+ messages in thread
From: Kees Cook @ 2018-04-30 23:48 UTC (permalink / raw)
  To: Dave Hansen, Linus Torvalds
  Cc: Alexander Popov, Kernel Hardening, PaX Team, Brad Spengler,
	Ingo Molnar, Andy Lutomirski, Tycho Andersen, Laura Abbott,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Mon, Apr 23, 2018 at 9:23 PM, Dave Hansen
<dave.hansen@linux.intel.com> wrote:
> Hi Alexander,
>
> You can add:
>
> Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
>
> for this patch if you like.  I haven't taken a super close look at the
> rest, but this is certainly minimally invasive from my point of view for
> the entry code.  Thanks, again for reworking it.

Thanks Dave!

Given this improvement and your review, I'm going to start carrying
this for linux-next. Linus, if you're still opposed to this even after
the changes here in v11, please let us know. I'd rather hash things
out now instead of during a NAK in the 4.18 merge window. :)

Thanks!

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-04-30 23:48     ` Kees Cook
@ 2018-05-02  8:42       ` Thomas Gleixner
  2018-05-02 12:38         ` Kees Cook
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Gleixner @ 2018-05-02  8:42 UTC (permalink / raw)
  To: Kees Cook
  Cc: Dave Hansen, Linus Torvalds, Alexander Popov, Kernel Hardening,
	PaX Team, Brad Spengler, Ingo Molnar, Andy Lutomirski,
	Tycho Andersen, Laura Abbott, Mark Rutland, Ard Biesheuvel,
	Borislav Petkov, Richard Sandiford, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Mon, 30 Apr 2018, Kees Cook wrote:

> On Mon, Apr 23, 2018 at 9:23 PM, Dave Hansen
> <dave.hansen@linux.intel.com> wrote:
> > Hi Alexander,
> >
> > You can add:
> >
> > Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
> >
> > for this patch if you like.  I haven't taken a super close look at the
> > rest, but this is certainly minimally invasive from my point of view for
> > the entry code.  Thanks, again for reworking it.
> 
> Thanks Dave!
> 
> Given this improvement and your review, I'm going to start carrying
> this for linux-next. Linus, if you're still opposed to this even after
> the changes here in v11, please let us know. I'd rather hash things
> out now instead of during a NAK in the 4.18 merge window. :)

Kees, can we please route that x86/entry stuff through tip to avoid
conflicts as there are other changes in that area on the horizon.

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-05-02  8:42       ` Thomas Gleixner
@ 2018-05-02 12:38         ` Kees Cook
  2018-05-02 12:39           ` Thomas Gleixner
  0 siblings, 1 reply; 70+ messages in thread
From: Kees Cook @ 2018-05-02 12:38 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Dave Hansen, Linus Torvalds, Alexander Popov, Kernel Hardening,
	PaX Team, Brad Spengler, Ingo Molnar, Andy Lutomirski,
	Tycho Andersen, Laura Abbott, Mark Rutland, Ard Biesheuvel,
	Borislav Petkov, Richard Sandiford, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Wed, May 2, 2018 at 1:42 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> On Mon, 30 Apr 2018, Kees Cook wrote:
>
>> On Mon, Apr 23, 2018 at 9:23 PM, Dave Hansen
>> <dave.hansen@linux.intel.com> wrote:
>> > Hi Alexander,
>> >
>> > You can add:
>> >
>> > Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
>> >
>> > for this patch if you like.  I haven't taken a super close look at the
>> > rest, but this is certainly minimally invasive from my point of view for
>> > the entry code.  Thanks, again for reworking it.
>>
>> Thanks Dave!
>>
>> Given this improvement and your review, I'm going to start carrying
>> this for linux-next. Linus, if you're still opposed to this even after
>> the changes here in v11, please let us know. I'd rather hash things
>> out now instead of during a NAK in the 4.18 merge window. :)
>
> Kees, can we please route that x86/entry stuff through tip to avoid
> conflicts as there are other changes in that area on the horizon.

Sure, let me figure out how best to split up the patches, since it
touch x86/entry, gcc-plugins, and lkdtm. Thanks!

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-05-02 12:38         ` Kees Cook
@ 2018-05-02 12:39           ` Thomas Gleixner
  2018-05-02 12:51             ` Kees Cook
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Gleixner @ 2018-05-02 12:39 UTC (permalink / raw)
  To: Kees Cook
  Cc: Dave Hansen, Linus Torvalds, Alexander Popov, Kernel Hardening,
	PaX Team, Brad Spengler, Ingo Molnar, Andy Lutomirski,
	Tycho Andersen, Laura Abbott, Mark Rutland, Ard Biesheuvel,
	Borislav Petkov, Richard Sandiford, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Wed, 2 May 2018, Kees Cook wrote:
> On Wed, May 2, 2018 at 1:42 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> > On Mon, 30 Apr 2018, Kees Cook wrote:
> >
> >> On Mon, Apr 23, 2018 at 9:23 PM, Dave Hansen
> >> <dave.hansen@linux.intel.com> wrote:
> >> > Hi Alexander,
> >> >
> >> > You can add:
> >> >
> >> > Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
> >> >
> >> > for this patch if you like.  I haven't taken a super close look at the
> >> > rest, but this is certainly minimally invasive from my point of view for
> >> > the entry code.  Thanks, again for reworking it.
> >>
> >> Thanks Dave!
> >>
> >> Given this improvement and your review, I'm going to start carrying
> >> this for linux-next. Linus, if you're still opposed to this even after
> >> the changes here in v11, please let us know. I'd rather hash things
> >> out now instead of during a NAK in the 4.18 merge window. :)
> >
> > Kees, can we please route that x86/entry stuff through tip to avoid
> > conflicts as there are other changes in that area on the horizon.
> 
> Sure, let me figure out how best to split up the patches, since it
> touch x86/entry, gcc-plugins, and lkdtm. Thanks!

Are they independent or do they carry dependencies?

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-05-02 12:39           ` Thomas Gleixner
@ 2018-05-02 12:51             ` Kees Cook
  2018-05-02 21:02               ` Kees Cook
  0 siblings, 1 reply; 70+ messages in thread
From: Kees Cook @ 2018-05-02 12:51 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Dave Hansen, Linus Torvalds, Alexander Popov, Kernel Hardening,
	PaX Team, Brad Spengler, Ingo Molnar, Andy Lutomirski,
	Tycho Andersen, Laura Abbott, Mark Rutland, Ard Biesheuvel,
	Borislav Petkov, Richard Sandiford, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Wed, May 2, 2018 at 5:39 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> On Wed, 2 May 2018, Kees Cook wrote:
>> On Wed, May 2, 2018 at 1:42 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
>> > On Mon, 30 Apr 2018, Kees Cook wrote:
>> >
>> >> On Mon, Apr 23, 2018 at 9:23 PM, Dave Hansen
>> >> <dave.hansen@linux.intel.com> wrote:
>> >> > Hi Alexander,
>> >> >
>> >> > You can add:
>> >> >
>> >> > Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
>> >> >
>> >> > for this patch if you like.  I haven't taken a super close look at the
>> >> > rest, but this is certainly minimally invasive from my point of view for
>> >> > the entry code.  Thanks, again for reworking it.
>> >>
>> >> Thanks Dave!
>> >>
>> >> Given this improvement and your review, I'm going to start carrying
>> >> this for linux-next. Linus, if you're still opposed to this even after
>> >> the changes here in v11, please let us know. I'd rather hash things
>> >> out now instead of during a NAK in the 4.18 merge window. :)
>> >
>> > Kees, can we please route that x86/entry stuff through tip to avoid
>> > conflicts as there are other changes in that area on the horizon.
>>
>> Sure, let me figure out how best to split up the patches, since it
>> touch x86/entry, gcc-plugins, and lkdtm. Thanks!
>
> Are they independent or do they carry dependencies?

They carry dependencies, as it interacts with the gcc plugin (and
lkdtm). As I don't have other plugin changes for 4.18 queued, you
could take the whole series for x86/entry if you want? Otherwise I can
try to split out the x86 change so it's more self-contained.

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 0/2] Stackleak for arm64
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
@ 2018-05-02 20:33   ` Laura Abbott
  2018-04-06 14:22 ` [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls Alexander Popov
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 20:33 UTC (permalink / raw)
  To: Alexander Popov, Kees Cook, Mark Rutland, Ard Biesheuvel
  Cc: Laura Abbott, kernel-hardening, linux-arm-kernel, linux-kernel

This is an extension of the arm64 stackleak plugin, based on top of the
v11 version for x86. The biggest change from the previous version is the
conversion from assembly to C. It's mostly taken straight from the x86
version modulo a few cleanups and seems to work just fine. If this gets
Acks, I'd like Kees to start carrying in his tree for -next.

Laura Abbott (2):
  stackleak: Update for arm64
  arm64: Clear the stack

 arch/arm64/Kconfig                     |  1 +
 arch/arm64/include/asm/processor.h     |  6 ++++
 arch/arm64/kernel/Makefile             |  3 ++
 arch/arm64/kernel/entry.S              |  6 ++++
 arch/arm64/kernel/erase.c              | 55 ++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/process.c            | 16 ++++++++++
 drivers/firmware/efi/libstub/Makefile  |  3 +-
 scripts/Makefile.gcc-plugins           |  5 +++-
 scripts/gcc-plugins/stackleak_plugin.c |  4 +++
 9 files changed, 97 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm64/kernel/erase.c

-- 
2.14.3

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 0/2] Stackleak for arm64
@ 2018-05-02 20:33   ` Laura Abbott
  0 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 20:33 UTC (permalink / raw)
  To: linux-arm-kernel

This is an extension of the arm64 stackleak plugin, based on top of the
v11 version for x86. The biggest change from the previous version is the
conversion from assembly to C. It's mostly taken straight from the x86
version modulo a few cleanups and seems to work just fine. If this gets
Acks, I'd like Kees to start carrying in his tree for -next.

Laura Abbott (2):
  stackleak: Update for arm64
  arm64: Clear the stack

 arch/arm64/Kconfig                     |  1 +
 arch/arm64/include/asm/processor.h     |  6 ++++
 arch/arm64/kernel/Makefile             |  3 ++
 arch/arm64/kernel/entry.S              |  6 ++++
 arch/arm64/kernel/erase.c              | 55 ++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/process.c            | 16 ++++++++++
 drivers/firmware/efi/libstub/Makefile  |  3 +-
 scripts/Makefile.gcc-plugins           |  5 +++-
 scripts/gcc-plugins/stackleak_plugin.c |  4 +++
 9 files changed, 97 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm64/kernel/erase.c

-- 
2.14.3

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 1/2] stackleak: Update for arm64
  2018-05-02 20:33   ` Laura Abbott
@ 2018-05-02 20:33     ` Laura Abbott
  -1 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 20:33 UTC (permalink / raw)
  To: Alexander Popov, Kees Cook, Mark Rutland, Ard Biesheuvel
  Cc: Laura Abbott, kernel-hardening, linux-arm-kernel, linux-kernel

arm64 has another layer of indirection in the RTL.
Account for this in the plugin.

Signed-off-by: Laura Abbott <labbott@redhat.com>
---
Fixed from previous version to be a vector expression.
---
 scripts/gcc-plugins/stackleak_plugin.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c
index 6ac2a055ec61..0a55ecaf44df 100644
--- a/scripts/gcc-plugins/stackleak_plugin.c
+++ b/scripts/gcc-plugins/stackleak_plugin.c
@@ -253,6 +253,10 @@ static unsigned int stackleak_cleanup_execute(void)
 		 * that insn.
 		 */
 		body = PATTERN(insn);
+		/* arm64 is different */
+		if (GET_CODE(body) == PARALLEL)
+			body = XVECEXP(body, 0, 0);
+
 		if (GET_CODE(body) != CALL)
 			continue;
 
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 1/2] stackleak: Update for arm64
@ 2018-05-02 20:33     ` Laura Abbott
  0 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 20:33 UTC (permalink / raw)
  To: linux-arm-kernel

arm64 has another layer of indirection in the RTL.
Account for this in the plugin.

Signed-off-by: Laura Abbott <labbott@redhat.com>
---
Fixed from previous version to be a vector expression.
---
 scripts/gcc-plugins/stackleak_plugin.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c
index 6ac2a055ec61..0a55ecaf44df 100644
--- a/scripts/gcc-plugins/stackleak_plugin.c
+++ b/scripts/gcc-plugins/stackleak_plugin.c
@@ -253,6 +253,10 @@ static unsigned int stackleak_cleanup_execute(void)
 		 * that insn.
 		 */
 		body = PATTERN(insn);
+		/* arm64 is different */
+		if (GET_CODE(body) == PARALLEL)
+			body = XVECEXP(body, 0, 0);
+
 		if (GET_CODE(body) != CALL)
 			continue;
 
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
  2018-05-02 20:33   ` Laura Abbott
@ 2018-05-02 20:33     ` Laura Abbott
  -1 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 20:33 UTC (permalink / raw)
  To: Alexander Popov, Kees Cook, Mark Rutland, Ard Biesheuvel
  Cc: Laura Abbott, kernel-hardening, linux-arm-kernel, linux-kernel


Implementation of stackleak based heavily on the x86 version

Signed-off-by: Laura Abbott <labbott@redhat.com>
---
Now written in C instead of a bunch of assembly.
---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    |  6 ++++
 arch/arm64/kernel/Makefile            |  3 ++
 arch/arm64/kernel/entry.S             |  6 ++++
 arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/process.c           | 16 ++++++++++
 drivers/firmware/efi/libstub/Makefile |  3 +-
 scripts/Makefile.gcc-plugins          |  5 +++-
 8 files changed, 93 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm64/kernel/erase.c

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index eb2cf4938f6d..b0221db95dc9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -92,6 +92,7 @@ config ARM64
 	select HAVE_ARCH_MMAP_RND_BITS
 	select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
 	select HAVE_ARCH_SECCOMP_FILTER
+	select HAVE_ARCH_STACKLEAK
 	select HAVE_ARCH_THREAD_STRUCT_WHITELIST
 	select HAVE_ARCH_TRACEHOOK
 	select HAVE_ARCH_TRANSPARENT_HUGEPAGE
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 767598932549..d31ab80ff647 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -124,6 +124,12 @@ struct thread_struct {
 	unsigned long		fault_address;	/* fault info */
 	unsigned long		fault_code;	/* ESR_EL1 value */
 	struct debug_info	debug;		/* debugging */
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	unsigned long           lowest_stack;
+#ifdef CONFIG_STACKLEAK_METRICS
+	unsigned long		prev_lowest_stack;
+#endif
+#endif
 };
 
 static inline void arch_thread_struct_whitelist(unsigned long *offset,
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index bf825f38d206..0ceea613c65b 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
 arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
 arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
 
+arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
+KASAN_SANITIZE_erase.o	:= n
+
 obj-y					+= $(arm64-obj-y) vdso/ probes/
 obj-m					+= $(arm64-obj-m)
 head-y					:= head.o
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index ec2ee720e33e..3144f1ebdc18 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	ERASE_KSTACK
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	erase_kstack
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -906,6 +911,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	ERASE_KSTACK
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
new file mode 100644
index 000000000000..b8b5648d893b
--- /dev/null
+++ b/arch/arm64/kernel/erase.c
@@ -0,0 +1,55 @@
+#include <linux/bug.h>
+#include <linux/sched.h>
+#include <asm/current.h>
+#include <asm/linkage.h>
+#include <asm/processor.h>
+
+asmlinkage void erase_kstack(void)
+{
+	unsigned long p = current->thread.lowest_stack;
+	unsigned long boundary = p & ~(THREAD_SIZE - 1);
+	unsigned long poison = 0;
+	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
+							sizeof(unsigned long);
+
+	/*
+	 * Let's search for the poison value in the stack.
+	 * Start from the lowest_stack and go to the bottom.
+	 */
+	while (p > boundary && poison <= check_depth) {
+		if (*(unsigned long *)p == STACKLEAK_POISON)
+			poison++;
+		else
+			poison = 0;
+
+		p -= sizeof(unsigned long);
+	}
+
+	/*
+	 * One long int at the bottom of the thread stack is reserved and
+	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
+	 */
+	if (p == boundary)
+		p += sizeof(unsigned long);
+
+#ifdef CONFIG_STACKLEAK_METRICS
+	current->thread.prev_lowest_stack = p;
+#endif
+
+	/*
+	 * So let's write the poison value to the kernel stack.
+	 * Start from the address in p and move up till the new boundary.
+	 */
+	boundary = current_stack_pointer;
+
+	BUG_ON(boundary - p >= THREAD_SIZE);
+
+	while (p < boundary) {
+		*(unsigned long *)p = STACKLEAK_POISON;
+		p += sizeof(unsigned long);
+	}
+
+	/* Reset the lowest_stack value for the next syscall */
+	current->thread.lowest_stack = current_stack_pointer;
+}
+
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index f08a2ed9db0d..156fa0a0da19 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
 	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
 	p->thread.cpu_context.sp = (unsigned long)childregs;
 
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
+#endif
 	ptrace_hw_copy_thread(p);
 
 	return 0;
@@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used check_alloca(unsigned long size)
+{
+	unsigned long sp, stack_left;
+
+	sp = current_stack_pointer;
+
+	stack_left = sp & (THREAD_SIZE - 1);
+	BUG_ON(stack_left < 256 || size >= stack_left - 256);
+}
+EXPORT_SYMBOL(check_alloca);
+#endif
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index a34e9290a699..25dd2a14560d 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
 KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
 				   -D__NO_FORTIFY \
 				   $(call cc-option,-ffreestanding) \
-				   $(call cc-option,-fno-stack-protector)
+				   $(call cc-option,-fno-stack-protector) \
+				   $(DISABLE_STACKLEAK_PLUGIN)
 
 GCOV_PROFILE			:= n
 KASAN_SANITIZE			:= n
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 8d6070fc538f..6cc0e35d3324 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
 
   gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)	+= stackleak_plugin.so
   gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)	+= -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
+  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+    DISABLE_STACKLEAK_PLUGIN		+= -fplugin-arg-stackleak_plugin-disable
+  endif
 
   GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
 
   export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
-  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
+  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
 
   ifneq ($(PLUGINCC),)
     # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-02 20:33     ` Laura Abbott
  0 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 20:33 UTC (permalink / raw)
  To: linux-arm-kernel


Implementation of stackleak based heavily on the x86 version

Signed-off-by: Laura Abbott <labbott@redhat.com>
---
Now written in C instead of a bunch of assembly.
---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    |  6 ++++
 arch/arm64/kernel/Makefile            |  3 ++
 arch/arm64/kernel/entry.S             |  6 ++++
 arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/process.c           | 16 ++++++++++
 drivers/firmware/efi/libstub/Makefile |  3 +-
 scripts/Makefile.gcc-plugins          |  5 +++-
 8 files changed, 93 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm64/kernel/erase.c

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index eb2cf4938f6d..b0221db95dc9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -92,6 +92,7 @@ config ARM64
 	select HAVE_ARCH_MMAP_RND_BITS
 	select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
 	select HAVE_ARCH_SECCOMP_FILTER
+	select HAVE_ARCH_STACKLEAK
 	select HAVE_ARCH_THREAD_STRUCT_WHITELIST
 	select HAVE_ARCH_TRACEHOOK
 	select HAVE_ARCH_TRANSPARENT_HUGEPAGE
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 767598932549..d31ab80ff647 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -124,6 +124,12 @@ struct thread_struct {
 	unsigned long		fault_address;	/* fault info */
 	unsigned long		fault_code;	/* ESR_EL1 value */
 	struct debug_info	debug;		/* debugging */
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	unsigned long           lowest_stack;
+#ifdef CONFIG_STACKLEAK_METRICS
+	unsigned long		prev_lowest_stack;
+#endif
+#endif
 };
 
 static inline void arch_thread_struct_whitelist(unsigned long *offset,
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index bf825f38d206..0ceea613c65b 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
 arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
 arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
 
+arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
+KASAN_SANITIZE_erase.o	:= n
+
 obj-y					+= $(arm64-obj-y) vdso/ probes/
 obj-m					+= $(arm64-obj-m)
 head-y					:= head.o
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index ec2ee720e33e..3144f1ebdc18 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	ERASE_KSTACK
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	erase_kstack
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -906,6 +911,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	ERASE_KSTACK
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
new file mode 100644
index 000000000000..b8b5648d893b
--- /dev/null
+++ b/arch/arm64/kernel/erase.c
@@ -0,0 +1,55 @@
+#include <linux/bug.h>
+#include <linux/sched.h>
+#include <asm/current.h>
+#include <asm/linkage.h>
+#include <asm/processor.h>
+
+asmlinkage void erase_kstack(void)
+{
+	unsigned long p = current->thread.lowest_stack;
+	unsigned long boundary = p & ~(THREAD_SIZE - 1);
+	unsigned long poison = 0;
+	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
+							sizeof(unsigned long);
+
+	/*
+	 * Let's search for the poison value in the stack.
+	 * Start from the lowest_stack and go to the bottom.
+	 */
+	while (p > boundary && poison <= check_depth) {
+		if (*(unsigned long *)p == STACKLEAK_POISON)
+			poison++;
+		else
+			poison = 0;
+
+		p -= sizeof(unsigned long);
+	}
+
+	/*
+	 * One long int@the bottom of the thread stack is reserved and
+	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
+	 */
+	if (p == boundary)
+		p += sizeof(unsigned long);
+
+#ifdef CONFIG_STACKLEAK_METRICS
+	current->thread.prev_lowest_stack = p;
+#endif
+
+	/*
+	 * So let's write the poison value to the kernel stack.
+	 * Start from the address in p and move up till the new boundary.
+	 */
+	boundary = current_stack_pointer;
+
+	BUG_ON(boundary - p >= THREAD_SIZE);
+
+	while (p < boundary) {
+		*(unsigned long *)p = STACKLEAK_POISON;
+		p += sizeof(unsigned long);
+	}
+
+	/* Reset the lowest_stack value for the next syscall */
+	current->thread.lowest_stack = current_stack_pointer;
+}
+
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index f08a2ed9db0d..156fa0a0da19 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
 	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
 	p->thread.cpu_context.sp = (unsigned long)childregs;
 
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
+#endif
 	ptrace_hw_copy_thread(p);
 
 	return 0;
@@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used check_alloca(unsigned long size)
+{
+	unsigned long sp, stack_left;
+
+	sp = current_stack_pointer;
+
+	stack_left = sp & (THREAD_SIZE - 1);
+	BUG_ON(stack_left < 256 || size >= stack_left - 256);
+}
+EXPORT_SYMBOL(check_alloca);
+#endif
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index a34e9290a699..25dd2a14560d 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
 KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
 				   -D__NO_FORTIFY \
 				   $(call cc-option,-ffreestanding) \
-				   $(call cc-option,-fno-stack-protector)
+				   $(call cc-option,-fno-stack-protector) \
+				   $(DISABLE_STACKLEAK_PLUGIN)
 
 GCOV_PROFILE			:= n
 KASAN_SANITIZE			:= n
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 8d6070fc538f..6cc0e35d3324 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
 
   gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)	+= stackleak_plugin.so
   gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)	+= -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
+  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+    DISABLE_STACKLEAK_PLUGIN		+= -fplugin-arg-stackleak_plugin-disable
+  endif
 
   GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
 
   export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
-  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
+  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
 
   ifneq ($(PLUGINCC),)
     # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-05-02 12:51             ` Kees Cook
@ 2018-05-02 21:02               ` Kees Cook
  2018-05-06 10:04                 ` Thomas Gleixner
  0 siblings, 1 reply; 70+ messages in thread
From: Kees Cook @ 2018-05-02 21:02 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Dave Hansen, Linus Torvalds, Alexander Popov, Kernel Hardening,
	PaX Team, Brad Spengler, Ingo Molnar, Andy Lutomirski,
	Tycho Andersen, Laura Abbott, Mark Rutland, Ard Biesheuvel,
	Borislav Petkov, Richard Sandiford, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Wed, May 2, 2018 at 5:51 AM, Kees Cook <keescook@chromium.org> wrote:
> On Wed, May 2, 2018 at 5:39 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
>> On Wed, 2 May 2018, Kees Cook wrote:
>>> On Wed, May 2, 2018 at 1:42 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
>>> > On Mon, 30 Apr 2018, Kees Cook wrote:
>>> >
>>> >> On Mon, Apr 23, 2018 at 9:23 PM, Dave Hansen
>>> >> <dave.hansen@linux.intel.com> wrote:
>>> >> > Hi Alexander,
>>> >> >
>>> >> > You can add:
>>> >> >
>>> >> > Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
>>> >> >
>>> >> > for this patch if you like.  I haven't taken a super close look at the
>>> >> > rest, but this is certainly minimally invasive from my point of view for
>>> >> > the entry code.  Thanks, again for reworking it.
>>> >>
>>> >> Thanks Dave!
>>> >>
>>> >> Given this improvement and your review, I'm going to start carrying
>>> >> this for linux-next. Linus, if you're still opposed to this even after
>>> >> the changes here in v11, please let us know. I'd rather hash things
>>> >> out now instead of during a NAK in the 4.18 merge window. :)
>>> >
>>> > Kees, can we please route that x86/entry stuff through tip to avoid
>>> > conflicts as there are other changes in that area on the horizon.
>>>
>>> Sure, let me figure out how best to split up the patches, since it
>>> touch x86/entry, gcc-plugins, and lkdtm. Thanks!
>>
>> Are they independent or do they carry dependencies?
>
> They carry dependencies, as it interacts with the gcc plugin (and
> lkdtm). As I don't have other plugin changes for 4.18 queued, you
> could take the whole series for x86/entry if you want? Otherwise I can
> try to split out the x86 change so it's more self-contained.

The best way to do this would be to add the x86 entry changes without
CONFIG_GCC_PLUGIN_STACKLEAK, which leaves the results not
compile-testable. Alternatively, if you carried everything, it'd be
weird too, with arm64 coming (which has small changes to the plugin).

I think it'd be better for this to go via my tree with your Ack (where
I can carry the plugin, lkdtm, x86, and arm64 changes). How does that
sound?

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-02 20:33     ` Laura Abbott
@ 2018-05-02 21:31       ` Kees Cook
  -1 siblings, 0 replies; 70+ messages in thread
From: Kees Cook @ 2018-05-02 21:31 UTC (permalink / raw)
  To: Laura Abbott
  Cc: Alexander Popov, Mark Rutland, Ard Biesheuvel, Kernel Hardening,
	linux-arm-kernel, LKML

On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>
> Implementation of stackleak based heavily on the x86 version

Awesome! Notes below for both you and Alexander, since I think we can
create a common code base instead of having near-duplicates in the
arch/ trees...

>
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> Now written in C instead of a bunch of assembly.
> ---
>  arch/arm64/Kconfig                    |  1 +
>  arch/arm64/include/asm/processor.h    |  6 ++++
>  arch/arm64/kernel/Makefile            |  3 ++
>  arch/arm64/kernel/entry.S             |  6 ++++
>  arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
>  arch/arm64/kernel/process.c           | 16 ++++++++++
>  drivers/firmware/efi/libstub/Makefile |  3 +-
>  scripts/Makefile.gcc-plugins          |  5 +++-
>  8 files changed, 93 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm64/kernel/erase.c
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index eb2cf4938f6d..b0221db95dc9 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -92,6 +92,7 @@ config ARM64
>         select HAVE_ARCH_MMAP_RND_BITS
>         select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
>         select HAVE_ARCH_SECCOMP_FILTER
> +       select HAVE_ARCH_STACKLEAK
>         select HAVE_ARCH_THREAD_STRUCT_WHITELIST
>         select HAVE_ARCH_TRACEHOOK
>         select HAVE_ARCH_TRANSPARENT_HUGEPAGE
> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
> index 767598932549..d31ab80ff647 100644
> --- a/arch/arm64/include/asm/processor.h
> +++ b/arch/arm64/include/asm/processor.h
> @@ -124,6 +124,12 @@ struct thread_struct {
>         unsigned long           fault_address;  /* fault info */
>         unsigned long           fault_code;     /* ESR_EL1 value */
>         struct debug_info       debug;          /* debugging */
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +       unsigned long           lowest_stack;
> +#ifdef CONFIG_STACKLEAK_METRICS
> +       unsigned long           prev_lowest_stack;
> +#endif
> +#endif

I wonder if x86 and arm64 could include a common struct here that was
empty when the plugin is disabled... it would keep the ifdefs in one
place. Maybe include/linux/stackleak.h could be:

---start---
/* Poison value points to the unused hole in the virtual memory map */
#define STACKLEAK_POISON -0xBEEF
#define STACKLEAK_POISON_CHECK_DEPTH 128

struct stackleak {
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
       unsigned long           lowest;
#ifdef CONFIG_STACKLEAK_METRICS
       unsigned long           prev_lowest;
#endif
#endif
};

asmlinkage void erase_kstack(void);
---eof---

and arch/*/include/asm/processor.h could do:

@@ -124,6 +124,12 @@ struct thread_struct {
        unsigned long           fault_address;  /* fault info */
        unsigned long           fault_code;     /* ESR_EL1 value */
        struct debug_info       debug;          /* debugging */
+       struct stackleak         stackleak;

and arch/x86/entry/erase.c could move to maybe kernel/stackleak.c?
(Oh, I notice this needs an SPDX line too.)

>  static inline void arch_thread_struct_whitelist(unsigned long *offset,
> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index bf825f38d206..0ceea613c65b 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>  arm64-obj-$(CONFIG_CRASH_DUMP)         += crash_dump.o
>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)  += sdei.o
>
> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
> +KASAN_SANITIZE_erase.o := n
> +
>  obj-y                                  += $(arm64-obj-y) vdso/ probes/
>  obj-m                                  += $(arm64-obj-m)
>  head-y                                 := head.o
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index ec2ee720e33e..3144f1ebdc18 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -401,6 +401,11 @@ tsk        .req    x28             // current thread_info
>
>         .text
>
> +       .macro  ERASE_KSTACK
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +       bl      erase_kstack
> +#endif
> +       .endm
>  /*
>   * Exception vectors.
>   */
> @@ -906,6 +911,7 @@ ret_to_user:
>         cbnz    x2, work_pending
>  finish_ret_to_user:
>         enable_step_tsk x1, x2
> +       ERASE_KSTACK
>         kernel_exit 0
>  ENDPROC(ret_to_user)

Nice. All of the return paths end up here (I went looking for
ret_from_fork's path). :)

>
> diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
> new file mode 100644
> index 000000000000..b8b5648d893b
> --- /dev/null
> +++ b/arch/arm64/kernel/erase.c
> @@ -0,0 +1,55 @@
> +#include <linux/bug.h>
> +#include <linux/sched.h>
> +#include <asm/current.h>
> +#include <asm/linkage.h>
> +#include <asm/processor.h>
> +
> +asmlinkage void erase_kstack(void)
> +{
> +       unsigned long p = current->thread.lowest_stack;
> +       unsigned long boundary = p & ~(THREAD_SIZE - 1);
> +       unsigned long poison = 0;
> +       const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
> +                                                       sizeof(unsigned long);
> +
> +       /*
> +        * Let's search for the poison value in the stack.
> +        * Start from the lowest_stack and go to the bottom.
> +        */
> +       while (p > boundary && poison <= check_depth) {
> +               if (*(unsigned long *)p == STACKLEAK_POISON)
> +                       poison++;
> +               else
> +                       poison = 0;
> +
> +               p -= sizeof(unsigned long);
> +       }
> +
> +       /*
> +        * One long int at the bottom of the thread stack is reserved and
> +        * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
> +        */
> +       if (p == boundary)
> +               p += sizeof(unsigned long);
> +
> +#ifdef CONFIG_STACKLEAK_METRICS
> +       current->thread.prev_lowest_stack = p;
> +#endif
> +
> +       /*
> +        * So let's write the poison value to the kernel stack.
> +        * Start from the address in p and move up till the new boundary.
> +        */
> +       boundary = current_stack_pointer;

This is the only difference between x86 and arm64 in this code. What
do you think about implementing on_thread_stack() to match x86:

        if (on_thread_stack())
                boundary = current_stack_pointer;
        else
                boundary = current_top_of_stack();

then we could make this common code too instead of having two copies in arch/?

> +       BUG_ON(boundary - p >= THREAD_SIZE);
> +
> +       while (p < boundary) {
> +               *(unsigned long *)p = STACKLEAK_POISON;
> +               p += sizeof(unsigned long);
> +       }
> +
> +       /* Reset the lowest_stack value for the next syscall */
> +       current->thread.lowest_stack = current_stack_pointer;
> +}
> +
> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index f08a2ed9db0d..156fa0a0da19 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>         p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>         p->thread.cpu_context.sp = (unsigned long)childregs;
>
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +       p->thread.lowest_stack = (unsigned long)task_stack_page(p);
> +#endif
>         ptrace_hw_copy_thread(p);
>
>         return 0;
> @@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
>  {
>         current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
>  }
> +
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +void __used check_alloca(unsigned long size)
> +{
> +       unsigned long sp, stack_left;
> +
> +       sp = current_stack_pointer;
> +
> +       stack_left = sp & (THREAD_SIZE - 1);
> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
> +}
> +EXPORT_SYMBOL(check_alloca);

This is pretty different from x86. Is this just an artifact of ORC, or
something else?

> +#endif
> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> index a34e9290a699..25dd2a14560d 100644
> --- a/drivers/firmware/efi/libstub/Makefile
> +++ b/drivers/firmware/efi/libstub/Makefile
> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)  += -I$(srctree)/scripts/dtc/libfdt
>  KBUILD_CFLAGS                  := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>                                    -D__NO_FORTIFY \
>                                    $(call cc-option,-ffreestanding) \
> -                                  $(call cc-option,-fno-stack-protector)
> +                                  $(call cc-option,-fno-stack-protector) \
> +                                  $(DISABLE_STACKLEAK_PLUGIN)
>
>  GCOV_PROFILE                   := n
>  KASAN_SANITIZE                 := n
> diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
> index 8d6070fc538f..6cc0e35d3324 100644
> --- a/scripts/Makefile.gcc-plugins
> +++ b/scripts/Makefile.gcc-plugins
> @@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
>
>    gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)    += stackleak_plugin.so
>    gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)     += -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
> +  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +    DISABLE_STACKLEAK_PLUGIN           += -fplugin-arg-stackleak_plugin-disable
> +  endif
>
>    GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
>
>    export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
> -  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
> +  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
>
>    ifneq ($(PLUGINCC),)
>      # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
> --
> 2.14.3
>

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-02 21:31       ` Kees Cook
  0 siblings, 0 replies; 70+ messages in thread
From: Kees Cook @ 2018-05-02 21:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>
> Implementation of stackleak based heavily on the x86 version

Awesome! Notes below for both you and Alexander, since I think we can
create a common code base instead of having near-duplicates in the
arch/ trees...

>
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> Now written in C instead of a bunch of assembly.
> ---
>  arch/arm64/Kconfig                    |  1 +
>  arch/arm64/include/asm/processor.h    |  6 ++++
>  arch/arm64/kernel/Makefile            |  3 ++
>  arch/arm64/kernel/entry.S             |  6 ++++
>  arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
>  arch/arm64/kernel/process.c           | 16 ++++++++++
>  drivers/firmware/efi/libstub/Makefile |  3 +-
>  scripts/Makefile.gcc-plugins          |  5 +++-
>  8 files changed, 93 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm64/kernel/erase.c
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index eb2cf4938f6d..b0221db95dc9 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -92,6 +92,7 @@ config ARM64
>         select HAVE_ARCH_MMAP_RND_BITS
>         select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
>         select HAVE_ARCH_SECCOMP_FILTER
> +       select HAVE_ARCH_STACKLEAK
>         select HAVE_ARCH_THREAD_STRUCT_WHITELIST
>         select HAVE_ARCH_TRACEHOOK
>         select HAVE_ARCH_TRANSPARENT_HUGEPAGE
> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
> index 767598932549..d31ab80ff647 100644
> --- a/arch/arm64/include/asm/processor.h
> +++ b/arch/arm64/include/asm/processor.h
> @@ -124,6 +124,12 @@ struct thread_struct {
>         unsigned long           fault_address;  /* fault info */
>         unsigned long           fault_code;     /* ESR_EL1 value */
>         struct debug_info       debug;          /* debugging */
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +       unsigned long           lowest_stack;
> +#ifdef CONFIG_STACKLEAK_METRICS
> +       unsigned long           prev_lowest_stack;
> +#endif
> +#endif

I wonder if x86 and arm64 could include a common struct here that was
empty when the plugin is disabled... it would keep the ifdefs in one
place. Maybe include/linux/stackleak.h could be:

---start---
/* Poison value points to the unused hole in the virtual memory map */
#define STACKLEAK_POISON -0xBEEF
#define STACKLEAK_POISON_CHECK_DEPTH 128

struct stackleak {
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
       unsigned long           lowest;
#ifdef CONFIG_STACKLEAK_METRICS
       unsigned long           prev_lowest;
#endif
#endif
};

asmlinkage void erase_kstack(void);
---eof---

and arch/*/include/asm/processor.h could do:

@@ -124,6 +124,12 @@ struct thread_struct {
        unsigned long           fault_address;  /* fault info */
        unsigned long           fault_code;     /* ESR_EL1 value */
        struct debug_info       debug;          /* debugging */
+       struct stackleak         stackleak;

and arch/x86/entry/erase.c could move to maybe kernel/stackleak.c?
(Oh, I notice this needs an SPDX line too.)

>  static inline void arch_thread_struct_whitelist(unsigned long *offset,
> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index bf825f38d206..0ceea613c65b 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>  arm64-obj-$(CONFIG_CRASH_DUMP)         += crash_dump.o
>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)  += sdei.o
>
> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
> +KASAN_SANITIZE_erase.o := n
> +
>  obj-y                                  += $(arm64-obj-y) vdso/ probes/
>  obj-m                                  += $(arm64-obj-m)
>  head-y                                 := head.o
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index ec2ee720e33e..3144f1ebdc18 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -401,6 +401,11 @@ tsk        .req    x28             // current thread_info
>
>         .text
>
> +       .macro  ERASE_KSTACK
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +       bl      erase_kstack
> +#endif
> +       .endm
>  /*
>   * Exception vectors.
>   */
> @@ -906,6 +911,7 @@ ret_to_user:
>         cbnz    x2, work_pending
>  finish_ret_to_user:
>         enable_step_tsk x1, x2
> +       ERASE_KSTACK
>         kernel_exit 0
>  ENDPROC(ret_to_user)

Nice. All of the return paths end up here (I went looking for
ret_from_fork's path). :)

>
> diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
> new file mode 100644
> index 000000000000..b8b5648d893b
> --- /dev/null
> +++ b/arch/arm64/kernel/erase.c
> @@ -0,0 +1,55 @@
> +#include <linux/bug.h>
> +#include <linux/sched.h>
> +#include <asm/current.h>
> +#include <asm/linkage.h>
> +#include <asm/processor.h>
> +
> +asmlinkage void erase_kstack(void)
> +{
> +       unsigned long p = current->thread.lowest_stack;
> +       unsigned long boundary = p & ~(THREAD_SIZE - 1);
> +       unsigned long poison = 0;
> +       const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
> +                                                       sizeof(unsigned long);
> +
> +       /*
> +        * Let's search for the poison value in the stack.
> +        * Start from the lowest_stack and go to the bottom.
> +        */
> +       while (p > boundary && poison <= check_depth) {
> +               if (*(unsigned long *)p == STACKLEAK_POISON)
> +                       poison++;
> +               else
> +                       poison = 0;
> +
> +               p -= sizeof(unsigned long);
> +       }
> +
> +       /*
> +        * One long int at the bottom of the thread stack is reserved and
> +        * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
> +        */
> +       if (p == boundary)
> +               p += sizeof(unsigned long);
> +
> +#ifdef CONFIG_STACKLEAK_METRICS
> +       current->thread.prev_lowest_stack = p;
> +#endif
> +
> +       /*
> +        * So let's write the poison value to the kernel stack.
> +        * Start from the address in p and move up till the new boundary.
> +        */
> +       boundary = current_stack_pointer;

This is the only difference between x86 and arm64 in this code. What
do you think about implementing on_thread_stack() to match x86:

        if (on_thread_stack())
                boundary = current_stack_pointer;
        else
                boundary = current_top_of_stack();

then we could make this common code too instead of having two copies in arch/?

> +       BUG_ON(boundary - p >= THREAD_SIZE);
> +
> +       while (p < boundary) {
> +               *(unsigned long *)p = STACKLEAK_POISON;
> +               p += sizeof(unsigned long);
> +       }
> +
> +       /* Reset the lowest_stack value for the next syscall */
> +       current->thread.lowest_stack = current_stack_pointer;
> +}
> +
> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index f08a2ed9db0d..156fa0a0da19 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>         p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>         p->thread.cpu_context.sp = (unsigned long)childregs;
>
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +       p->thread.lowest_stack = (unsigned long)task_stack_page(p);
> +#endif
>         ptrace_hw_copy_thread(p);
>
>         return 0;
> @@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
>  {
>         current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
>  }
> +
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +void __used check_alloca(unsigned long size)
> +{
> +       unsigned long sp, stack_left;
> +
> +       sp = current_stack_pointer;
> +
> +       stack_left = sp & (THREAD_SIZE - 1);
> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
> +}
> +EXPORT_SYMBOL(check_alloca);

This is pretty different from x86. Is this just an artifact of ORC, or
something else?

> +#endif
> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> index a34e9290a699..25dd2a14560d 100644
> --- a/drivers/firmware/efi/libstub/Makefile
> +++ b/drivers/firmware/efi/libstub/Makefile
> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)  += -I$(srctree)/scripts/dtc/libfdt
>  KBUILD_CFLAGS                  := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>                                    -D__NO_FORTIFY \
>                                    $(call cc-option,-ffreestanding) \
> -                                  $(call cc-option,-fno-stack-protector)
> +                                  $(call cc-option,-fno-stack-protector) \
> +                                  $(DISABLE_STACKLEAK_PLUGIN)
>
>  GCOV_PROFILE                   := n
>  KASAN_SANITIZE                 := n
> diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
> index 8d6070fc538f..6cc0e35d3324 100644
> --- a/scripts/Makefile.gcc-plugins
> +++ b/scripts/Makefile.gcc-plugins
> @@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
>
>    gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)    += stackleak_plugin.so
>    gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)     += -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
> +  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +    DISABLE_STACKLEAK_PLUGIN           += -fplugin-arg-stackleak_plugin-disable
> +  endif
>
>    GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
>
>    export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
> -  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
> +  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
>
>    ifneq ($(PLUGINCC),)
>      # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
> --
> 2.14.3
>

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-02 21:31       ` Kees Cook
@ 2018-05-02 23:07         ` Laura Abbott
  -1 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 23:07 UTC (permalink / raw)
  To: Kees Cook
  Cc: Alexander Popov, Mark Rutland, Ard Biesheuvel, Kernel Hardening,
	linux-arm-kernel, LKML

On 05/02/2018 02:31 PM, Kees Cook wrote:
> On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
> 
> Awesome! Notes below for both you and Alexander, since I think we can
> create a common code base instead of having near-duplicates in the
> arch/ trees...
> 
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
>> ---
>>   arch/arm64/Kconfig                    |  1 +
>>   arch/arm64/include/asm/processor.h    |  6 ++++
>>   arch/arm64/kernel/Makefile            |  3 ++
>>   arch/arm64/kernel/entry.S             |  6 ++++
>>   arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
>>   arch/arm64/kernel/process.c           | 16 ++++++++++
>>   drivers/firmware/efi/libstub/Makefile |  3 +-
>>   scripts/Makefile.gcc-plugins          |  5 +++-
>>   8 files changed, 93 insertions(+), 2 deletions(-)
>>   create mode 100644 arch/arm64/kernel/erase.c
>>
>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>> index eb2cf4938f6d..b0221db95dc9 100644
>> --- a/arch/arm64/Kconfig
>> +++ b/arch/arm64/Kconfig
>> @@ -92,6 +92,7 @@ config ARM64
>>          select HAVE_ARCH_MMAP_RND_BITS
>>          select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
>>          select HAVE_ARCH_SECCOMP_FILTER
>> +       select HAVE_ARCH_STACKLEAK
>>          select HAVE_ARCH_THREAD_STRUCT_WHITELIST
>>          select HAVE_ARCH_TRACEHOOK
>>          select HAVE_ARCH_TRANSPARENT_HUGEPAGE
>> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
>> index 767598932549..d31ab80ff647 100644
>> --- a/arch/arm64/include/asm/processor.h
>> +++ b/arch/arm64/include/asm/processor.h
>> @@ -124,6 +124,12 @@ struct thread_struct {
>>          unsigned long           fault_address;  /* fault info */
>>          unsigned long           fault_code;     /* ESR_EL1 value */
>>          struct debug_info       debug;          /* debugging */
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +       unsigned long           lowest_stack;
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +       unsigned long           prev_lowest_stack;
>> +#endif
>> +#endif
> 
> I wonder if x86 and arm64 could include a common struct here that was
> empty when the plugin is disabled... it would keep the ifdefs in one
> place. Maybe include/linux/stackleak.h could be:
> 
> ---start---
> /* Poison value points to the unused hole in the virtual memory map */
> #define STACKLEAK_POISON -0xBEEF
> #define STACKLEAK_POISON_CHECK_DEPTH 128
> 
> struct stackleak {
> #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>         unsigned long           lowest;
> #ifdef CONFIG_STACKLEAK_METRICS
>         unsigned long           prev_lowest;
> #endif
> #endif
> };
> 

Is this well defined across all compilers if the plugin is off?
This seems to compile with gcc at least but 0 sized structs
make me a little uneasy.

> asmlinkage void erase_kstack(void);
> ---eof---
> 
> and arch/*/include/asm/processor.h could do:
> 
> @@ -124,6 +124,12 @@ struct thread_struct {
>          unsigned long           fault_address;  /* fault info */
>          unsigned long           fault_code;     /* ESR_EL1 value */
>          struct debug_info       debug;          /* debugging */
> +       struct stackleak         stackleak;
> 
> and arch/x86/entry/erase.c could move to maybe kernel/stackleak.c?
> (Oh, I notice this needs an SPDX line too.)
> 
>>   static inline void arch_thread_struct_whitelist(unsigned long *offset,
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>   arm64-obj-$(CONFIG_CRASH_DUMP)         += crash_dump.o
>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)  += sdei.o
>>
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o := n
>> +
>>   obj-y                                  += $(arm64-obj-y) vdso/ probes/
>>   obj-m                                  += $(arm64-obj-m)
>>   head-y                                 := head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk        .req    x28             // current thread_info
>>
>>          .text
>>
>> +       .macro  ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +       bl      erase_kstack
>> +#endif
>> +       .endm
>>   /*
>>    * Exception vectors.
>>    */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>          cbnz    x2, work_pending
>>   finish_ret_to_user:
>>          enable_step_tsk x1, x2
>> +       ERASE_KSTACK
>>          kernel_exit 0
>>   ENDPROC(ret_to_user)
> 
> Nice. All of the return paths end up here (I went looking for
> ret_from_fork's path). :)
> 
>>
>> diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
>> new file mode 100644
>> index 000000000000..b8b5648d893b
>> --- /dev/null
>> +++ b/arch/arm64/kernel/erase.c
>> @@ -0,0 +1,55 @@
>> +#include <linux/bug.h>
>> +#include <linux/sched.h>
>> +#include <asm/current.h>
>> +#include <asm/linkage.h>
>> +#include <asm/processor.h>
>> +
>> +asmlinkage void erase_kstack(void)
>> +{
>> +       unsigned long p = current->thread.lowest_stack;
>> +       unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +       unsigned long poison = 0;
>> +       const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +                                                       sizeof(unsigned long);
>> +
>> +       /*
>> +        * Let's search for the poison value in the stack.
>> +        * Start from the lowest_stack and go to the bottom.
>> +        */
>> +       while (p > boundary && poison <= check_depth) {
>> +               if (*(unsigned long *)p == STACKLEAK_POISON)
>> +                       poison++;
>> +               else
>> +                       poison = 0;
>> +
>> +               p -= sizeof(unsigned long);
>> +       }
>> +
>> +       /*
>> +        * One long int at the bottom of the thread stack is reserved and
>> +        * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +        */
>> +       if (p == boundary)
>> +               p += sizeof(unsigned long);
>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +       current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +       /*
>> +        * So let's write the poison value to the kernel stack.
>> +        * Start from the address in p and move up till the new boundary.
>> +        */
>> +       boundary = current_stack_pointer;
> 
> This is the only difference between x86 and arm64 in this code. What
> do you think about implementing on_thread_stack() to match x86:
> 
>          if (on_thread_stack())
>                  boundary = current_stack_pointer;
>          else
>                  boundary = current_top_of_stack();
> 
> then we could make this common code too instead of having two copies in arch/?
> 

The issue isn't on_thread_stack, it's current_top_of_stack which isn't
defined on arm64. I agree it would be good if the code would be common
but I'm not sure how much we want to start trying to force APIs.

>> +       BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +       while (p < boundary) {
>> +               *(unsigned long *)p = STACKLEAK_POISON;
>> +               p += sizeof(unsigned long);
>> +       }
>> +
>> +       /* Reset the lowest_stack value for the next syscall */
>> +       current->thread.lowest_stack = current_stack_pointer;
>> +}
>> +
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>          p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>          p->thread.cpu_context.sp = (unsigned long)childregs;
>>
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +       p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>> +#endif
>>          ptrace_hw_copy_thread(p);
>>
>>          return 0;
>> @@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
>>   {
>>          current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
>>   }
>> +
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +       unsigned long sp, stack_left;
>> +
>> +       sp = current_stack_pointer;
>> +
>> +       stack_left = sp & (THREAD_SIZE - 1);
>> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
>> +EXPORT_SYMBOL(check_alloca);
> 
> This is pretty different from x86. Is this just an artifact of ORC, or
> something else?
> 

This was based on the earlier version of x86. I'll confess to
not seeing how the current x86 version ended up with get_stack_info
but I suspect it's either related to ORC unwinding or it's best
practice.

>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)  += -I$(srctree)/scripts/dtc/libfdt
>>   KBUILD_CFLAGS                  := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>                                     -D__NO_FORTIFY \
>>                                     $(call cc-option,-ffreestanding) \
>> -                                  $(call cc-option,-fno-stack-protector)
>> +                                  $(call cc-option,-fno-stack-protector) \
>> +                                  $(DISABLE_STACKLEAK_PLUGIN)
>>
>>   GCOV_PROFILE                   := n
>>   KASAN_SANITIZE                 := n
>> diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
>> index 8d6070fc538f..6cc0e35d3324 100644
>> --- a/scripts/Makefile.gcc-plugins
>> +++ b/scripts/Makefile.gcc-plugins
>> @@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
>>
>>     gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)    += stackleak_plugin.so
>>     gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)     += -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
>> +  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +    DISABLE_STACKLEAK_PLUGIN           += -fplugin-arg-stackleak_plugin-disable
>> +  endif
>>
>>     GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
>>
>>     export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
>> -  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
>> +  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
>>
>>     ifneq ($(PLUGINCC),)
>>       # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
>> --
>> 2.14.3
>>
> 
> -Kees
> 

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-02 23:07         ` Laura Abbott
  0 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-02 23:07 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/02/2018 02:31 PM, Kees Cook wrote:
> On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
> 
> Awesome! Notes below for both you and Alexander, since I think we can
> create a common code base instead of having near-duplicates in the
> arch/ trees...
> 
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
>> ---
>>   arch/arm64/Kconfig                    |  1 +
>>   arch/arm64/include/asm/processor.h    |  6 ++++
>>   arch/arm64/kernel/Makefile            |  3 ++
>>   arch/arm64/kernel/entry.S             |  6 ++++
>>   arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
>>   arch/arm64/kernel/process.c           | 16 ++++++++++
>>   drivers/firmware/efi/libstub/Makefile |  3 +-
>>   scripts/Makefile.gcc-plugins          |  5 +++-
>>   8 files changed, 93 insertions(+), 2 deletions(-)
>>   create mode 100644 arch/arm64/kernel/erase.c
>>
>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>> index eb2cf4938f6d..b0221db95dc9 100644
>> --- a/arch/arm64/Kconfig
>> +++ b/arch/arm64/Kconfig
>> @@ -92,6 +92,7 @@ config ARM64
>>          select HAVE_ARCH_MMAP_RND_BITS
>>          select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
>>          select HAVE_ARCH_SECCOMP_FILTER
>> +       select HAVE_ARCH_STACKLEAK
>>          select HAVE_ARCH_THREAD_STRUCT_WHITELIST
>>          select HAVE_ARCH_TRACEHOOK
>>          select HAVE_ARCH_TRANSPARENT_HUGEPAGE
>> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
>> index 767598932549..d31ab80ff647 100644
>> --- a/arch/arm64/include/asm/processor.h
>> +++ b/arch/arm64/include/asm/processor.h
>> @@ -124,6 +124,12 @@ struct thread_struct {
>>          unsigned long           fault_address;  /* fault info */
>>          unsigned long           fault_code;     /* ESR_EL1 value */
>>          struct debug_info       debug;          /* debugging */
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +       unsigned long           lowest_stack;
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +       unsigned long           prev_lowest_stack;
>> +#endif
>> +#endif
> 
> I wonder if x86 and arm64 could include a common struct here that was
> empty when the plugin is disabled... it would keep the ifdefs in one
> place. Maybe include/linux/stackleak.h could be:
> 
> ---start---
> /* Poison value points to the unused hole in the virtual memory map */
> #define STACKLEAK_POISON -0xBEEF
> #define STACKLEAK_POISON_CHECK_DEPTH 128
> 
> struct stackleak {
> #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>         unsigned long           lowest;
> #ifdef CONFIG_STACKLEAK_METRICS
>         unsigned long           prev_lowest;
> #endif
> #endif
> };
> 

Is this well defined across all compilers if the plugin is off?
This seems to compile with gcc at least but 0 sized structs
make me a little uneasy.

> asmlinkage void erase_kstack(void);
> ---eof---
> 
> and arch/*/include/asm/processor.h could do:
> 
> @@ -124,6 +124,12 @@ struct thread_struct {
>          unsigned long           fault_address;  /* fault info */
>          unsigned long           fault_code;     /* ESR_EL1 value */
>          struct debug_info       debug;          /* debugging */
> +       struct stackleak         stackleak;
> 
> and arch/x86/entry/erase.c could move to maybe kernel/stackleak.c?
> (Oh, I notice this needs an SPDX line too.)
> 
>>   static inline void arch_thread_struct_whitelist(unsigned long *offset,
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>   arm64-obj-$(CONFIG_CRASH_DUMP)         += crash_dump.o
>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)  += sdei.o
>>
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o := n
>> +
>>   obj-y                                  += $(arm64-obj-y) vdso/ probes/
>>   obj-m                                  += $(arm64-obj-m)
>>   head-y                                 := head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk        .req    x28             // current thread_info
>>
>>          .text
>>
>> +       .macro  ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +       bl      erase_kstack
>> +#endif
>> +       .endm
>>   /*
>>    * Exception vectors.
>>    */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>          cbnz    x2, work_pending
>>   finish_ret_to_user:
>>          enable_step_tsk x1, x2
>> +       ERASE_KSTACK
>>          kernel_exit 0
>>   ENDPROC(ret_to_user)
> 
> Nice. All of the return paths end up here (I went looking for
> ret_from_fork's path). :)
> 
>>
>> diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
>> new file mode 100644
>> index 000000000000..b8b5648d893b
>> --- /dev/null
>> +++ b/arch/arm64/kernel/erase.c
>> @@ -0,0 +1,55 @@
>> +#include <linux/bug.h>
>> +#include <linux/sched.h>
>> +#include <asm/current.h>
>> +#include <asm/linkage.h>
>> +#include <asm/processor.h>
>> +
>> +asmlinkage void erase_kstack(void)
>> +{
>> +       unsigned long p = current->thread.lowest_stack;
>> +       unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +       unsigned long poison = 0;
>> +       const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +                                                       sizeof(unsigned long);
>> +
>> +       /*
>> +        * Let's search for the poison value in the stack.
>> +        * Start from the lowest_stack and go to the bottom.
>> +        */
>> +       while (p > boundary && poison <= check_depth) {
>> +               if (*(unsigned long *)p == STACKLEAK_POISON)
>> +                       poison++;
>> +               else
>> +                       poison = 0;
>> +
>> +               p -= sizeof(unsigned long);
>> +       }
>> +
>> +       /*
>> +        * One long int at the bottom of the thread stack is reserved and
>> +        * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +        */
>> +       if (p == boundary)
>> +               p += sizeof(unsigned long);
>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +       current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +       /*
>> +        * So let's write the poison value to the kernel stack.
>> +        * Start from the address in p and move up till the new boundary.
>> +        */
>> +       boundary = current_stack_pointer;
> 
> This is the only difference between x86 and arm64 in this code. What
> do you think about implementing on_thread_stack() to match x86:
> 
>          if (on_thread_stack())
>                  boundary = current_stack_pointer;
>          else
>                  boundary = current_top_of_stack();
> 
> then we could make this common code too instead of having two copies in arch/?
> 

The issue isn't on_thread_stack, it's current_top_of_stack which isn't
defined on arm64. I agree it would be good if the code would be common
but I'm not sure how much we want to start trying to force APIs.

>> +       BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +       while (p < boundary) {
>> +               *(unsigned long *)p = STACKLEAK_POISON;
>> +               p += sizeof(unsigned long);
>> +       }
>> +
>> +       /* Reset the lowest_stack value for the next syscall */
>> +       current->thread.lowest_stack = current_stack_pointer;
>> +}
>> +
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>          p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>          p->thread.cpu_context.sp = (unsigned long)childregs;
>>
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +       p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>> +#endif
>>          ptrace_hw_copy_thread(p);
>>
>>          return 0;
>> @@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
>>   {
>>          current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
>>   }
>> +
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +       unsigned long sp, stack_left;
>> +
>> +       sp = current_stack_pointer;
>> +
>> +       stack_left = sp & (THREAD_SIZE - 1);
>> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
>> +EXPORT_SYMBOL(check_alloca);
> 
> This is pretty different from x86. Is this just an artifact of ORC, or
> something else?
> 

This was based on the earlier version of x86. I'll confess to
not seeing how the current x86 version ended up with get_stack_info
but I suspect it's either related to ORC unwinding or it's best
practice.

>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)  += -I$(srctree)/scripts/dtc/libfdt
>>   KBUILD_CFLAGS                  := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>                                     -D__NO_FORTIFY \
>>                                     $(call cc-option,-ffreestanding) \
>> -                                  $(call cc-option,-fno-stack-protector)
>> +                                  $(call cc-option,-fno-stack-protector) \
>> +                                  $(DISABLE_STACKLEAK_PLUGIN)
>>
>>   GCOV_PROFILE                   := n
>>   KASAN_SANITIZE                 := n
>> diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
>> index 8d6070fc538f..6cc0e35d3324 100644
>> --- a/scripts/Makefile.gcc-plugins
>> +++ b/scripts/Makefile.gcc-plugins
>> @@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
>>
>>     gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)    += stackleak_plugin.so
>>     gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)     += -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
>> +  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +    DISABLE_STACKLEAK_PLUGIN           += -fplugin-arg-stackleak_plugin-disable
>> +  endif
>>
>>     GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
>>
>>     export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
>> -  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
>> +  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
>>
>>     ifneq ($(PLUGINCC),)
>>       # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
>> --
>> 2.14.3
>>
> 
> -Kees
> 

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-02 23:07         ` Laura Abbott
@ 2018-05-02 23:37           ` Kees Cook
  -1 siblings, 0 replies; 70+ messages in thread
From: Kees Cook @ 2018-05-02 23:37 UTC (permalink / raw)
  To: Laura Abbott
  Cc: Alexander Popov, Mark Rutland, Ard Biesheuvel, Kernel Hardening,
	linux-arm-kernel, LKML

On Wed, May 2, 2018 at 4:07 PM, Laura Abbott <labbott@redhat.com> wrote:
> On 05/02/2018 02:31 PM, Kees Cook wrote:
>> struct stackleak {
>> #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>         unsigned long           lowest;
>> #ifdef CONFIG_STACKLEAK_METRICS
>>         unsigned long           prev_lowest;
>> #endif
>> #endif
>> };
>>
>
> Is this well defined across all compilers if the plugin is off?
> This seems to compile with gcc at least but 0 sized structs
> make me a little uneasy.

Yup! Or at least, there have been no problems with this and the
seccomp struct, which is empty when !CONFIG_SECCOMP.

>> This is the only difference between x86 and arm64 in this code. What
>> do you think about implementing on_thread_stack() to match x86:
>>
>>          if (on_thread_stack())
>>                  boundary = current_stack_pointer;
>>          else
>>                  boundary = current_top_of_stack();
>>
>> then we could make this common code too instead of having two copies in
>> arch/?
>>
>
> The issue isn't on_thread_stack, it's current_top_of_stack which isn't
> defined on arm64. I agree it would be good if the code would be common
> but I'm not sure how much we want to start trying to force APIs.

Ah, gotcha. Well, I'd rather we had an #ifdef here that two copies of
the code. ;)

>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +void __used check_alloca(unsigned long size)
>>> +{
>>> +       unsigned long sp, stack_left;
>>> +
>>> +       sp = current_stack_pointer;
>>> +
>>> +       stack_left = sp & (THREAD_SIZE - 1);
>>> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>> +}
>>> +EXPORT_SYMBOL(check_alloca);
>>
>>
>> This is pretty different from x86. Is this just an artifact of ORC, or
>> something else?
>>
>
> This was based on the earlier version of x86. I'll confess to
> not seeing how the current x86 version ended up with get_stack_info
> but I suspect it's either related to ORC unwinding or it's best
> practice.

Alexander, what was the history here?

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-02 23:37           ` Kees Cook
  0 siblings, 0 replies; 70+ messages in thread
From: Kees Cook @ 2018-05-02 23:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, May 2, 2018 at 4:07 PM, Laura Abbott <labbott@redhat.com> wrote:
> On 05/02/2018 02:31 PM, Kees Cook wrote:
>> struct stackleak {
>> #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>         unsigned long           lowest;
>> #ifdef CONFIG_STACKLEAK_METRICS
>>         unsigned long           prev_lowest;
>> #endif
>> #endif
>> };
>>
>
> Is this well defined across all compilers if the plugin is off?
> This seems to compile with gcc at least but 0 sized structs
> make me a little uneasy.

Yup! Or at least, there have been no problems with this and the
seccomp struct, which is empty when !CONFIG_SECCOMP.

>> This is the only difference between x86 and arm64 in this code. What
>> do you think about implementing on_thread_stack() to match x86:
>>
>>          if (on_thread_stack())
>>                  boundary = current_stack_pointer;
>>          else
>>                  boundary = current_top_of_stack();
>>
>> then we could make this common code too instead of having two copies in
>> arch/?
>>
>
> The issue isn't on_thread_stack, it's current_top_of_stack which isn't
> defined on arm64. I agree it would be good if the code would be common
> but I'm not sure how much we want to start trying to force APIs.

Ah, gotcha. Well, I'd rather we had an #ifdef here that two copies of
the code. ;)

>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +void __used check_alloca(unsigned long size)
>>> +{
>>> +       unsigned long sp, stack_left;
>>> +
>>> +       sp = current_stack_pointer;
>>> +
>>> +       stack_left = sp & (THREAD_SIZE - 1);
>>> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>> +}
>>> +EXPORT_SYMBOL(check_alloca);
>>
>>
>> This is pretty different from x86. Is this just an artifact of ORC, or
>> something else?
>>
>
> This was based on the earlier version of x86. I'll confess to
> not seeing how the current x86 version ended up with get_stack_info
> but I suspect it's either related to ORC unwinding or it's best
> practice.

Alexander, what was the history here?

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-02 20:33     ` Laura Abbott
@ 2018-05-03  7:19       ` Mark Rutland
  -1 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-03  7:19 UTC (permalink / raw)
  To: Laura Abbott
  Cc: Alexander Popov, Kees Cook, Ard Biesheuvel, kernel-hardening,
	linux-arm-kernel, linux-kernel

Hi Laura,

On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
> 
> Implementation of stackleak based heavily on the x86 version
> 
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> Now written in C instead of a bunch of assembly.

This looks neat!

I have a few minor comments below.

> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index bf825f38d206..0ceea613c65b 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>  
> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
> +KASAN_SANITIZE_erase.o	:= n

I suspect we want to avoid the full set of instrumentation suspects here, e.g.
GKOV, KASAN, UBSAN, and KCOV.

> +
>  obj-y					+= $(arm64-obj-y) vdso/ probes/
>  obj-m					+= $(arm64-obj-m)
>  head-y					:= head.o
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index ec2ee720e33e..3144f1ebdc18 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>  
>  	.text
>  
> +	.macro	ERASE_KSTACK
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +	bl	erase_kstack
> +#endif
> +	.endm

Nit: The rest of our asm macros are lower-case -- can we stick to that here?

>  /*
>   * Exception vectors.
>   */
> @@ -906,6 +911,7 @@ ret_to_user:
>  	cbnz	x2, work_pending
>  finish_ret_to_user:
>  	enable_step_tsk x1, x2
> +	ERASE_KSTACK
>  	kernel_exit 0
>  ENDPROC(ret_to_user)

I believe we also need this in ret_fast_syscall.

[...]

> +asmlinkage void erase_kstack(void)
> +{
> +	unsigned long p = current->thread.lowest_stack;
> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
> +	unsigned long poison = 0;
> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
> +							sizeof(unsigned long);
> +
> +	/*
> +	 * Let's search for the poison value in the stack.
> +	 * Start from the lowest_stack and go to the bottom.
> +	 */
> +	while (p > boundary && poison <= check_depth) {
> +		if (*(unsigned long *)p == STACKLEAK_POISON)
> +			poison++;
> +		else
> +			poison = 0;
> +
> +		p -= sizeof(unsigned long);
> +	}
> +
> +	/*
> +	 * One long int at the bottom of the thread stack is reserved and
> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
> +	 */
> +	if (p == boundary)
> +		p += sizeof(unsigned long);

I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
given that's supposed to return the last *usable* long on the stack, and we
don't account for this elsewhere.

If we did, then IIUC we could do:

	unsigned long boundary = (unsigned long)end_of_stack(current);

... at the start of the function, and not have to worry about this explicitly.

> +
> +#ifdef CONFIG_STACKLEAK_METRICS
> +	current->thread.prev_lowest_stack = p;
> +#endif
> +
> +	/*
> +	 * So let's write the poison value to the kernel stack.
> +	 * Start from the address in p and move up till the new boundary.
> +	 */
> +	boundary = current_stack_pointer;

I worry a little that the compiler can move the SP during a function's
lifetime, but maybe that's only the case when there are VLAs, or something like
that?

> +
> +	BUG_ON(boundary - p >= THREAD_SIZE);
> +
> +	while (p < boundary) {
> +		*(unsigned long *)p = STACKLEAK_POISON;
> +		p += sizeof(unsigned long);
> +	}
> +
> +	/* Reset the lowest_stack value for the next syscall */
> +	current->thread.lowest_stack = current_stack_pointer;
> +}

Once this function returns, its data is left on the stack. Is that not a problem?

No strong feelings either way, but it might be worth mentioning in the commit
message.

> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index f08a2ed9db0d..156fa0a0da19 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>  	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>  	p->thread.cpu_context.sp = (unsigned long)childregs;
>  
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);

Nit: end_of_stack(p) would be slightly better semantically, even though
currently equivalent to task_stack_page(p).

[...]

> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +void __used check_alloca(unsigned long size)
> +{
> +	unsigned long sp, stack_left;
> +
> +	sp = current_stack_pointer;
> +
> +	stack_left = sp & (THREAD_SIZE - 1);
> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
> +}

Is this arbitrary, or is there something special about 256?

Even if this is arbitrary, can we give it some mnemonic?

> +EXPORT_SYMBOL(check_alloca);
> +#endif
> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> index a34e9290a699..25dd2a14560d 100644
> --- a/drivers/firmware/efi/libstub/Makefile
> +++ b/drivers/firmware/efi/libstub/Makefile
> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>  				   -D__NO_FORTIFY \
>  				   $(call cc-option,-ffreestanding) \
> -				   $(call cc-option,-fno-stack-protector)
> +				   $(call cc-option,-fno-stack-protector) \
> +				   $(DISABLE_STACKLEAK_PLUGIN)
>  
>  GCOV_PROFILE			:= n
>  KASAN_SANITIZE			:= n

I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-03  7:19       ` Mark Rutland
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-03  7:19 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Laura,

On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
> 
> Implementation of stackleak based heavily on the x86 version
> 
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> Now written in C instead of a bunch of assembly.

This looks neat!

I have a few minor comments below.

> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index bf825f38d206..0ceea613c65b 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>  
> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
> +KASAN_SANITIZE_erase.o	:= n

I suspect we want to avoid the full set of instrumentation suspects here, e.g.
GKOV, KASAN, UBSAN, and KCOV.

> +
>  obj-y					+= $(arm64-obj-y) vdso/ probes/
>  obj-m					+= $(arm64-obj-m)
>  head-y					:= head.o
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index ec2ee720e33e..3144f1ebdc18 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>  
>  	.text
>  
> +	.macro	ERASE_KSTACK
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +	bl	erase_kstack
> +#endif
> +	.endm

Nit: The rest of our asm macros are lower-case -- can we stick to that here?

>  /*
>   * Exception vectors.
>   */
> @@ -906,6 +911,7 @@ ret_to_user:
>  	cbnz	x2, work_pending
>  finish_ret_to_user:
>  	enable_step_tsk x1, x2
> +	ERASE_KSTACK
>  	kernel_exit 0
>  ENDPROC(ret_to_user)

I believe we also need this in ret_fast_syscall.

[...]

> +asmlinkage void erase_kstack(void)
> +{
> +	unsigned long p = current->thread.lowest_stack;
> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
> +	unsigned long poison = 0;
> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
> +							sizeof(unsigned long);
> +
> +	/*
> +	 * Let's search for the poison value in the stack.
> +	 * Start from the lowest_stack and go to the bottom.
> +	 */
> +	while (p > boundary && poison <= check_depth) {
> +		if (*(unsigned long *)p == STACKLEAK_POISON)
> +			poison++;
> +		else
> +			poison = 0;
> +
> +		p -= sizeof(unsigned long);
> +	}
> +
> +	/*
> +	 * One long int at the bottom of the thread stack is reserved and
> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
> +	 */
> +	if (p == boundary)
> +		p += sizeof(unsigned long);

I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
given that's supposed to return the last *usable* long on the stack, and we
don't account for this elsewhere.

If we did, then IIUC we could do:

	unsigned long boundary = (unsigned long)end_of_stack(current);

... at the start of the function, and not have to worry about this explicitly.

> +
> +#ifdef CONFIG_STACKLEAK_METRICS
> +	current->thread.prev_lowest_stack = p;
> +#endif
> +
> +	/*
> +	 * So let's write the poison value to the kernel stack.
> +	 * Start from the address in p and move up till the new boundary.
> +	 */
> +	boundary = current_stack_pointer;

I worry a little that the compiler can move the SP during a function's
lifetime, but maybe that's only the case when there are VLAs, or something like
that?

> +
> +	BUG_ON(boundary - p >= THREAD_SIZE);
> +
> +	while (p < boundary) {
> +		*(unsigned long *)p = STACKLEAK_POISON;
> +		p += sizeof(unsigned long);
> +	}
> +
> +	/* Reset the lowest_stack value for the next syscall */
> +	current->thread.lowest_stack = current_stack_pointer;
> +}

Once this function returns, its data is left on the stack. Is that not a problem?

No strong feelings either way, but it might be worth mentioning in the commit
message.

> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index f08a2ed9db0d..156fa0a0da19 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>  	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>  	p->thread.cpu_context.sp = (unsigned long)childregs;
>  
> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);

Nit: end_of_stack(p) would be slightly better semantically, even though
currently equivalent to task_stack_page(p).

[...]

> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> +void __used check_alloca(unsigned long size)
> +{
> +	unsigned long sp, stack_left;
> +
> +	sp = current_stack_pointer;
> +
> +	stack_left = sp & (THREAD_SIZE - 1);
> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
> +}

Is this arbitrary, or is there something special about 256?

Even if this is arbitrary, can we give it some mnemonic?

> +EXPORT_SYMBOL(check_alloca);
> +#endif
> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> index a34e9290a699..25dd2a14560d 100644
> --- a/drivers/firmware/efi/libstub/Makefile
> +++ b/drivers/firmware/efi/libstub/Makefile
> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>  				   -D__NO_FORTIFY \
>  				   $(call cc-option,-ffreestanding) \
> -				   $(call cc-option,-fno-stack-protector)
> +				   $(call cc-option,-fno-stack-protector) \
> +				   $(DISABLE_STACKLEAK_PLUGIN)
>  
>  GCOV_PROFILE			:= n
>  KASAN_SANITIZE			:= n

I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03  7:19       ` Mark Rutland
@ 2018-05-03 11:37         ` Ard Biesheuvel
  -1 siblings, 0 replies; 70+ messages in thread
From: Ard Biesheuvel @ 2018-05-03 11:37 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Laura Abbott, Alexander Popov, Kees Cook, Kernel Hardening,
	linux-arm-kernel, Linux Kernel Mailing List

On 3 May 2018 at 09:19, Mark Rutland <mark.rutland@arm.com> wrote:
> Hi Laura,
>
> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
>
> This looks neat!
>
> I have a few minor comments below.
>
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>  arm64-obj-$(CONFIG_CRASH_DUMP)               += crash_dump.o
>>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)        += sdei.o
>>
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o       := n
>
> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> GKOV, KASAN, UBSAN, and KCOV.
>
>> +
>>  obj-y                                        += $(arm64-obj-y) vdso/ probes/
>>  obj-m                                        += $(arm64-obj-m)
>>  head-y                                       := head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk      .req    x28             // current thread_info
>>
>>       .text
>>
>> +     .macro  ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +     bl      erase_kstack
>> +#endif
>> +     .endm
>
> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
>
>>  /*
>>   * Exception vectors.
>>   */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>       cbnz    x2, work_pending
>>  finish_ret_to_user:
>>       enable_step_tsk x1, x2
>> +     ERASE_KSTACK
>>       kernel_exit 0
>>  ENDPROC(ret_to_user)
>
> I believe we also need this in ret_fast_syscall.
>
> [...]
>
>> +asmlinkage void erase_kstack(void)
>> +{
>> +     unsigned long p = current->thread.lowest_stack;
>> +     unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +     unsigned long poison = 0;
>> +     const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +                                                     sizeof(unsigned long);
>> +
>> +     /*
>> +      * Let's search for the poison value in the stack.
>> +      * Start from the lowest_stack and go to the bottom.
>> +      */
>> +     while (p > boundary && poison <= check_depth) {
>> +             if (*(unsigned long *)p == STACKLEAK_POISON)
>> +                     poison++;
>> +             else
>> +                     poison = 0;
>> +
>> +             p -= sizeof(unsigned long);
>> +     }
>> +
>> +     /*
>> +      * One long int at the bottom of the thread stack is reserved and
>> +      * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +      */
>> +     if (p == boundary)
>> +             p += sizeof(unsigned long);
>
> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> given that's supposed to return the last *usable* long on the stack, and we
> don't account for this elsewhere.
>
> If we did, then IIUC we could do:
>
>         unsigned long boundary = (unsigned long)end_of_stack(current);
>
> ... at the start of the function, and not have to worry about this explicitly.
>
>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +     current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +     /*
>> +      * So let's write the poison value to the kernel stack.
>> +      * Start from the address in p and move up till the new boundary.
>> +      */
>> +     boundary = current_stack_pointer;
>
> I worry a little that the compiler can move the SP during a function's
> lifetime, but maybe that's only the case when there are VLAs, or something like
> that?
>

I think the AAPCS permits the compiler to allocate the stack space for
outgoing variables (i.e., args 9 and beyond or other argument types
that require passing via the stack) at a smaller scope than the entire
function, although GCC does appear to allocate it at the beginning
(based on some quick experiments)


>> +
>> +     BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +     while (p < boundary) {
>> +             *(unsigned long *)p = STACKLEAK_POISON;
>> +             p += sizeof(unsigned long);
>> +     }
>> +
>> +     /* Reset the lowest_stack value for the next syscall */
>> +     current->thread.lowest_stack = current_stack_pointer;
>> +}
>
> Once this function returns, its data is left on the stack. Is that not a problem?
>
> No strong feelings either way, but it might be worth mentioning in the commit
> message.
>
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>       p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>       p->thread.cpu_context.sp = (unsigned long)childregs;
>>
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +     p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>
> Nit: end_of_stack(p) would be slightly better semantically, even though
> currently equivalent to task_stack_page(p).
>
> [...]
>
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +     unsigned long sp, stack_left;
>> +
>> +     sp = current_stack_pointer;
>> +
>> +     stack_left = sp & (THREAD_SIZE - 1);
>> +     BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
>
> Is this arbitrary, or is there something special about 256?
>
> Even if this is arbitrary, can we give it some mnemonic?
>
>> +EXPORT_SYMBOL(check_alloca);
>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)        += -I$(srctree)/scripts/dtc/libfdt
>>  KBUILD_CFLAGS                        := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>                                  -D__NO_FORTIFY \
>>                                  $(call cc-option,-ffreestanding) \
>> -                                $(call cc-option,-fno-stack-protector)
>> +                                $(call cc-option,-fno-stack-protector) \
>> +                                $(DISABLE_STACKLEAK_PLUGIN)
>>
>>  GCOV_PROFILE                 := n
>>  KASAN_SANITIZE                       := n
>
> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
>
> Thanks,
> Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-03 11:37         ` Ard Biesheuvel
  0 siblings, 0 replies; 70+ messages in thread
From: Ard Biesheuvel @ 2018-05-03 11:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 3 May 2018 at 09:19, Mark Rutland <mark.rutland@arm.com> wrote:
> Hi Laura,
>
> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
>
> This looks neat!
>
> I have a few minor comments below.
>
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>  arm64-obj-$(CONFIG_CRASH_DUMP)               += crash_dump.o
>>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)        += sdei.o
>>
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o       := n
>
> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> GKOV, KASAN, UBSAN, and KCOV.
>
>> +
>>  obj-y                                        += $(arm64-obj-y) vdso/ probes/
>>  obj-m                                        += $(arm64-obj-m)
>>  head-y                                       := head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk      .req    x28             // current thread_info
>>
>>       .text
>>
>> +     .macro  ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +     bl      erase_kstack
>> +#endif
>> +     .endm
>
> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
>
>>  /*
>>   * Exception vectors.
>>   */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>       cbnz    x2, work_pending
>>  finish_ret_to_user:
>>       enable_step_tsk x1, x2
>> +     ERASE_KSTACK
>>       kernel_exit 0
>>  ENDPROC(ret_to_user)
>
> I believe we also need this in ret_fast_syscall.
>
> [...]
>
>> +asmlinkage void erase_kstack(void)
>> +{
>> +     unsigned long p = current->thread.lowest_stack;
>> +     unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +     unsigned long poison = 0;
>> +     const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +                                                     sizeof(unsigned long);
>> +
>> +     /*
>> +      * Let's search for the poison value in the stack.
>> +      * Start from the lowest_stack and go to the bottom.
>> +      */
>> +     while (p > boundary && poison <= check_depth) {
>> +             if (*(unsigned long *)p == STACKLEAK_POISON)
>> +                     poison++;
>> +             else
>> +                     poison = 0;
>> +
>> +             p -= sizeof(unsigned long);
>> +     }
>> +
>> +     /*
>> +      * One long int at the bottom of the thread stack is reserved and
>> +      * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +      */
>> +     if (p == boundary)
>> +             p += sizeof(unsigned long);
>
> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> given that's supposed to return the last *usable* long on the stack, and we
> don't account for this elsewhere.
>
> If we did, then IIUC we could do:
>
>         unsigned long boundary = (unsigned long)end_of_stack(current);
>
> ... at the start of the function, and not have to worry about this explicitly.
>
>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +     current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +     /*
>> +      * So let's write the poison value to the kernel stack.
>> +      * Start from the address in p and move up till the new boundary.
>> +      */
>> +     boundary = current_stack_pointer;
>
> I worry a little that the compiler can move the SP during a function's
> lifetime, but maybe that's only the case when there are VLAs, or something like
> that?
>

I think the AAPCS permits the compiler to allocate the stack space for
outgoing variables (i.e., args 9 and beyond or other argument types
that require passing via the stack) at a smaller scope than the entire
function, although GCC does appear to allocate it at the beginning
(based on some quick experiments)


>> +
>> +     BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +     while (p < boundary) {
>> +             *(unsigned long *)p = STACKLEAK_POISON;
>> +             p += sizeof(unsigned long);
>> +     }
>> +
>> +     /* Reset the lowest_stack value for the next syscall */
>> +     current->thread.lowest_stack = current_stack_pointer;
>> +}
>
> Once this function returns, its data is left on the stack. Is that not a problem?
>
> No strong feelings either way, but it might be worth mentioning in the commit
> message.
>
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>       p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>       p->thread.cpu_context.sp = (unsigned long)childregs;
>>
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +     p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>
> Nit: end_of_stack(p) would be slightly better semantically, even though
> currently equivalent to task_stack_page(p).
>
> [...]
>
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +     unsigned long sp, stack_left;
>> +
>> +     sp = current_stack_pointer;
>> +
>> +     stack_left = sp & (THREAD_SIZE - 1);
>> +     BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
>
> Is this arbitrary, or is there something special about 256?
>
> Even if this is arbitrary, can we give it some mnemonic?
>
>> +EXPORT_SYMBOL(check_alloca);
>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)        += -I$(srctree)/scripts/dtc/libfdt
>>  KBUILD_CFLAGS                        := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>                                  -D__NO_FORTIFY \
>>                                  $(call cc-option,-ffreestanding) \
>> -                                $(call cc-option,-fno-stack-protector)
>> +                                $(call cc-option,-fno-stack-protector) \
>> +                                $(DISABLE_STACKLEAK_PLUGIN)
>>
>>  GCOV_PROFILE                 := n
>>  KASAN_SANITIZE                       := n
>
> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
>
> Thanks,
> Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-02 23:07         ` Laura Abbott
@ 2018-05-03 16:05           ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-03 16:05 UTC (permalink / raw)
  To: Laura Abbott, Kees Cook
  Cc: Mark Rutland, Ard Biesheuvel, Kernel Hardening, linux-arm-kernel, LKML

Hello Laura and Kees,

On 03.05.2018 02:07, Laura Abbott wrote:
> On 05/02/2018 02:31 PM, Kees Cook wrote:
>> On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>>>
>>> Implementation of stackleak based heavily on the x86 version
>>
>> Awesome! Notes below for both you and Alexander, since I think we can
>> create a common code base instead of having near-duplicates in the
>> arch/ trees...

Yes, sure.

I will extract the common part and send v12 for x86. Then Laura will be able to
add arm64 support in a separate patch series. Is it fine?

>>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>>> ---
>>> Now written in C instead of a bunch of assembly.
>>> ---
>>>   arch/arm64/Kconfig                    |  1 +
>>>   arch/arm64/include/asm/processor.h    |  6 ++++
>>>   arch/arm64/kernel/Makefile            |  3 ++
>>>   arch/arm64/kernel/entry.S             |  6 ++++
>>>   arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
>>>   arch/arm64/kernel/process.c           | 16 ++++++++++
>>>   drivers/firmware/efi/libstub/Makefile |  3 +-
>>>   scripts/Makefile.gcc-plugins          |  5 +++-
>>>   8 files changed, 93 insertions(+), 2 deletions(-)
>>>   create mode 100644 arch/arm64/kernel/erase.c
>>>
>>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>>> index eb2cf4938f6d..b0221db95dc9 100644
>>> --- a/arch/arm64/Kconfig
>>> +++ b/arch/arm64/Kconfig
>>> @@ -92,6 +92,7 @@ config ARM64
>>>          select HAVE_ARCH_MMAP_RND_BITS
>>>          select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
>>>          select HAVE_ARCH_SECCOMP_FILTER
>>> +       select HAVE_ARCH_STACKLEAK
>>>          select HAVE_ARCH_THREAD_STRUCT_WHITELIST
>>>          select HAVE_ARCH_TRACEHOOK
>>>          select HAVE_ARCH_TRANSPARENT_HUGEPAGE
>>> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
>>> index 767598932549..d31ab80ff647 100644
>>> --- a/arch/arm64/include/asm/processor.h
>>> +++ b/arch/arm64/include/asm/processor.h
>>> @@ -124,6 +124,12 @@ struct thread_struct {
>>>          unsigned long           fault_address;  /* fault info */
>>>          unsigned long           fault_code;     /* ESR_EL1 value */
>>>          struct debug_info       debug;          /* debugging */
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +       unsigned long           lowest_stack;
>>> +#ifdef CONFIG_STACKLEAK_METRICS
>>> +       unsigned long           prev_lowest_stack;
>>> +#endif
>>> +#endif
>>
>> I wonder if x86 and arm64 could include a common struct here that was
>> empty when the plugin is disabled... it would keep the ifdefs in one
>> place. Maybe include/linux/stackleak.h could be:
>>
>> ---start---
>> /* Poison value points to the unused hole in the virtual memory map */
>> #define STACKLEAK_POISON -0xBEEF
>> #define STACKLEAK_POISON_CHECK_DEPTH 128
>>
>> struct stackleak {
>> #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>         unsigned long           lowest;
>> #ifdef CONFIG_STACKLEAK_METRICS
>>         unsigned long           prev_lowest;
>> #endif
>> #endif
>> };
>>
> 
> Is this well defined across all compilers if the plugin is off?
> This seems to compile with gcc at least but 0 sized structs
> make me a little uneasy.

Empty struct is not defined by C standard but is permitted by gcc
https://gcc.gnu.org/onlinedocs/gcc/Empty-Structures.html#Empty-Structures

Fast example:

#include <stdio.h>

int main(void)
{
	struct a {};

	printf("size %zu\n", sizeof(struct a));

	return 0;
}

# gcc -pedantic t.c -o t
t.c: In function ‘main’:
t.c:5:9: warning: struct has no members [-Wpedantic]
  struct a {};
         ^

# clang -Weverything t.c -o tc
t.c:5:2: warning: empty struct has size 0 in C, size 1 in C++ [-Wc++-compat]
        struct a {};
        ^
t.c:5:2: warning: empty struct is a GNU extension [-Wgnu-empty-struct]
2 warnings generated.


But both programs print "size 0". There are a lot of empty structs around the
kernel, so I'll create another one.

>> asmlinkage void erase_kstack(void);
>> ---eof---
>>
>> and arch/*/include/asm/processor.h could do:
>>
>> @@ -124,6 +124,12 @@ struct thread_struct {
>>          unsigned long           fault_address;  /* fault info */
>>          unsigned long           fault_code;     /* ESR_EL1 value */
>>          struct debug_info       debug;          /* debugging */
>> +       struct stackleak         stackleak;
>>
>> and arch/x86/entry/erase.c could move to maybe kernel/stackleak.c?
>> (Oh, I notice this needs an SPDX line too.)

Thanks, I'll add it.

>>>   static inline void arch_thread_struct_whitelist(unsigned long *offset,
>>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>>> index bf825f38d206..0ceea613c65b 100644
>>> --- a/arch/arm64/kernel/Makefile
>>> +++ b/arch/arm64/kernel/Makefile
>>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>>   arm64-obj-$(CONFIG_CRASH_DUMP)         += crash_dump.o
>>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)  += sdei.o
>>>
>>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>>> +KASAN_SANITIZE_erase.o := n
>>> +
>>>   obj-y                                  += $(arm64-obj-y) vdso/ probes/
>>>   obj-m                                  += $(arm64-obj-m)
>>>   head-y                                 := head.o
>>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>>> index ec2ee720e33e..3144f1ebdc18 100644
>>> --- a/arch/arm64/kernel/entry.S
>>> +++ b/arch/arm64/kernel/entry.S
>>> @@ -401,6 +401,11 @@ tsk        .req    x28             // current thread_info
>>>
>>>          .text
>>>
>>> +       .macro  ERASE_KSTACK
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +       bl      erase_kstack
>>> +#endif
>>> +       .endm
>>>   /*
>>>    * Exception vectors.
>>>    */
>>> @@ -906,6 +911,7 @@ ret_to_user:
>>>          cbnz    x2, work_pending
>>>   finish_ret_to_user:
>>>          enable_step_tsk x1, x2
>>> +       ERASE_KSTACK
>>>          kernel_exit 0
>>>   ENDPROC(ret_to_user)
>>
>> Nice. All of the return paths end up here (I went looking for
>> ret_from_fork's path). :)
>>
>>>
>>> diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
>>> new file mode 100644
>>> index 000000000000..b8b5648d893b
>>> --- /dev/null
>>> +++ b/arch/arm64/kernel/erase.c
>>> @@ -0,0 +1,55 @@
>>> +#include <linux/bug.h>
>>> +#include <linux/sched.h>
>>> +#include <asm/current.h>
>>> +#include <asm/linkage.h>
>>> +#include <asm/processor.h>
>>> +
>>> +asmlinkage void erase_kstack(void)
>>> +{
>>> +       unsigned long p = current->thread.lowest_stack;
>>> +       unsigned long boundary = p & ~(THREAD_SIZE - 1);
>>> +       unsigned long poison = 0;
>>> +       const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>>> +                                                       sizeof(unsigned long);
>>> +
>>> +       /*
>>> +        * Let's search for the poison value in the stack.
>>> +        * Start from the lowest_stack and go to the bottom.
>>> +        */
>>> +       while (p > boundary && poison <= check_depth) {
>>> +               if (*(unsigned long *)p == STACKLEAK_POISON)
>>> +                       poison++;
>>> +               else
>>> +                       poison = 0;
>>> +
>>> +               p -= sizeof(unsigned long);
>>> +       }
>>> +
>>> +       /*
>>> +        * One long int at the bottom of the thread stack is reserved and
>>> +        * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>>> +        */
>>> +       if (p == boundary)
>>> +               p += sizeof(unsigned long);
>>> +
>>> +#ifdef CONFIG_STACKLEAK_METRICS
>>> +       current->thread.prev_lowest_stack = p;
>>> +#endif
>>> +
>>> +       /*
>>> +        * So let's write the poison value to the kernel stack.
>>> +        * Start from the address in p and move up till the new boundary.
>>> +        */
>>> +       boundary = current_stack_pointer;
>>
>> This is the only difference between x86 and arm64 in this code. What
>> do you think about implementing on_thread_stack() to match x86:
>>
>>          if (on_thread_stack())
>>                  boundary = current_stack_pointer;
>>          else
>>                  boundary = current_top_of_stack();
>>
>> then we could make this common code too instead of having two copies in arch/?
>>
> 
> The issue isn't on_thread_stack, it's current_top_of_stack which isn't
> defined on arm64. I agree it would be good if the code would be common
> but I'm not sure how much we want to start trying to force APIs.
> 
>>> +       BUG_ON(boundary - p >= THREAD_SIZE);
>>> +
>>> +       while (p < boundary) {
>>> +               *(unsigned long *)p = STACKLEAK_POISON;
>>> +               p += sizeof(unsigned long);
>>> +       }
>>> +
>>> +       /* Reset the lowest_stack value for the next syscall */
>>> +       current->thread.lowest_stack = current_stack_pointer;
>>> +}
>>> +
>>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>>> index f08a2ed9db0d..156fa0a0da19 100644
>>> --- a/arch/arm64/kernel/process.c
>>> +++ b/arch/arm64/kernel/process.c
>>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>>          p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>>          p->thread.cpu_context.sp = (unsigned long)childregs;
>>>
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +       p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>>> +#endif

I think it should be (unsigned long)task_stack_page(p) + sizeof(unsigned long).

>>>          ptrace_hw_copy_thread(p);
>>>
>>>          return 0;
>>> @@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
>>>   {
>>>          current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
>>>   }
>>> +
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +void __used check_alloca(unsigned long size)
>>> +{
>>> +       unsigned long sp, stack_left;
>>> +
>>> +       sp = current_stack_pointer;
>>> +
>>> +       stack_left = sp & (THREAD_SIZE - 1);
>>> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>> +}
>>> +EXPORT_SYMBOL(check_alloca);
>>
>> This is pretty different from x86. Is this just an artifact of ORC, or
>> something else?
>>
> 
> This was based on the earlier version of x86. I'll confess to
> not seeing how the current x86 version ended up with get_stack_info
> but I suspect it's either related to ORC unwinding or it's best
> practice.

I've changed that in v4. Quote from the changelog:
  - Fixed the surplus and erroneous code for calculating stack_left in
     check_alloca() on x86_64. That code repeats the work which is already
     done in get_stack_info() and it misses the fact that different
     exception stacks on x86_64 have different size.

http://www.openwall.com/lists/kernel-hardening/2017/10/04/68

We can see that in arch/x86/kernel/dumpstack_64.c.

Is it fine if check_alloca() would be arch-specific?

>>> +#endif
>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>> index a34e9290a699..25dd2a14560d 100644
>>> --- a/drivers/firmware/efi/libstub/Makefile
>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)  += -I$(srctree)/scripts/dtc/libfdt
>>>   KBUILD_CFLAGS                  := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>                                     -D__NO_FORTIFY \
>>>                                     $(call cc-option,-ffreestanding) \
>>> -                                  $(call cc-option,-fno-stack-protector)
>>> +                                  $(call cc-option,-fno-stack-protector) \
>>> +                                  $(DISABLE_STACKLEAK_PLUGIN)
>>>
>>>   GCOV_PROFILE                   := n
>>>   KASAN_SANITIZE                 := n
>>> diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
>>> index 8d6070fc538f..6cc0e35d3324 100644
>>> --- a/scripts/Makefile.gcc-plugins
>>> +++ b/scripts/Makefile.gcc-plugins
>>> @@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
>>>
>>>     gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)    += stackleak_plugin.so
>>>     gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)     += -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
>>> +  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +    DISABLE_STACKLEAK_PLUGIN           += -fplugin-arg-stackleak_plugin-disable
>>> +  endif
>>>
>>>     GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
>>>
>>>     export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
>>> -  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
>>> +  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
>>>
>>>     ifneq ($(PLUGINCC),)
>>>       # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
>>> --
>>> 2.14.3
>>>

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-03 16:05           ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-03 16:05 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Laura and Kees,

On 03.05.2018 02:07, Laura Abbott wrote:
> On 05/02/2018 02:31 PM, Kees Cook wrote:
>> On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>>>
>>> Implementation of stackleak based heavily on the x86 version
>>
>> Awesome! Notes below for both you and Alexander, since I think we can
>> create a common code base instead of having near-duplicates in the
>> arch/ trees...

Yes, sure.

I will extract the common part and send v12 for x86. Then Laura will be able to
add arm64 support in a separate patch series. Is it fine?

>>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>>> ---
>>> Now written in C instead of a bunch of assembly.
>>> ---
>>>   arch/arm64/Kconfig                    |  1 +
>>>   arch/arm64/include/asm/processor.h    |  6 ++++
>>>   arch/arm64/kernel/Makefile            |  3 ++
>>>   arch/arm64/kernel/entry.S             |  6 ++++
>>>   arch/arm64/kernel/erase.c             | 55 +++++++++++++++++++++++++++++++++++
>>>   arch/arm64/kernel/process.c           | 16 ++++++++++
>>>   drivers/firmware/efi/libstub/Makefile |  3 +-
>>>   scripts/Makefile.gcc-plugins          |  5 +++-
>>>   8 files changed, 93 insertions(+), 2 deletions(-)
>>>   create mode 100644 arch/arm64/kernel/erase.c
>>>
>>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>>> index eb2cf4938f6d..b0221db95dc9 100644
>>> --- a/arch/arm64/Kconfig
>>> +++ b/arch/arm64/Kconfig
>>> @@ -92,6 +92,7 @@ config ARM64
>>>          select HAVE_ARCH_MMAP_RND_BITS
>>>          select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
>>>          select HAVE_ARCH_SECCOMP_FILTER
>>> +       select HAVE_ARCH_STACKLEAK
>>>          select HAVE_ARCH_THREAD_STRUCT_WHITELIST
>>>          select HAVE_ARCH_TRACEHOOK
>>>          select HAVE_ARCH_TRANSPARENT_HUGEPAGE
>>> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
>>> index 767598932549..d31ab80ff647 100644
>>> --- a/arch/arm64/include/asm/processor.h
>>> +++ b/arch/arm64/include/asm/processor.h
>>> @@ -124,6 +124,12 @@ struct thread_struct {
>>>          unsigned long           fault_address;  /* fault info */
>>>          unsigned long           fault_code;     /* ESR_EL1 value */
>>>          struct debug_info       debug;          /* debugging */
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +       unsigned long           lowest_stack;
>>> +#ifdef CONFIG_STACKLEAK_METRICS
>>> +       unsigned long           prev_lowest_stack;
>>> +#endif
>>> +#endif
>>
>> I wonder if x86 and arm64 could include a common struct here that was
>> empty when the plugin is disabled... it would keep the ifdefs in one
>> place. Maybe include/linux/stackleak.h could be:
>>
>> ---start---
>> /* Poison value points to the unused hole in the virtual memory map */
>> #define STACKLEAK_POISON -0xBEEF
>> #define STACKLEAK_POISON_CHECK_DEPTH 128
>>
>> struct stackleak {
>> #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>         unsigned long           lowest;
>> #ifdef CONFIG_STACKLEAK_METRICS
>>         unsigned long           prev_lowest;
>> #endif
>> #endif
>> };
>>
> 
> Is this well defined across all compilers if the plugin is off?
> This seems to compile with gcc at least but 0 sized structs
> make me a little uneasy.

Empty struct is not defined by C standard but is permitted by gcc
https://gcc.gnu.org/onlinedocs/gcc/Empty-Structures.html#Empty-Structures

Fast example:

#include <stdio.h>

int main(void)
{
	struct a {};

	printf("size %zu\n", sizeof(struct a));

	return 0;
}

# gcc -pedantic t.c -o t
t.c: In function ?main?:
t.c:5:9: warning: struct has no members [-Wpedantic]
  struct a {};
         ^

# clang -Weverything t.c -o tc
t.c:5:2: warning: empty struct has size 0 in C, size 1 in C++ [-Wc++-compat]
        struct a {};
        ^
t.c:5:2: warning: empty struct is a GNU extension [-Wgnu-empty-struct]
2 warnings generated.


But both programs print "size 0". There are a lot of empty structs around the
kernel, so I'll create another one.

>> asmlinkage void erase_kstack(void);
>> ---eof---
>>
>> and arch/*/include/asm/processor.h could do:
>>
>> @@ -124,6 +124,12 @@ struct thread_struct {
>>          unsigned long           fault_address;  /* fault info */
>>          unsigned long           fault_code;     /* ESR_EL1 value */
>>          struct debug_info       debug;          /* debugging */
>> +       struct stackleak         stackleak;
>>
>> and arch/x86/entry/erase.c could move to maybe kernel/stackleak.c?
>> (Oh, I notice this needs an SPDX line too.)

Thanks, I'll add it.

>>>   static inline void arch_thread_struct_whitelist(unsigned long *offset,
>>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>>> index bf825f38d206..0ceea613c65b 100644
>>> --- a/arch/arm64/kernel/Makefile
>>> +++ b/arch/arm64/kernel/Makefile
>>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>>   arm64-obj-$(CONFIG_CRASH_DUMP)         += crash_dump.o
>>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)  += sdei.o
>>>
>>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>>> +KASAN_SANITIZE_erase.o := n
>>> +
>>>   obj-y                                  += $(arm64-obj-y) vdso/ probes/
>>>   obj-m                                  += $(arm64-obj-m)
>>>   head-y                                 := head.o
>>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>>> index ec2ee720e33e..3144f1ebdc18 100644
>>> --- a/arch/arm64/kernel/entry.S
>>> +++ b/arch/arm64/kernel/entry.S
>>> @@ -401,6 +401,11 @@ tsk        .req    x28             // current thread_info
>>>
>>>          .text
>>>
>>> +       .macro  ERASE_KSTACK
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +       bl      erase_kstack
>>> +#endif
>>> +       .endm
>>>   /*
>>>    * Exception vectors.
>>>    */
>>> @@ -906,6 +911,7 @@ ret_to_user:
>>>          cbnz    x2, work_pending
>>>   finish_ret_to_user:
>>>          enable_step_tsk x1, x2
>>> +       ERASE_KSTACK
>>>          kernel_exit 0
>>>   ENDPROC(ret_to_user)
>>
>> Nice. All of the return paths end up here (I went looking for
>> ret_from_fork's path). :)
>>
>>>
>>> diff --git a/arch/arm64/kernel/erase.c b/arch/arm64/kernel/erase.c
>>> new file mode 100644
>>> index 000000000000..b8b5648d893b
>>> --- /dev/null
>>> +++ b/arch/arm64/kernel/erase.c
>>> @@ -0,0 +1,55 @@
>>> +#include <linux/bug.h>
>>> +#include <linux/sched.h>
>>> +#include <asm/current.h>
>>> +#include <asm/linkage.h>
>>> +#include <asm/processor.h>
>>> +
>>> +asmlinkage void erase_kstack(void)
>>> +{
>>> +       unsigned long p = current->thread.lowest_stack;
>>> +       unsigned long boundary = p & ~(THREAD_SIZE - 1);
>>> +       unsigned long poison = 0;
>>> +       const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>>> +                                                       sizeof(unsigned long);
>>> +
>>> +       /*
>>> +        * Let's search for the poison value in the stack.
>>> +        * Start from the lowest_stack and go to the bottom.
>>> +        */
>>> +       while (p > boundary && poison <= check_depth) {
>>> +               if (*(unsigned long *)p == STACKLEAK_POISON)
>>> +                       poison++;
>>> +               else
>>> +                       poison = 0;
>>> +
>>> +               p -= sizeof(unsigned long);
>>> +       }
>>> +
>>> +       /*
>>> +        * One long int at the bottom of the thread stack is reserved and
>>> +        * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>>> +        */
>>> +       if (p == boundary)
>>> +               p += sizeof(unsigned long);
>>> +
>>> +#ifdef CONFIG_STACKLEAK_METRICS
>>> +       current->thread.prev_lowest_stack = p;
>>> +#endif
>>> +
>>> +       /*
>>> +        * So let's write the poison value to the kernel stack.
>>> +        * Start from the address in p and move up till the new boundary.
>>> +        */
>>> +       boundary = current_stack_pointer;
>>
>> This is the only difference between x86 and arm64 in this code. What
>> do you think about implementing on_thread_stack() to match x86:
>>
>>          if (on_thread_stack())
>>                  boundary = current_stack_pointer;
>>          else
>>                  boundary = current_top_of_stack();
>>
>> then we could make this common code too instead of having two copies in arch/?
>>
> 
> The issue isn't on_thread_stack, it's current_top_of_stack which isn't
> defined on arm64. I agree it would be good if the code would be common
> but I'm not sure how much we want to start trying to force APIs.
> 
>>> +       BUG_ON(boundary - p >= THREAD_SIZE);
>>> +
>>> +       while (p < boundary) {
>>> +               *(unsigned long *)p = STACKLEAK_POISON;
>>> +               p += sizeof(unsigned long);
>>> +       }
>>> +
>>> +       /* Reset the lowest_stack value for the next syscall */
>>> +       current->thread.lowest_stack = current_stack_pointer;
>>> +}
>>> +
>>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>>> index f08a2ed9db0d..156fa0a0da19 100644
>>> --- a/arch/arm64/kernel/process.c
>>> +++ b/arch/arm64/kernel/process.c
>>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>>          p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>>          p->thread.cpu_context.sp = (unsigned long)childregs;
>>>
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +       p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>>> +#endif

I think it should be (unsigned long)task_stack_page(p) + sizeof(unsigned long).

>>>          ptrace_hw_copy_thread(p);
>>>
>>>          return 0;
>>> @@ -493,3 +496,16 @@ void arch_setup_new_exec(void)
>>>   {
>>>          current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
>>>   }
>>> +
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +void __used check_alloca(unsigned long size)
>>> +{
>>> +       unsigned long sp, stack_left;
>>> +
>>> +       sp = current_stack_pointer;
>>> +
>>> +       stack_left = sp & (THREAD_SIZE - 1);
>>> +       BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>> +}
>>> +EXPORT_SYMBOL(check_alloca);
>>
>> This is pretty different from x86. Is this just an artifact of ORC, or
>> something else?
>>
> 
> This was based on the earlier version of x86. I'll confess to
> not seeing how the current x86 version ended up with get_stack_info
> but I suspect it's either related to ORC unwinding or it's best
> practice.

I've changed that in v4. Quote from the changelog:
  - Fixed the surplus and erroneous code for calculating stack_left in
     check_alloca() on x86_64. That code repeats the work which is already
     done in get_stack_info() and it misses the fact that different
     exception stacks on x86_64 have different size.

http://www.openwall.com/lists/kernel-hardening/2017/10/04/68

We can see that in arch/x86/kernel/dumpstack_64.c.

Is it fine if check_alloca() would be arch-specific?

>>> +#endif
>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>> index a34e9290a699..25dd2a14560d 100644
>>> --- a/drivers/firmware/efi/libstub/Makefile
>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)  += -I$(srctree)/scripts/dtc/libfdt
>>>   KBUILD_CFLAGS                  := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>                                     -D__NO_FORTIFY \
>>>                                     $(call cc-option,-ffreestanding) \
>>> -                                  $(call cc-option,-fno-stack-protector)
>>> +                                  $(call cc-option,-fno-stack-protector) \
>>> +                                  $(DISABLE_STACKLEAK_PLUGIN)
>>>
>>>   GCOV_PROFILE                   := n
>>>   KASAN_SANITIZE                 := n
>>> diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
>>> index 8d6070fc538f..6cc0e35d3324 100644
>>> --- a/scripts/Makefile.gcc-plugins
>>> +++ b/scripts/Makefile.gcc-plugins
>>> @@ -37,11 +37,14 @@ ifdef CONFIG_GCC_PLUGINS
>>>
>>>     gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK)    += stackleak_plugin.so
>>>     gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK)     += -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
>>> +  ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +    DISABLE_STACKLEAK_PLUGIN           += -fplugin-arg-stackleak_plugin-disable
>>> +  endif
>>>
>>>     GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
>>>
>>>     export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
>>> -  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
>>> +  export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
>>>
>>>     ifneq ($(PLUGINCC),)
>>>       # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
>>> --
>>> 2.14.3
>>>

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03 16:05           ` Alexander Popov
@ 2018-05-03 16:45             ` Kees Cook
  -1 siblings, 0 replies; 70+ messages in thread
From: Kees Cook @ 2018-05-03 16:45 UTC (permalink / raw)
  To: Alexander Popov
  Cc: Laura Abbott, Mark Rutland, Ard Biesheuvel, Kernel Hardening,
	linux-arm-kernel, LKML

On Thu, May 3, 2018 at 9:05 AM, Alexander Popov <alex.popov@linux.com> wrote:
> Hello Laura and Kees,
>
> On 03.05.2018 02:07, Laura Abbott wrote:
>> On 05/02/2018 02:31 PM, Kees Cook wrote:
>>> On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>>>>
>>>> Implementation of stackleak based heavily on the x86 version
>>>
>>> Awesome! Notes below for both you and Alexander, since I think we can
>>> create a common code base instead of having near-duplicates in the
>>> arch/ trees...
>
> Yes, sure.
>
> I will extract the common part and send v12 for x86. Then Laura will be able to
> add arm64 support in a separate patch series. Is it fine?

Sure, though if you could fold the plugin fix from her, that would be
ideal. I'll likely carry both patch sets together once the arm64 one
stabilizes.

>> This was based on the earlier version of x86. I'll confess to
>> not seeing how the current x86 version ended up with get_stack_info
>> but I suspect it's either related to ORC unwinding or it's best
>> practice.
>
> I've changed that in v4. Quote from the changelog:
>   - Fixed the surplus and erroneous code for calculating stack_left in
>      check_alloca() on x86_64. That code repeats the work which is already
>      done in get_stack_info() and it misses the fact that different
>      exception stacks on x86_64 have different size.
>
> http://www.openwall.com/lists/kernel-hardening/2017/10/04/68
>
> We can see that in arch/x86/kernel/dumpstack_64.c.
>
> Is it fine if check_alloca() would be arch-specific?

I'm fine if check_alloca() remains arch-specific.

Thanks!

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-03 16:45             ` Kees Cook
  0 siblings, 0 replies; 70+ messages in thread
From: Kees Cook @ 2018-05-03 16:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, May 3, 2018 at 9:05 AM, Alexander Popov <alex.popov@linux.com> wrote:
> Hello Laura and Kees,
>
> On 03.05.2018 02:07, Laura Abbott wrote:
>> On 05/02/2018 02:31 PM, Kees Cook wrote:
>>> On Wed, May 2, 2018 at 1:33 PM, Laura Abbott <labbott@redhat.com> wrote:
>>>>
>>>> Implementation of stackleak based heavily on the x86 version
>>>
>>> Awesome! Notes below for both you and Alexander, since I think we can
>>> create a common code base instead of having near-duplicates in the
>>> arch/ trees...
>
> Yes, sure.
>
> I will extract the common part and send v12 for x86. Then Laura will be able to
> add arm64 support in a separate patch series. Is it fine?

Sure, though if you could fold the plugin fix from her, that would be
ideal. I'll likely carry both patch sets together once the arm64 one
stabilizes.

>> This was based on the earlier version of x86. I'll confess to
>> not seeing how the current x86 version ended up with get_stack_info
>> but I suspect it's either related to ORC unwinding or it's best
>> practice.
>
> I've changed that in v4. Quote from the changelog:
>   - Fixed the surplus and erroneous code for calculating stack_left in
>      check_alloca() on x86_64. That code repeats the work which is already
>      done in get_stack_info() and it misses the fact that different
>      exception stacks on x86_64 have different size.
>
> http://www.openwall.com/lists/kernel-hardening/2017/10/04/68
>
> We can see that in arch/x86/kernel/dumpstack_64.c.
>
> Is it fine if check_alloca() would be arch-specific?

I'm fine if check_alloca() remains arch-specific.

Thanks!

-Kees

-- 
Kees Cook
Pixel Security

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03  7:19       ` Mark Rutland
@ 2018-05-03 17:33         ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-03 17:33 UTC (permalink / raw)
  To: Mark Rutland, Laura Abbott
  Cc: Kees Cook, Ard Biesheuvel, kernel-hardening, linux-arm-kernel,
	linux-kernel

Hello Mark and Laura,

Let me join the discussion. Mark, thanks for your feedback!

On 03.05.2018 10:19, Mark Rutland wrote:
> Hi Laura,
> 
> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
> 
> This looks neat!
> 
> I have a few minor comments below.
> 
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>  
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o	:= n
> 
> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> GKOV, KASAN, UBSAN, and KCOV.

I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
intentionally writes to the stack and causes KASAN false positive reports.

But I didn't see any conflicts with other types of instrumentation that you
mentioned.

>> +
>>  obj-y					+= $(arm64-obj-y) vdso/ probes/
>>  obj-m					+= $(arm64-obj-m)
>>  head-y					:= head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>>  
>>  	.text
>>  
>> +	.macro	ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	bl	erase_kstack
>> +#endif
>> +	.endm
> 
> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
> 
>>  /*
>>   * Exception vectors.
>>   */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>  	cbnz	x2, work_pending
>>  finish_ret_to_user:
>>  	enable_step_tsk x1, x2
>> +	ERASE_KSTACK
>>  	kernel_exit 0
>>  ENDPROC(ret_to_user)
> 
> I believe we also need this in ret_fast_syscall.
> 
> [...]
> 
>> +asmlinkage void erase_kstack(void)
>> +{
>> +	unsigned long p = current->thread.lowest_stack;
>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +	unsigned long poison = 0;
>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +							sizeof(unsigned long);
>> +
>> +	/*
>> +	 * Let's search for the poison value in the stack.
>> +	 * Start from the lowest_stack and go to the bottom.
>> +	 */
>> +	while (p > boundary && poison <= check_depth) {
>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>> +			poison++;
>> +		else
>> +			poison = 0;
>> +
>> +		p -= sizeof(unsigned long);
>> +	}
>> +
>> +	/*
>> +	 * One long int at the bottom of the thread stack is reserved and
>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +	 */
>> +	if (p == boundary)
>> +		p += sizeof(unsigned long);
> 
> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> given that's supposed to return the last *usable* long on the stack, and we
> don't account for this elsewhere.

I would be afraid to change the meaning of end_of_stack()... Currently it
considers that magic long as usable (include/linux/sched/task_stack.h):

#define task_stack_end_corrupted(task) \
		(*(end_of_stack(task)) != STACK_END_MAGIC)


> If we did, then IIUC we could do:
> 
> 	unsigned long boundary = (unsigned long)end_of_stack(current);
> 
> ... at the start of the function, and not have to worry about this explicitly.

I should mention that erase_kstack() can be called from x86 trampoline stack.
That's why the boundary is calculated from the lowest_stack.

>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +	current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +	/*
>> +	 * So let's write the poison value to the kernel stack.
>> +	 * Start from the address in p and move up till the new boundary.
>> +	 */
>> +	boundary = current_stack_pointer;
> 
> I worry a little that the compiler can move the SP during a function's
> lifetime, but maybe that's only the case when there are VLAs, or something like
> that?

Oh, I don't know.

However, erase_kstack() doesn't call anything except simple inline functions.
And as I see from its disasm on x86, the local variables reside in registers.

>> +
>> +	BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +	while (p < boundary) {
>> +		*(unsigned long *)p = STACKLEAK_POISON;
>> +		p += sizeof(unsigned long);
>> +	}
>> +
>> +	/* Reset the lowest_stack value for the next syscall */
>> +	current->thread.lowest_stack = current_stack_pointer;

Laura, that might be wrong and introduce huge performance impact.

I think, lowest_stack should be reset similarly to the original version.

>> +}
> 
> Once this function returns, its data is left on the stack. Is that not a problem?
> 
> No strong feelings either way, but it might be worth mentioning in the commit
> message.

I managed to bypass that with "register" specifier. Although it doesn't give an
absolute guarantee.

>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>  	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>  	p->thread.cpu_context.sp = (unsigned long)childregs;
>>  
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
> 
> Nit: end_of_stack(p) would be slightly better semantically, even though
> currently equivalent to task_stack_page(p).

Thanks, I agree, I'll fix it in v12.

> [...]
> 
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +	unsigned long sp, stack_left;
>> +
>> +	sp = current_stack_pointer;
>> +
>> +	stack_left = sp & (THREAD_SIZE - 1);
>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
> 
> Is this arbitrary, or is there something special about 256?
> 
> Even if this is arbitrary, can we give it some mnemonic?

It's just a reasonable number. We can introduce a macro for it.

>> +EXPORT_SYMBOL(check_alloca);
>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>  				   -D__NO_FORTIFY \
>>  				   $(call cc-option,-ffreestanding) \
>> -				   $(call cc-option,-fno-stack-protector)
>> +				   $(call cc-option,-fno-stack-protector) \
>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>  
>>  GCOV_PROFILE			:= n
>>  KASAN_SANITIZE			:= n
> 
> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.

Could you please give more details on that? Why STACKLEAK breaks it?

Thanks a lot!

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-03 17:33         ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-03 17:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark and Laura,

Let me join the discussion. Mark, thanks for your feedback!

On 03.05.2018 10:19, Mark Rutland wrote:
> Hi Laura,
> 
> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
> 
> This looks neat!
> 
> I have a few minor comments below.
> 
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>  
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o	:= n
> 
> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> GKOV, KASAN, UBSAN, and KCOV.

I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
intentionally writes to the stack and causes KASAN false positive reports.

But I didn't see any conflicts with other types of instrumentation that you
mentioned.

>> +
>>  obj-y					+= $(arm64-obj-y) vdso/ probes/
>>  obj-m					+= $(arm64-obj-m)
>>  head-y					:= head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>>  
>>  	.text
>>  
>> +	.macro	ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	bl	erase_kstack
>> +#endif
>> +	.endm
> 
> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
> 
>>  /*
>>   * Exception vectors.
>>   */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>  	cbnz	x2, work_pending
>>  finish_ret_to_user:
>>  	enable_step_tsk x1, x2
>> +	ERASE_KSTACK
>>  	kernel_exit 0
>>  ENDPROC(ret_to_user)
> 
> I believe we also need this in ret_fast_syscall.
> 
> [...]
> 
>> +asmlinkage void erase_kstack(void)
>> +{
>> +	unsigned long p = current->thread.lowest_stack;
>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +	unsigned long poison = 0;
>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +							sizeof(unsigned long);
>> +
>> +	/*
>> +	 * Let's search for the poison value in the stack.
>> +	 * Start from the lowest_stack and go to the bottom.
>> +	 */
>> +	while (p > boundary && poison <= check_depth) {
>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>> +			poison++;
>> +		else
>> +			poison = 0;
>> +
>> +		p -= sizeof(unsigned long);
>> +	}
>> +
>> +	/*
>> +	 * One long int at the bottom of the thread stack is reserved and
>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +	 */
>> +	if (p == boundary)
>> +		p += sizeof(unsigned long);
> 
> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> given that's supposed to return the last *usable* long on the stack, and we
> don't account for this elsewhere.

I would be afraid to change the meaning of end_of_stack()... Currently it
considers that magic long as usable (include/linux/sched/task_stack.h):

#define task_stack_end_corrupted(task) \
		(*(end_of_stack(task)) != STACK_END_MAGIC)


> If we did, then IIUC we could do:
> 
> 	unsigned long boundary = (unsigned long)end_of_stack(current);
> 
> ... at the start of the function, and not have to worry about this explicitly.

I should mention that erase_kstack() can be called from x86 trampoline stack.
That's why the boundary is calculated from the lowest_stack.

>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +	current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +	/*
>> +	 * So let's write the poison value to the kernel stack.
>> +	 * Start from the address in p and move up till the new boundary.
>> +	 */
>> +	boundary = current_stack_pointer;
> 
> I worry a little that the compiler can move the SP during a function's
> lifetime, but maybe that's only the case when there are VLAs, or something like
> that?

Oh, I don't know.

However, erase_kstack() doesn't call anything except simple inline functions.
And as I see from its disasm on x86, the local variables reside in registers.

>> +
>> +	BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +	while (p < boundary) {
>> +		*(unsigned long *)p = STACKLEAK_POISON;
>> +		p += sizeof(unsigned long);
>> +	}
>> +
>> +	/* Reset the lowest_stack value for the next syscall */
>> +	current->thread.lowest_stack = current_stack_pointer;

Laura, that might be wrong and introduce huge performance impact.

I think, lowest_stack should be reset similarly to the original version.

>> +}
> 
> Once this function returns, its data is left on the stack. Is that not a problem?
> 
> No strong feelings either way, but it might be worth mentioning in the commit
> message.

I managed to bypass that with "register" specifier. Although it doesn't give an
absolute guarantee.

>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>  	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>  	p->thread.cpu_context.sp = (unsigned long)childregs;
>>  
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
> 
> Nit: end_of_stack(p) would be slightly better semantically, even though
> currently equivalent to task_stack_page(p).

Thanks, I agree, I'll fix it in v12.

> [...]
> 
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +	unsigned long sp, stack_left;
>> +
>> +	sp = current_stack_pointer;
>> +
>> +	stack_left = sp & (THREAD_SIZE - 1);
>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
> 
> Is this arbitrary, or is there something special about 256?
> 
> Even if this is arbitrary, can we give it some mnemonic?

It's just a reasonable number. We can introduce a macro for it.

>> +EXPORT_SYMBOL(check_alloca);
>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>  				   -D__NO_FORTIFY \
>>  				   $(call cc-option,-ffreestanding) \
>> -				   $(call cc-option,-fno-stack-protector)
>> +				   $(call cc-option,-fno-stack-protector) \
>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>  
>>  GCOV_PROFILE			:= n
>>  KASAN_SANITIZE			:= n
> 
> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.

Could you please give more details on that? Why STACKLEAK breaks it?

Thanks a lot!

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03  7:19       ` Mark Rutland
@ 2018-05-03 19:00         ` Laura Abbott
  -1 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-03 19:00 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Alexander Popov, Kees Cook, Ard Biesheuvel, kernel-hardening,
	linux-arm-kernel, linux-kernel

On 05/03/2018 12:19 AM, Mark Rutland wrote:
> Hi Laura,
> 
> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
> 
> This looks neat!
> 
> I have a few minor comments below.
> 
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>   arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>   
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o	:= n
> 
> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> GKOV, KASAN, UBSAN, and KCOV.
> 
>> +
>>   obj-y					+= $(arm64-obj-y) vdso/ probes/
>>   obj-m					+= $(arm64-obj-m)
>>   head-y					:= head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>>   
>>   	.text
>>   
>> +	.macro	ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	bl	erase_kstack
>> +#endif
>> +	.endm
> 
> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
> 
>>   /*
>>    * Exception vectors.
>>    */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>   	cbnz	x2, work_pending
>>   finish_ret_to_user:
>>   	enable_step_tsk x1, x2
>> +	ERASE_KSTACK
>>   	kernel_exit 0
>>   ENDPROC(ret_to_user)
> 
> I believe we also need this in ret_fast_syscall.
> 
> [...]
> 

Yeah I had this in previous versions but I managed to out think
myself. I'll add it in with a comment to avoid confusion.

>> +asmlinkage void erase_kstack(void)
>> +{
>> +	unsigned long p = current->thread.lowest_stack;
>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +	unsigned long poison = 0;
>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +							sizeof(unsigned long);
>> +
>> +	/*
>> +	 * Let's search for the poison value in the stack.
>> +	 * Start from the lowest_stack and go to the bottom.
>> +	 */
>> +	while (p > boundary && poison <= check_depth) {
>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>> +			poison++;
>> +		else
>> +			poison = 0;
>> +
>> +		p -= sizeof(unsigned long);
>> +	}
>> +
>> +	/*
>> +	 * One long int at the bottom of the thread stack is reserved and
>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +	 */
>> +	if (p == boundary)
>> +		p += sizeof(unsigned long);
> 
> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> given that's supposed to return the last *usable* long on the stack, and we
> don't account for this elsewhere.
> 
> If we did, then IIUC we could do:
> 
> 	unsigned long boundary = (unsigned long)end_of_stack(current);
> 
> ... at the start of the function, and not have to worry about this explicitly.
> 
>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +	current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +	/*
>> +	 * So let's write the poison value to the kernel stack.
>> +	 * Start from the address in p and move up till the new boundary.
>> +	 */
>> +	boundary = current_stack_pointer;
> 
> I worry a little that the compiler can move the SP during a function's
> lifetime, but maybe that's only the case when there are VLAs, or something like
> that?
> 

I think that's true and a risk we take writing this in C. Here's
the disassembly on gcc-7.3.1:

ffff00000809d4d8 <erase_kstack>:
ffff00000809d4d8:       a9bf7bfd        stp     x29, x30, [sp, #-16]!
ffff00000809d4dc:       d5384100        mrs     x0, sp_el0
ffff00000809d4e0:       910003fd        mov     x29, sp
ffff00000809d4e4:       f946e400        ldr     x0, [x0, #3528]
ffff00000809d4e8:       9272c404        and     x4, x0, #0xffffffffffffc000
ffff00000809d4ec:       eb04001f        cmp     x0, x4
ffff00000809d4f0:       540002c9        b.ls    ffff00000809d548 <erase_kstack+0x70>  // b.plast
ffff00000809d4f4:       d2800003        mov     x3, #0x0                        // #0
ffff00000809d4f8:       9297ddc5        mov     x5, #0xffffffffffff4111         // #-48879
ffff00000809d4fc:       14000008        b       ffff00000809d51c <erase_kstack+0x44>
ffff00000809d500:       d1002000        sub     x0, x0, #0x8
ffff00000809d504:       52800022        mov     w2, #0x1                        // #1
ffff00000809d508:       eb00009f        cmp     x4, x0
ffff00000809d50c:       d2800003        mov     x3, #0x0                        // #0
ffff00000809d510:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
ffff00000809d514:       6a01005f        tst     w2, w1
ffff00000809d518:       54000180        b.eq    ffff00000809d548 <erase_kstack+0x70>  // b.none
ffff00000809d51c:       f9400001        ldr     x1, [x0]
ffff00000809d520:       eb05003f        cmp     x1, x5
ffff00000809d524:       54fffee1        b.ne    ffff00000809d500 <erase_kstack+0x28>  // b.any
ffff00000809d528:       91000463        add     x3, x3, #0x1
ffff00000809d52c:       d1002000        sub     x0, x0, #0x8
ffff00000809d530:       f100407f        cmp     x3, #0x10
ffff00000809d534:       1a9f87e2        cset    w2, ls  // ls = plast
ffff00000809d538:       eb00009f        cmp     x4, x0
ffff00000809d53c:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
ffff00000809d540:       6a01005f        tst     w2, w1
ffff00000809d544:       54fffec1        b.ne    ffff00000809d51c <erase_kstack+0x44>  // b.any
ffff00000809d548:       eb00009f        cmp     x4, x0
ffff00000809d54c:       91002001        add     x1, x0, #0x8
ffff00000809d550:       9a800020        csel    x0, x1, x0, eq  // eq = none
ffff00000809d554:       910003e1        mov     x1, sp
ffff00000809d558:       d5384102        mrs     x2, sp_el0
ffff00000809d55c:       f906e840        str     x0, [x2, #3536]
ffff00000809d560:       cb000023        sub     x3, x1, x0
ffff00000809d564:       d287ffe2        mov     x2, #0x3fff                     // #16383
ffff00000809d568:       eb02007f        cmp     x3, x2
ffff00000809d56c:       540001a8        b.hi    ffff00000809d5a0 <erase_kstack+0xc8>  // b.pmore
ffff00000809d570:       9297ddc2        mov     x2, #0xffffffffffff4111         // #-48879
ffff00000809d574:       eb01001f        cmp     x0, x1
ffff00000809d578:       54000082        b.cs    ffff00000809d588 <erase_kstack+0xb0>  // b.hs, b.nlast
ffff00000809d57c:       f8008402        str     x2, [x0], #8
ffff00000809d580:       eb00003f        cmp     x1, x0
ffff00000809d584:       54ffffc8        b.hi    ffff00000809d57c <erase_kstack+0xa4>  // b.pmore
ffff00000809d588:       910003e1        mov     x1, sp
ffff00000809d58c:       d5384100        mrs     x0, sp_el0
ffff00000809d590:       f906e401        str     x1, [x0, #3528]
ffff00000809d594:       a8c17bfd        ldp     x29, x30, [sp], #16
ffff00000809d598:       d65f03c0        ret
ffff00000809d59c:       d503201f        nop
ffff00000809d5a0:       d4210000        brk     #0x800
ffff00000809d5a4:       00000000        .inst   0x00000000 ; undefined

It looks to be okay although admittedly that's subject to compiler
whims. It might be safer to save the stack pointer almost as soon as
we get into the function and use that?

>> +
>> +	BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +	while (p < boundary) {
>> +		*(unsigned long *)p = STACKLEAK_POISON;
>> +		p += sizeof(unsigned long);
>> +	}
>> +
>> +	/* Reset the lowest_stack value for the next syscall */
>> +	current->thread.lowest_stack = current_stack_pointer;
>> +}
> 
> Once this function returns, its data is left on the stack. Is that not a problem?
> 
> No strong feelings either way, but it might be worth mentioning in the commit
> message.
> 
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>   	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>   	p->thread.cpu_context.sp = (unsigned long)childregs;
>>   
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
> 
> Nit: end_of_stack(p) would be slightly better semantically, even though
> currently equivalent to task_stack_page(p).
> 
> [...]
> 
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +	unsigned long sp, stack_left;
>> +
>> +	sp = current_stack_pointer;
>> +
>> +	stack_left = sp & (THREAD_SIZE - 1);
>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
> 
> Is this arbitrary, or is there something special about 256?
> 
> Even if this is arbitrary, can we give it some mnemonic?
> 
>> +EXPORT_SYMBOL(check_alloca);
>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>   KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>   				   -D__NO_FORTIFY \
>>   				   $(call cc-option,-ffreestanding) \
>> -				   $(call cc-option,-fno-stack-protector)
>> +				   $(call cc-option,-fno-stack-protector) \
>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>   
>>   GCOV_PROFILE			:= n
>>   KASAN_SANITIZE			:= n
> 
> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
> 
> Thanks,
> Mark.
> 

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-03 19:00         ` Laura Abbott
  0 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-03 19:00 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/03/2018 12:19 AM, Mark Rutland wrote:
> Hi Laura,
> 
> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>
>> Implementation of stackleak based heavily on the x86 version
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>> ---
>> Now written in C instead of a bunch of assembly.
> 
> This looks neat!
> 
> I have a few minor comments below.
> 
>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>> index bf825f38d206..0ceea613c65b 100644
>> --- a/arch/arm64/kernel/Makefile
>> +++ b/arch/arm64/kernel/Makefile
>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>   arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>   
>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>> +KASAN_SANITIZE_erase.o	:= n
> 
> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> GKOV, KASAN, UBSAN, and KCOV.
> 
>> +
>>   obj-y					+= $(arm64-obj-y) vdso/ probes/
>>   obj-m					+= $(arm64-obj-m)
>>   head-y					:= head.o
>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>> index ec2ee720e33e..3144f1ebdc18 100644
>> --- a/arch/arm64/kernel/entry.S
>> +++ b/arch/arm64/kernel/entry.S
>> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>>   
>>   	.text
>>   
>> +	.macro	ERASE_KSTACK
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	bl	erase_kstack
>> +#endif
>> +	.endm
> 
> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
> 
>>   /*
>>    * Exception vectors.
>>    */
>> @@ -906,6 +911,7 @@ ret_to_user:
>>   	cbnz	x2, work_pending
>>   finish_ret_to_user:
>>   	enable_step_tsk x1, x2
>> +	ERASE_KSTACK
>>   	kernel_exit 0
>>   ENDPROC(ret_to_user)
> 
> I believe we also need this in ret_fast_syscall.
> 
> [...]
> 

Yeah I had this in previous versions but I managed to out think
myself. I'll add it in with a comment to avoid confusion.

>> +asmlinkage void erase_kstack(void)
>> +{
>> +	unsigned long p = current->thread.lowest_stack;
>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>> +	unsigned long poison = 0;
>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>> +							sizeof(unsigned long);
>> +
>> +	/*
>> +	 * Let's search for the poison value in the stack.
>> +	 * Start from the lowest_stack and go to the bottom.
>> +	 */
>> +	while (p > boundary && poison <= check_depth) {
>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>> +			poison++;
>> +		else
>> +			poison = 0;
>> +
>> +		p -= sizeof(unsigned long);
>> +	}
>> +
>> +	/*
>> +	 * One long int at the bottom of the thread stack is reserved and
>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>> +	 */
>> +	if (p == boundary)
>> +		p += sizeof(unsigned long);
> 
> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> given that's supposed to return the last *usable* long on the stack, and we
> don't account for this elsewhere.
> 
> If we did, then IIUC we could do:
> 
> 	unsigned long boundary = (unsigned long)end_of_stack(current);
> 
> ... at the start of the function, and not have to worry about this explicitly.
> 
>> +
>> +#ifdef CONFIG_STACKLEAK_METRICS
>> +	current->thread.prev_lowest_stack = p;
>> +#endif
>> +
>> +	/*
>> +	 * So let's write the poison value to the kernel stack.
>> +	 * Start from the address in p and move up till the new boundary.
>> +	 */
>> +	boundary = current_stack_pointer;
> 
> I worry a little that the compiler can move the SP during a function's
> lifetime, but maybe that's only the case when there are VLAs, or something like
> that?
> 

I think that's true and a risk we take writing this in C. Here's
the disassembly on gcc-7.3.1:

ffff00000809d4d8 <erase_kstack>:
ffff00000809d4d8:       a9bf7bfd        stp     x29, x30, [sp, #-16]!
ffff00000809d4dc:       d5384100        mrs     x0, sp_el0
ffff00000809d4e0:       910003fd        mov     x29, sp
ffff00000809d4e4:       f946e400        ldr     x0, [x0, #3528]
ffff00000809d4e8:       9272c404        and     x4, x0, #0xffffffffffffc000
ffff00000809d4ec:       eb04001f        cmp     x0, x4
ffff00000809d4f0:       540002c9        b.ls    ffff00000809d548 <erase_kstack+0x70>  // b.plast
ffff00000809d4f4:       d2800003        mov     x3, #0x0                        // #0
ffff00000809d4f8:       9297ddc5        mov     x5, #0xffffffffffff4111         // #-48879
ffff00000809d4fc:       14000008        b       ffff00000809d51c <erase_kstack+0x44>
ffff00000809d500:       d1002000        sub     x0, x0, #0x8
ffff00000809d504:       52800022        mov     w2, #0x1                        // #1
ffff00000809d508:       eb00009f        cmp     x4, x0
ffff00000809d50c:       d2800003        mov     x3, #0x0                        // #0
ffff00000809d510:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
ffff00000809d514:       6a01005f        tst     w2, w1
ffff00000809d518:       54000180        b.eq    ffff00000809d548 <erase_kstack+0x70>  // b.none
ffff00000809d51c:       f9400001        ldr     x1, [x0]
ffff00000809d520:       eb05003f        cmp     x1, x5
ffff00000809d524:       54fffee1        b.ne    ffff00000809d500 <erase_kstack+0x28>  // b.any
ffff00000809d528:       91000463        add     x3, x3, #0x1
ffff00000809d52c:       d1002000        sub     x0, x0, #0x8
ffff00000809d530:       f100407f        cmp     x3, #0x10
ffff00000809d534:       1a9f87e2        cset    w2, ls  // ls = plast
ffff00000809d538:       eb00009f        cmp     x4, x0
ffff00000809d53c:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
ffff00000809d540:       6a01005f        tst     w2, w1
ffff00000809d544:       54fffec1        b.ne    ffff00000809d51c <erase_kstack+0x44>  // b.any
ffff00000809d548:       eb00009f        cmp     x4, x0
ffff00000809d54c:       91002001        add     x1, x0, #0x8
ffff00000809d550:       9a800020        csel    x0, x1, x0, eq  // eq = none
ffff00000809d554:       910003e1        mov     x1, sp
ffff00000809d558:       d5384102        mrs     x2, sp_el0
ffff00000809d55c:       f906e840        str     x0, [x2, #3536]
ffff00000809d560:       cb000023        sub     x3, x1, x0
ffff00000809d564:       d287ffe2        mov     x2, #0x3fff                     // #16383
ffff00000809d568:       eb02007f        cmp     x3, x2
ffff00000809d56c:       540001a8        b.hi    ffff00000809d5a0 <erase_kstack+0xc8>  // b.pmore
ffff00000809d570:       9297ddc2        mov     x2, #0xffffffffffff4111         // #-48879
ffff00000809d574:       eb01001f        cmp     x0, x1
ffff00000809d578:       54000082        b.cs    ffff00000809d588 <erase_kstack+0xb0>  // b.hs, b.nlast
ffff00000809d57c:       f8008402        str     x2, [x0], #8
ffff00000809d580:       eb00003f        cmp     x1, x0
ffff00000809d584:       54ffffc8        b.hi    ffff00000809d57c <erase_kstack+0xa4>  // b.pmore
ffff00000809d588:       910003e1        mov     x1, sp
ffff00000809d58c:       d5384100        mrs     x0, sp_el0
ffff00000809d590:       f906e401        str     x1, [x0, #3528]
ffff00000809d594:       a8c17bfd        ldp     x29, x30, [sp], #16
ffff00000809d598:       d65f03c0        ret
ffff00000809d59c:       d503201f        nop
ffff00000809d5a0:       d4210000        brk     #0x800
ffff00000809d5a4:       00000000        .inst   0x00000000 ; undefined

It looks to be okay although admittedly that's subject to compiler
whims. It might be safer to save the stack pointer almost as soon as
we get into the function and use that?

>> +
>> +	BUG_ON(boundary - p >= THREAD_SIZE);
>> +
>> +	while (p < boundary) {
>> +		*(unsigned long *)p = STACKLEAK_POISON;
>> +		p += sizeof(unsigned long);
>> +	}
>> +
>> +	/* Reset the lowest_stack value for the next syscall */
>> +	current->thread.lowest_stack = current_stack_pointer;
>> +}
> 
> Once this function returns, its data is left on the stack. Is that not a problem?
> 
> No strong feelings either way, but it might be worth mentioning in the commit
> message.
> 
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index f08a2ed9db0d..156fa0a0da19 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>   	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>   	p->thread.cpu_context.sp = (unsigned long)childregs;
>>   
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
> 
> Nit: end_of_stack(p) would be slightly better semantically, even though
> currently equivalent to task_stack_page(p).
> 
> [...]
> 
>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> +void __used check_alloca(unsigned long size)
>> +{
>> +	unsigned long sp, stack_left;
>> +
>> +	sp = current_stack_pointer;
>> +
>> +	stack_left = sp & (THREAD_SIZE - 1);
>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>> +}
> 
> Is this arbitrary, or is there something special about 256?
> 
> Even if this is arbitrary, can we give it some mnemonic?
> 
>> +EXPORT_SYMBOL(check_alloca);
>> +#endif
>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>> index a34e9290a699..25dd2a14560d 100644
>> --- a/drivers/firmware/efi/libstub/Makefile
>> +++ b/drivers/firmware/efi/libstub/Makefile
>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>   KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>   				   -D__NO_FORTIFY \
>>   				   $(call cc-option,-ffreestanding) \
>> -				   $(call cc-option,-fno-stack-protector)
>> +				   $(call cc-option,-fno-stack-protector) \
>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>   
>>   GCOV_PROFILE			:= n
>>   KASAN_SANITIZE			:= n
> 
> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
> 
> Thanks,
> Mark.
> 

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03 17:33         ` Alexander Popov
@ 2018-05-03 19:09           ` Laura Abbott
  -1 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-03 19:09 UTC (permalink / raw)
  To: alex.popov, Mark Rutland
  Cc: Kees Cook, Ard Biesheuvel, kernel-hardening, linux-arm-kernel,
	linux-kernel

On 05/03/2018 10:33 AM, Alexander Popov wrote:
> Hello Mark and Laura,
> 
> Let me join the discussion. Mark, thanks for your feedback!
> 
> On 03.05.2018 10:19, Mark Rutland wrote:
>> Hi Laura,
>>
>> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>>
>>> Implementation of stackleak based heavily on the x86 version
>>>
>>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>>> ---
>>> Now written in C instead of a bunch of assembly.
>>
>> This looks neat!
>>
>> I have a few minor comments below.
>>
>>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>>> index bf825f38d206..0ceea613c65b 100644
>>> --- a/arch/arm64/kernel/Makefile
>>> +++ b/arch/arm64/kernel/Makefile
>>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>>   arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>>   
>>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>>> +KASAN_SANITIZE_erase.o	:= n
>>
>> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
>> GKOV, KASAN, UBSAN, and KCOV.
> 
> I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
> intentionally writes to the stack and causes KASAN false positive reports.
> 
> But I didn't see any conflicts with other types of instrumentation that you
> mentioned.
> 
>>> +
>>>   obj-y					+= $(arm64-obj-y) vdso/ probes/
>>>   obj-m					+= $(arm64-obj-m)
>>>   head-y					:= head.o
>>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>>> index ec2ee720e33e..3144f1ebdc18 100644
>>> --- a/arch/arm64/kernel/entry.S
>>> +++ b/arch/arm64/kernel/entry.S
>>> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>>>   
>>>   	.text
>>>   
>>> +	.macro	ERASE_KSTACK
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +	bl	erase_kstack
>>> +#endif
>>> +	.endm
>>
>> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
>>
>>>   /*
>>>    * Exception vectors.
>>>    */
>>> @@ -906,6 +911,7 @@ ret_to_user:
>>>   	cbnz	x2, work_pending
>>>   finish_ret_to_user:
>>>   	enable_step_tsk x1, x2
>>> +	ERASE_KSTACK
>>>   	kernel_exit 0
>>>   ENDPROC(ret_to_user)
>>
>> I believe we also need this in ret_fast_syscall.
>>
>> [...]
>>
>>> +asmlinkage void erase_kstack(void)
>>> +{
>>> +	unsigned long p = current->thread.lowest_stack;
>>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>>> +	unsigned long poison = 0;
>>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>>> +							sizeof(unsigned long);
>>> +
>>> +	/*
>>> +	 * Let's search for the poison value in the stack.
>>> +	 * Start from the lowest_stack and go to the bottom.
>>> +	 */
>>> +	while (p > boundary && poison <= check_depth) {
>>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>>> +			poison++;
>>> +		else
>>> +			poison = 0;
>>> +
>>> +		p -= sizeof(unsigned long);
>>> +	}
>>> +
>>> +	/*
>>> +	 * One long int at the bottom of the thread stack is reserved and
>>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>>> +	 */
>>> +	if (p == boundary)
>>> +		p += sizeof(unsigned long);
>>
>> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
>> given that's supposed to return the last *usable* long on the stack, and we
>> don't account for this elsewhere.
> 
> I would be afraid to change the meaning of end_of_stack()... Currently it
> considers that magic long as usable (include/linux/sched/task_stack.h):
> 
> #define task_stack_end_corrupted(task) \
> 		(*(end_of_stack(task)) != STACK_END_MAGIC)
> 
> 
>> If we did, then IIUC we could do:
>>
>> 	unsigned long boundary = (unsigned long)end_of_stack(current);
>>
>> ... at the start of the function, and not have to worry about this explicitly.
> 
> I should mention that erase_kstack() can be called from x86 trampoline stack.
> That's why the boundary is calculated from the lowest_stack.
> 
>>> +
>>> +#ifdef CONFIG_STACKLEAK_METRICS
>>> +	current->thread.prev_lowest_stack = p;
>>> +#endif
>>> +
>>> +	/*
>>> +	 * So let's write the poison value to the kernel stack.
>>> +	 * Start from the address in p and move up till the new boundary.
>>> +	 */
>>> +	boundary = current_stack_pointer;
>>
>> I worry a little that the compiler can move the SP during a function's
>> lifetime, but maybe that's only the case when there are VLAs, or something like
>> that?
> 
> Oh, I don't know.
> 
> However, erase_kstack() doesn't call anything except simple inline functions.
> And as I see from its disasm on x86, the local variables reside in registers.
> 
>>> +
>>> +	BUG_ON(boundary - p >= THREAD_SIZE);
>>> +
>>> +	while (p < boundary) {
>>> +		*(unsigned long *)p = STACKLEAK_POISON;
>>> +		p += sizeof(unsigned long);
>>> +	}
>>> +
>>> +	/* Reset the lowest_stack value for the next syscall */
>>> +	current->thread.lowest_stack = current_stack_pointer;
> 
> Laura, that might be wrong and introduce huge performance impact.
> 
> I think, lowest_stack should be reset similarly to the original version.
> 

Sorry, I'm not understanding here. What's the performance impact and
what do you mean by original version?

>>> +}
>>
>> Once this function returns, its data is left on the stack. Is that not a problem?
>>
>> No strong feelings either way, but it might be worth mentioning in the commit
>> message.
> 
> I managed to bypass that with "register" specifier. Although it doesn't give an
> absolute guarantee.
> 

I guess I was assuming gcc would be smart enough not to spill stuff
on the stack. I also intentionally removed the register keyword
since it wasn't clear gcc does much with it on a modern system? I
could be completely off base here though so please correct me if
I'm wrong. It probably is worth documenting what we are assuming about
the compiler here.


>>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>>> index f08a2ed9db0d..156fa0a0da19 100644
>>> --- a/arch/arm64/kernel/process.c
>>> +++ b/arch/arm64/kernel/process.c
>>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>>   	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>>   	p->thread.cpu_context.sp = (unsigned long)childregs;
>>>   
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>>
>> Nit: end_of_stack(p) would be slightly better semantically, even though
>> currently equivalent to task_stack_page(p).
> 
> Thanks, I agree, I'll fix it in v12.
> 
>> [...]
>>
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +void __used check_alloca(unsigned long size)
>>> +{
>>> +	unsigned long sp, stack_left;
>>> +
>>> +	sp = current_stack_pointer;
>>> +
>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>> +}
>>
>> Is this arbitrary, or is there something special about 256?
>>
>> Even if this is arbitrary, can we give it some mnemonic?
> 
> It's just a reasonable number. We can introduce a macro for it.
> 
>>> +EXPORT_SYMBOL(check_alloca);
>>> +#endif
>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>> index a34e9290a699..25dd2a14560d 100644
>>> --- a/drivers/firmware/efi/libstub/Makefile
>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>>   KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>   				   -D__NO_FORTIFY \
>>>   				   $(call cc-option,-ffreestanding) \
>>> -				   $(call cc-option,-fno-stack-protector)
>>> +				   $(call cc-option,-fno-stack-protector) \
>>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>>   
>>>   GCOV_PROFILE			:= n
>>>   KASAN_SANITIZE			:= n
>>
>> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
> 
> Could you please give more details on that? Why STACKLEAK breaks it?
> 

For reference, I originally added this for the efistub because
it would not compile. I did compile this against my Fedora tree
which has KVM enabled.

> Thanks a lot!
> 
> Best regards,
> Alexander
> 

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-03 19:09           ` Laura Abbott
  0 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-03 19:09 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/03/2018 10:33 AM, Alexander Popov wrote:
> Hello Mark and Laura,
> 
> Let me join the discussion. Mark, thanks for your feedback!
> 
> On 03.05.2018 10:19, Mark Rutland wrote:
>> Hi Laura,
>>
>> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>>
>>> Implementation of stackleak based heavily on the x86 version
>>>
>>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>>> ---
>>> Now written in C instead of a bunch of assembly.
>>
>> This looks neat!
>>
>> I have a few minor comments below.
>>
>>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>>> index bf825f38d206..0ceea613c65b 100644
>>> --- a/arch/arm64/kernel/Makefile
>>> +++ b/arch/arm64/kernel/Makefile
>>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>>   arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>>   arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>>   
>>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>>> +KASAN_SANITIZE_erase.o	:= n
>>
>> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
>> GKOV, KASAN, UBSAN, and KCOV.
> 
> I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
> intentionally writes to the stack and causes KASAN false positive reports.
> 
> But I didn't see any conflicts with other types of instrumentation that you
> mentioned.
> 
>>> +
>>>   obj-y					+= $(arm64-obj-y) vdso/ probes/
>>>   obj-m					+= $(arm64-obj-m)
>>>   head-y					:= head.o
>>> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
>>> index ec2ee720e33e..3144f1ebdc18 100644
>>> --- a/arch/arm64/kernel/entry.S
>>> +++ b/arch/arm64/kernel/entry.S
>>> @@ -401,6 +401,11 @@ tsk	.req	x28		// current thread_info
>>>   
>>>   	.text
>>>   
>>> +	.macro	ERASE_KSTACK
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +	bl	erase_kstack
>>> +#endif
>>> +	.endm
>>
>> Nit: The rest of our asm macros are lower-case -- can we stick to that here?
>>
>>>   /*
>>>    * Exception vectors.
>>>    */
>>> @@ -906,6 +911,7 @@ ret_to_user:
>>>   	cbnz	x2, work_pending
>>>   finish_ret_to_user:
>>>   	enable_step_tsk x1, x2
>>> +	ERASE_KSTACK
>>>   	kernel_exit 0
>>>   ENDPROC(ret_to_user)
>>
>> I believe we also need this in ret_fast_syscall.
>>
>> [...]
>>
>>> +asmlinkage void erase_kstack(void)
>>> +{
>>> +	unsigned long p = current->thread.lowest_stack;
>>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>>> +	unsigned long poison = 0;
>>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>>> +							sizeof(unsigned long);
>>> +
>>> +	/*
>>> +	 * Let's search for the poison value in the stack.
>>> +	 * Start from the lowest_stack and go to the bottom.
>>> +	 */
>>> +	while (p > boundary && poison <= check_depth) {
>>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>>> +			poison++;
>>> +		else
>>> +			poison = 0;
>>> +
>>> +		p -= sizeof(unsigned long);
>>> +	}
>>> +
>>> +	/*
>>> +	 * One long int at the bottom of the thread stack is reserved and
>>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>>> +	 */
>>> +	if (p == boundary)
>>> +		p += sizeof(unsigned long);
>>
>> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
>> given that's supposed to return the last *usable* long on the stack, and we
>> don't account for this elsewhere.
> 
> I would be afraid to change the meaning of end_of_stack()... Currently it
> considers that magic long as usable (include/linux/sched/task_stack.h):
> 
> #define task_stack_end_corrupted(task) \
> 		(*(end_of_stack(task)) != STACK_END_MAGIC)
> 
> 
>> If we did, then IIUC we could do:
>>
>> 	unsigned long boundary = (unsigned long)end_of_stack(current);
>>
>> ... at the start of the function, and not have to worry about this explicitly.
> 
> I should mention that erase_kstack() can be called from x86 trampoline stack.
> That's why the boundary is calculated from the lowest_stack.
> 
>>> +
>>> +#ifdef CONFIG_STACKLEAK_METRICS
>>> +	current->thread.prev_lowest_stack = p;
>>> +#endif
>>> +
>>> +	/*
>>> +	 * So let's write the poison value to the kernel stack.
>>> +	 * Start from the address in p and move up till the new boundary.
>>> +	 */
>>> +	boundary = current_stack_pointer;
>>
>> I worry a little that the compiler can move the SP during a function's
>> lifetime, but maybe that's only the case when there are VLAs, or something like
>> that?
> 
> Oh, I don't know.
> 
> However, erase_kstack() doesn't call anything except simple inline functions.
> And as I see from its disasm on x86, the local variables reside in registers.
> 
>>> +
>>> +	BUG_ON(boundary - p >= THREAD_SIZE);
>>> +
>>> +	while (p < boundary) {
>>> +		*(unsigned long *)p = STACKLEAK_POISON;
>>> +		p += sizeof(unsigned long);
>>> +	}
>>> +
>>> +	/* Reset the lowest_stack value for the next syscall */
>>> +	current->thread.lowest_stack = current_stack_pointer;
> 
> Laura, that might be wrong and introduce huge performance impact.
> 
> I think, lowest_stack should be reset similarly to the original version.
> 

Sorry, I'm not understanding here. What's the performance impact and
what do you mean by original version?

>>> +}
>>
>> Once this function returns, its data is left on the stack. Is that not a problem?
>>
>> No strong feelings either way, but it might be worth mentioning in the commit
>> message.
> 
> I managed to bypass that with "register" specifier. Although it doesn't give an
> absolute guarantee.
> 

I guess I was assuming gcc would be smart enough not to spill stuff
on the stack. I also intentionally removed the register keyword
since it wasn't clear gcc does much with it on a modern system? I
could be completely off base here though so please correct me if
I'm wrong. It probably is worth documenting what we are assuming about
the compiler here.


>>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>>> index f08a2ed9db0d..156fa0a0da19 100644
>>> --- a/arch/arm64/kernel/process.c
>>> +++ b/arch/arm64/kernel/process.c
>>> @@ -364,6 +364,9 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
>>>   	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
>>>   	p->thread.cpu_context.sp = (unsigned long)childregs;
>>>   
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +	p->thread.lowest_stack = (unsigned long)task_stack_page(p);
>>
>> Nit: end_of_stack(p) would be slightly better semantically, even though
>> currently equivalent to task_stack_page(p).
> 
> Thanks, I agree, I'll fix it in v12.
> 
>> [...]
>>
>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>> +void __used check_alloca(unsigned long size)
>>> +{
>>> +	unsigned long sp, stack_left;
>>> +
>>> +	sp = current_stack_pointer;
>>> +
>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>> +}
>>
>> Is this arbitrary, or is there something special about 256?
>>
>> Even if this is arbitrary, can we give it some mnemonic?
> 
> It's just a reasonable number. We can introduce a macro for it.
> 
>>> +EXPORT_SYMBOL(check_alloca);
>>> +#endif
>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>> index a34e9290a699..25dd2a14560d 100644
>>> --- a/drivers/firmware/efi/libstub/Makefile
>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>>   KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>   				   -D__NO_FORTIFY \
>>>   				   $(call cc-option,-ffreestanding) \
>>> -				   $(call cc-option,-fno-stack-protector)
>>> +				   $(call cc-option,-fno-stack-protector) \
>>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>>   
>>>   GCOV_PROFILE			:= n
>>>   KASAN_SANITIZE			:= n
>>
>> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
> 
> Could you please give more details on that? Why STACKLEAK breaks it?
> 

For reference, I originally added this for the efistub because
it would not compile. I did compile this against my Fedora tree
which has KVM enabled.

> Thanks a lot!
> 
> Best regards,
> Alexander
> 

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03 19:09           ` Laura Abbott
@ 2018-05-04  8:30             ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-04  8:30 UTC (permalink / raw)
  To: Laura Abbott, Mark Rutland
  Cc: Kees Cook, Ard Biesheuvel, kernel-hardening, linux-arm-kernel,
	linux-kernel

On 03.05.2018 22:09, Laura Abbott wrote:
> On 05/03/2018 10:33 AM, Alexander Popov wrote:
>> On 03.05.2018 10:19, Mark Rutland wrote:
>>> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>>> +	/* Reset the lowest_stack value for the next syscall */
>>>> +	current->thread.lowest_stack = current_stack_pointer;
>>
>> Laura, that might be wrong and introduce huge performance impact.
>>
>> I think, lowest_stack should be reset similarly to the original version.
>>
> 
> Sorry, I'm not understanding here. What's the performance impact and
> what do you mean by original version?

I meant the code for x86:
	/* Reset the lowest_stack value for the next syscall */
	current->thread.lowest_stack = current_top_of_stack() - 256;

...Now when I'm writing about the performance impact, I see that I was wrong
about "huge". Excuse me.

Let me describe the implications of this code change.

So we are at the end of a syscall. We've just erased the used part of the kernel
stack. The current stack pointer is near to the top of stack. On x86_64 I see
that the stack pointer is stack top minus 56 bytes (just before switching onto
the trampoline stack).

I took the idea of resetting lowest_stack to stack top minus 256 from the
original PaX Team's code. It should give the speedup when lowest_stack is not
updated during a syscall (a lot of functions are not instrumented) and we start
to search for the poison value from that reasonable point.

If we speak about the common erase_kstack() code, this code change can break
x86, because this function can be called from the trampoline stack (separate
from the thread stack).

>>>> +}
>>>
>>> Once this function returns, its data is left on the stack. Is that not a problem?
>>>
>>> No strong feelings either way, but it might be worth mentioning in the commit
>>> message.
>>
>> I managed to bypass that with "register" specifier. Although it doesn't give an
>> absolute guarantee.
>>
> 
> I guess I was assuming gcc would be smart enough not to spill stuff
> on the stack. I also intentionally removed the register keyword
> since it wasn't clear gcc does much with it on a modern system? I
> could be completely off base here though so please correct me if
> I'm wrong. It probably is worth documenting what we are assuming about
> the compiler here.

I think having register storage class specifier here is a bit better than
nothing. And yes, I'll add a comment. Right now don't see a better solution.

>>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>>> index a34e9290a699..25dd2a14560d 100644
>>>> --- a/drivers/firmware/efi/libstub/Makefile
>>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>>>   KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>>   				   -D__NO_FORTIFY \
>>>>   				   $(call cc-option,-ffreestanding) \
>>>> -				   $(call cc-option,-fno-stack-protector)
>>>> +				   $(call cc-option,-fno-stack-protector) \
>>>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>>>   
>>>>   GCOV_PROFILE			:= n
>>>>   KASAN_SANITIZE			:= n
>>>
>>> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
>>
>> Could you please give more details on that? Why STACKLEAK breaks it?
>>
> 
> For reference, I originally added this for the efistub because
> it would not compile.

I guess it was a linkage error, right?

> I did compile this against my Fedora tree which has KVM enabled.

Looked through this big article about ARM, KVM and HYP mode:
https://lwn.net/Articles/557132/

So we have some limited amount of kernel code which runs in HYP mode. Is it only
in arch/arm64/kvm/hyp/ directory?

Mark, could you give a clue what trouble will we have if we call track_stack()
or check_alloca() from that code?

Thanks in advance!

--
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-04  8:30             ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-04  8:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 03.05.2018 22:09, Laura Abbott wrote:
> On 05/03/2018 10:33 AM, Alexander Popov wrote:
>> On 03.05.2018 10:19, Mark Rutland wrote:
>>> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>>> +	/* Reset the lowest_stack value for the next syscall */
>>>> +	current->thread.lowest_stack = current_stack_pointer;
>>
>> Laura, that might be wrong and introduce huge performance impact.
>>
>> I think, lowest_stack should be reset similarly to the original version.
>>
> 
> Sorry, I'm not understanding here. What's the performance impact and
> what do you mean by original version?

I meant the code for x86:
	/* Reset the lowest_stack value for the next syscall */
	current->thread.lowest_stack = current_top_of_stack() - 256;

...Now when I'm writing about the performance impact, I see that I was wrong
about "huge". Excuse me.

Let me describe the implications of this code change.

So we are at the end of a syscall. We've just erased the used part of the kernel
stack. The current stack pointer is near to the top of stack. On x86_64 I see
that the stack pointer is stack top minus 56 bytes (just before switching onto
the trampoline stack).

I took the idea of resetting lowest_stack to stack top minus 256 from the
original PaX Team's code. It should give the speedup when lowest_stack is not
updated during a syscall (a lot of functions are not instrumented) and we start
to search for the poison value from that reasonable point.

If we speak about the common erase_kstack() code, this code change can break
x86, because this function can be called from the trampoline stack (separate
from the thread stack).

>>>> +}
>>>
>>> Once this function returns, its data is left on the stack. Is that not a problem?
>>>
>>> No strong feelings either way, but it might be worth mentioning in the commit
>>> message.
>>
>> I managed to bypass that with "register" specifier. Although it doesn't give an
>> absolute guarantee.
>>
> 
> I guess I was assuming gcc would be smart enough not to spill stuff
> on the stack. I also intentionally removed the register keyword
> since it wasn't clear gcc does much with it on a modern system? I
> could be completely off base here though so please correct me if
> I'm wrong. It probably is worth documenting what we are assuming about
> the compiler here.

I think having register storage class specifier here is a bit better than
nothing. And yes, I'll add a comment. Right now don't see a better solution.

>>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>>> index a34e9290a699..25dd2a14560d 100644
>>>> --- a/drivers/firmware/efi/libstub/Makefile
>>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>>>   KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>>   				   -D__NO_FORTIFY \
>>>>   				   $(call cc-option,-ffreestanding) \
>>>> -				   $(call cc-option,-fno-stack-protector)
>>>> +				   $(call cc-option,-fno-stack-protector) \
>>>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>>>   
>>>>   GCOV_PROFILE			:= n
>>>>   KASAN_SANITIZE			:= n
>>>
>>> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
>>
>> Could you please give more details on that? Why STACKLEAK breaks it?
>>
> 
> For reference, I originally added this for the efistub because
> it would not compile.

I guess it was a linkage error, right?

> I did compile this against my Fedora tree which has KVM enabled.

Looked through this big article about ARM, KVM and HYP mode:
https://lwn.net/Articles/557132/

So we have some limited amount of kernel code which runs in HYP mode. Is it only
in arch/arm64/kvm/hyp/ directory?

Mark, could you give a clue what trouble will we have if we call track_stack()
or check_alloca() from that code?

Thanks in advance!

--
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03 17:33         ` Alexander Popov
@ 2018-05-04 11:09           ` Mark Rutland
  -1 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-04 11:09 UTC (permalink / raw)
  To: Alexander Popov
  Cc: Laura Abbott, Kees Cook, Ard Biesheuvel, kernel-hardening,
	linux-arm-kernel, linux-kernel

On Thu, May 03, 2018 at 08:33:38PM +0300, Alexander Popov wrote:
> Hello Mark and Laura,
> 
> Let me join the discussion. Mark, thanks for your feedback!
> 
> On 03.05.2018 10:19, Mark Rutland wrote:
> > Hi Laura,
> > 
> > On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
> >>
> >> Implementation of stackleak based heavily on the x86 version
> >>
> >> Signed-off-by: Laura Abbott <labbott@redhat.com>
> >> ---
> >> Now written in C instead of a bunch of assembly.
> > 
> > This looks neat!
> > 
> > I have a few minor comments below.
> > 
> >> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> >> index bf825f38d206..0ceea613c65b 100644
> >> --- a/arch/arm64/kernel/Makefile
> >> +++ b/arch/arm64/kernel/Makefile
> >> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
> >>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
> >>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
> >>  
> >> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
> >> +KASAN_SANITIZE_erase.o	:= n
> > 
> > I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> > GKOV, KASAN, UBSAN, and KCOV.
> 
> I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
> intentionally writes to the stack and causes KASAN false positive reports.
> 
> But I didn't see any conflicts with other types of instrumentation that you
> mentioned.

The rationale is that any of these can result in implicit calls to C
functions at arbitrary points during erase_kstack(). That could
interfere with the search for poison, and/or leave data on the stack
which is not erased.

They won't result in hard failures, as KASAN would, but we should
probably avoid them regardless.

[...]

> >> +asmlinkage void erase_kstack(void)
> >> +{
> >> +	unsigned long p = current->thread.lowest_stack;
> >> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
> >> +	unsigned long poison = 0;
> >> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
> >> +							sizeof(unsigned long);
> >> +
> >> +	/*
> >> +	 * Let's search for the poison value in the stack.
> >> +	 * Start from the lowest_stack and go to the bottom.
> >> +	 */
> >> +	while (p > boundary && poison <= check_depth) {
> >> +		if (*(unsigned long *)p == STACKLEAK_POISON)
> >> +			poison++;
> >> +		else
> >> +			poison = 0;
> >> +
> >> +		p -= sizeof(unsigned long);
> >> +	}
> >> +
> >> +	/*
> >> +	 * One long int at the bottom of the thread stack is reserved and
> >> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
> >> +	 */
> >> +	if (p == boundary)
> >> +		p += sizeof(unsigned long);
> > 
> > I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> > given that's supposed to return the last *usable* long on the stack, and we
> > don't account for this elsewhere.
> 
> I would be afraid to change the meaning of end_of_stack()... Currently it
> considers that magic long as usable (include/linux/sched/task_stack.h):
> 
> #define task_stack_end_corrupted(task) \
> 		(*(end_of_stack(task)) != STACK_END_MAGIC)
> 
> 
> > If we did, then IIUC we could do:
> > 
> > 	unsigned long boundary = (unsigned long)end_of_stack(current);
> > 
> > ... at the start of the function, and not have to worry about this explicitly.
> 
> I should mention that erase_kstack() can be called from x86 trampoline stack.
> That's why the boundary is calculated from the lowest_stack.

Ok. Under what circumstances does that happen?

It seems a little scary that curent::thread::lowest_stack might not be
on current's task stack. Is that reset when transitioning to/from the
trampoile stack?

[...]

> >> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> >> +void __used check_alloca(unsigned long size)
> >> +{
> >> +	unsigned long sp, stack_left;
> >> +
> >> +	sp = current_stack_pointer;
> >> +
> >> +	stack_left = sp & (THREAD_SIZE - 1);
> >> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
> >> +}
> > 
> > Is this arbitrary, or is there something special about 256?
> > 
> > Even if this is arbitrary, can we give it some mnemonic?
> 
> It's just a reasonable number. We can introduce a macro for it.

I'm just not sure I see the point in the offset, given things like
VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
bytes of stack, so it seems superfluous, as we'd be relying on stack
overflow detection at that point.

I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
into account, though.

> >> +EXPORT_SYMBOL(check_alloca);
> >> +#endif
> >> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> >> index a34e9290a699..25dd2a14560d 100644
> >> --- a/drivers/firmware/efi/libstub/Makefile
> >> +++ b/drivers/firmware/efi/libstub/Makefile
> >> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
> >>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
> >>  				   -D__NO_FORTIFY \
> >>  				   $(call cc-option,-ffreestanding) \
> >> -				   $(call cc-option,-fno-stack-protector)
> >> +				   $(call cc-option,-fno-stack-protector) \
> >> +				   $(DISABLE_STACKLEAK_PLUGIN)
> >>  
> >>  GCOV_PROFILE			:= n
> >>  KASAN_SANITIZE			:= n
> > 
> > I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
> 
> Could you please give more details on that? Why STACKLEAK breaks it?

In the hyp/EL2 exception level, we only map the hyp text, and not the
rest of the kernel. So erase_kstack and check_alloca won't be mapped,
and attempt to branch to them will fault.

Even if it were mapped, things like BUG_ON(), get_current(), etc do not
work at hyp.

Additionally, the hyp code is mapped as a different virtual address from
the rest of the kernel, so if any of the STACKLEAK code happens to use
an absolute address, this will not work correctly.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-04 11:09           ` Mark Rutland
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-04 11:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, May 03, 2018 at 08:33:38PM +0300, Alexander Popov wrote:
> Hello Mark and Laura,
> 
> Let me join the discussion. Mark, thanks for your feedback!
> 
> On 03.05.2018 10:19, Mark Rutland wrote:
> > Hi Laura,
> > 
> > On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
> >>
> >> Implementation of stackleak based heavily on the x86 version
> >>
> >> Signed-off-by: Laura Abbott <labbott@redhat.com>
> >> ---
> >> Now written in C instead of a bunch of assembly.
> > 
> > This looks neat!
> > 
> > I have a few minor comments below.
> > 
> >> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> >> index bf825f38d206..0ceea613c65b 100644
> >> --- a/arch/arm64/kernel/Makefile
> >> +++ b/arch/arm64/kernel/Makefile
> >> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
> >>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
> >>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
> >>  
> >> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
> >> +KASAN_SANITIZE_erase.o	:= n
> > 
> > I suspect we want to avoid the full set of instrumentation suspects here, e.g.
> > GKOV, KASAN, UBSAN, and KCOV.
> 
> I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
> intentionally writes to the stack and causes KASAN false positive reports.
> 
> But I didn't see any conflicts with other types of instrumentation that you
> mentioned.

The rationale is that any of these can result in implicit calls to C
functions at arbitrary points during erase_kstack(). That could
interfere with the search for poison, and/or leave data on the stack
which is not erased.

They won't result in hard failures, as KASAN would, but we should
probably avoid them regardless.

[...]

> >> +asmlinkage void erase_kstack(void)
> >> +{
> >> +	unsigned long p = current->thread.lowest_stack;
> >> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
> >> +	unsigned long poison = 0;
> >> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
> >> +							sizeof(unsigned long);
> >> +
> >> +	/*
> >> +	 * Let's search for the poison value in the stack.
> >> +	 * Start from the lowest_stack and go to the bottom.
> >> +	 */
> >> +	while (p > boundary && poison <= check_depth) {
> >> +		if (*(unsigned long *)p == STACKLEAK_POISON)
> >> +			poison++;
> >> +		else
> >> +			poison = 0;
> >> +
> >> +		p -= sizeof(unsigned long);
> >> +	}
> >> +
> >> +	/*
> >> +	 * One long int at the bottom of the thread stack is reserved and
> >> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
> >> +	 */
> >> +	if (p == boundary)
> >> +		p += sizeof(unsigned long);
> > 
> > I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
> > given that's supposed to return the last *usable* long on the stack, and we
> > don't account for this elsewhere.
> 
> I would be afraid to change the meaning of end_of_stack()... Currently it
> considers that magic long as usable (include/linux/sched/task_stack.h):
> 
> #define task_stack_end_corrupted(task) \
> 		(*(end_of_stack(task)) != STACK_END_MAGIC)
> 
> 
> > If we did, then IIUC we could do:
> > 
> > 	unsigned long boundary = (unsigned long)end_of_stack(current);
> > 
> > ... at the start of the function, and not have to worry about this explicitly.
> 
> I should mention that erase_kstack() can be called from x86 trampoline stack.
> That's why the boundary is calculated from the lowest_stack.

Ok. Under what circumstances does that happen?

It seems a little scary that curent::thread::lowest_stack might not be
on current's task stack. Is that reset when transitioning to/from the
trampoile stack?

[...]

> >> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> >> +void __used check_alloca(unsigned long size)
> >> +{
> >> +	unsigned long sp, stack_left;
> >> +
> >> +	sp = current_stack_pointer;
> >> +
> >> +	stack_left = sp & (THREAD_SIZE - 1);
> >> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
> >> +}
> > 
> > Is this arbitrary, or is there something special about 256?
> > 
> > Even if this is arbitrary, can we give it some mnemonic?
> 
> It's just a reasonable number. We can introduce a macro for it.

I'm just not sure I see the point in the offset, given things like
VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
bytes of stack, so it seems superfluous, as we'd be relying on stack
overflow detection at that point.

I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
into account, though.

> >> +EXPORT_SYMBOL(check_alloca);
> >> +#endif
> >> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> >> index a34e9290a699..25dd2a14560d 100644
> >> --- a/drivers/firmware/efi/libstub/Makefile
> >> +++ b/drivers/firmware/efi/libstub/Makefile
> >> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
> >>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
> >>  				   -D__NO_FORTIFY \
> >>  				   $(call cc-option,-ffreestanding) \
> >> -				   $(call cc-option,-fno-stack-protector)
> >> +				   $(call cc-option,-fno-stack-protector) \
> >> +				   $(DISABLE_STACKLEAK_PLUGIN)
> >>  
> >>  GCOV_PROFILE			:= n
> >>  KASAN_SANITIZE			:= n
> > 
> > I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
> 
> Could you please give more details on that? Why STACKLEAK breaks it?

In the hyp/EL2 exception level, we only map the hyp text, and not the
rest of the kernel. So erase_kstack and check_alloca won't be mapped,
and attempt to branch to them will fault.

Even if it were mapped, things like BUG_ON(), get_current(), etc do not
work at hyp.

Additionally, the hyp code is mapped as a different virtual address from
the rest of the kernel, so if any of the STACKLEAK code happens to use
an absolute address, this will not work correctly.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-03 19:00         ` Laura Abbott
@ 2018-05-04 11:16           ` Mark Rutland
  -1 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-04 11:16 UTC (permalink / raw)
  To: Laura Abbott
  Cc: Alexander Popov, Kees Cook, Ard Biesheuvel, kernel-hardening,
	linux-arm-kernel, linux-kernel

On Thu, May 03, 2018 at 12:00:26PM -0700, Laura Abbott wrote:
> On 05/03/2018 12:19 AM, Mark Rutland wrote:
> > On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:

> > > +asmlinkage void erase_kstack(void)
> > > +{

> > > +
> > > +	/*
> > > +	 * So let's write the poison value to the kernel stack.
> > > +	 * Start from the address in p and move up till the new boundary.
> > > +	 */
> > > +	boundary = current_stack_pointer;
> > 
> > I worry a little that the compiler can move the SP during a function's
> > lifetime, but maybe that's only the case when there are VLAs, or something like
> > that?
> 
> I think that's true and a risk we take writing this in C. Here's
> the disassembly on gcc-7.3.1:
> 
> ffff00000809d4d8 <erase_kstack>:
> ffff00000809d4d8:       a9bf7bfd        stp     x29, x30, [sp, #-16]!
> ffff00000809d4dc:       d5384100        mrs     x0, sp_el0
> ffff00000809d4e0:       910003fd        mov     x29, sp
> ffff00000809d4e4:       f946e400        ldr     x0, [x0, #3528]
> ffff00000809d4e8:       9272c404        and     x4, x0, #0xffffffffffffc000
> ffff00000809d4ec:       eb04001f        cmp     x0, x4
> ffff00000809d4f0:       540002c9        b.ls    ffff00000809d548 <erase_kstack+0x70>  // b.plast
> ffff00000809d4f4:       d2800003        mov     x3, #0x0                        // #0
> ffff00000809d4f8:       9297ddc5        mov     x5, #0xffffffffffff4111         // #-48879
> ffff00000809d4fc:       14000008        b       ffff00000809d51c <erase_kstack+0x44>
> ffff00000809d500:       d1002000        sub     x0, x0, #0x8
> ffff00000809d504:       52800022        mov     w2, #0x1                        // #1
> ffff00000809d508:       eb00009f        cmp     x4, x0
> ffff00000809d50c:       d2800003        mov     x3, #0x0                        // #0
> ffff00000809d510:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
> ffff00000809d514:       6a01005f        tst     w2, w1
> ffff00000809d518:       54000180        b.eq    ffff00000809d548 <erase_kstack+0x70>  // b.none
> ffff00000809d51c:       f9400001        ldr     x1, [x0]
> ffff00000809d520:       eb05003f        cmp     x1, x5
> ffff00000809d524:       54fffee1        b.ne    ffff00000809d500 <erase_kstack+0x28>  // b.any
> ffff00000809d528:       91000463        add     x3, x3, #0x1
> ffff00000809d52c:       d1002000        sub     x0, x0, #0x8
> ffff00000809d530:       f100407f        cmp     x3, #0x10
> ffff00000809d534:       1a9f87e2        cset    w2, ls  // ls = plast
> ffff00000809d538:       eb00009f        cmp     x4, x0
> ffff00000809d53c:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
> ffff00000809d540:       6a01005f        tst     w2, w1
> ffff00000809d544:       54fffec1        b.ne    ffff00000809d51c <erase_kstack+0x44>  // b.any
> ffff00000809d548:       eb00009f        cmp     x4, x0
> ffff00000809d54c:       91002001        add     x1, x0, #0x8
> ffff00000809d550:       9a800020        csel    x0, x1, x0, eq  // eq = none
> ffff00000809d554:       910003e1        mov     x1, sp
> ffff00000809d558:       d5384102        mrs     x2, sp_el0
> ffff00000809d55c:       f906e840        str     x0, [x2, #3536]
> ffff00000809d560:       cb000023        sub     x3, x1, x0
> ffff00000809d564:       d287ffe2        mov     x2, #0x3fff                     // #16383
> ffff00000809d568:       eb02007f        cmp     x3, x2
> ffff00000809d56c:       540001a8        b.hi    ffff00000809d5a0 <erase_kstack+0xc8>  // b.pmore
> ffff00000809d570:       9297ddc2        mov     x2, #0xffffffffffff4111         // #-48879
> ffff00000809d574:       eb01001f        cmp     x0, x1
> ffff00000809d578:       54000082        b.cs    ffff00000809d588 <erase_kstack+0xb0>  // b.hs, b.nlast
> ffff00000809d57c:       f8008402        str     x2, [x0], #8
> ffff00000809d580:       eb00003f        cmp     x1, x0
> ffff00000809d584:       54ffffc8        b.hi    ffff00000809d57c <erase_kstack+0xa4>  // b.pmore
> ffff00000809d588:       910003e1        mov     x1, sp
> ffff00000809d58c:       d5384100        mrs     x0, sp_el0
> ffff00000809d590:       f906e401        str     x1, [x0, #3528]
> ffff00000809d594:       a8c17bfd        ldp     x29, x30, [sp], #16
> ffff00000809d598:       d65f03c0        ret
> ffff00000809d59c:       d503201f        nop
> ffff00000809d5a0:       d4210000        brk     #0x800
> ffff00000809d5a4:       00000000        .inst   0x00000000 ; undefined
> 
> It looks to be okay although admittedly that's subject to compiler
> whims. It might be safer to save the stack pointer almost as soon as
> we get into the function and use that?

I think that's still potentially a problem. If the compiler expands the
stack frame after we've taken a snaphot of the stack pointer, we might
end up erasing portions of the active stackframe.

Maybe we should just document we rely on the compiler not doing that,
and if we end up seeing it in practice we rewrite this in asm? I can't
think of a simple way we can auto-detect if this happens. :/

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-04 11:16           ` Mark Rutland
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-04 11:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, May 03, 2018 at 12:00:26PM -0700, Laura Abbott wrote:
> On 05/03/2018 12:19 AM, Mark Rutland wrote:
> > On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:

> > > +asmlinkage void erase_kstack(void)
> > > +{

> > > +
> > > +	/*
> > > +	 * So let's write the poison value to the kernel stack.
> > > +	 * Start from the address in p and move up till the new boundary.
> > > +	 */
> > > +	boundary = current_stack_pointer;
> > 
> > I worry a little that the compiler can move the SP during a function's
> > lifetime, but maybe that's only the case when there are VLAs, or something like
> > that?
> 
> I think that's true and a risk we take writing this in C. Here's
> the disassembly on gcc-7.3.1:
> 
> ffff00000809d4d8 <erase_kstack>:
> ffff00000809d4d8:       a9bf7bfd        stp     x29, x30, [sp, #-16]!
> ffff00000809d4dc:       d5384100        mrs     x0, sp_el0
> ffff00000809d4e0:       910003fd        mov     x29, sp
> ffff00000809d4e4:       f946e400        ldr     x0, [x0, #3528]
> ffff00000809d4e8:       9272c404        and     x4, x0, #0xffffffffffffc000
> ffff00000809d4ec:       eb04001f        cmp     x0, x4
> ffff00000809d4f0:       540002c9        b.ls    ffff00000809d548 <erase_kstack+0x70>  // b.plast
> ffff00000809d4f4:       d2800003        mov     x3, #0x0                        // #0
> ffff00000809d4f8:       9297ddc5        mov     x5, #0xffffffffffff4111         // #-48879
> ffff00000809d4fc:       14000008        b       ffff00000809d51c <erase_kstack+0x44>
> ffff00000809d500:       d1002000        sub     x0, x0, #0x8
> ffff00000809d504:       52800022        mov     w2, #0x1                        // #1
> ffff00000809d508:       eb00009f        cmp     x4, x0
> ffff00000809d50c:       d2800003        mov     x3, #0x0                        // #0
> ffff00000809d510:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
> ffff00000809d514:       6a01005f        tst     w2, w1
> ffff00000809d518:       54000180        b.eq    ffff00000809d548 <erase_kstack+0x70>  // b.none
> ffff00000809d51c:       f9400001        ldr     x1, [x0]
> ffff00000809d520:       eb05003f        cmp     x1, x5
> ffff00000809d524:       54fffee1        b.ne    ffff00000809d500 <erase_kstack+0x28>  // b.any
> ffff00000809d528:       91000463        add     x3, x3, #0x1
> ffff00000809d52c:       d1002000        sub     x0, x0, #0x8
> ffff00000809d530:       f100407f        cmp     x3, #0x10
> ffff00000809d534:       1a9f87e2        cset    w2, ls  // ls = plast
> ffff00000809d538:       eb00009f        cmp     x4, x0
> ffff00000809d53c:       1a9f27e1        cset    w1, cc  // cc = lo, ul, last
> ffff00000809d540:       6a01005f        tst     w2, w1
> ffff00000809d544:       54fffec1        b.ne    ffff00000809d51c <erase_kstack+0x44>  // b.any
> ffff00000809d548:       eb00009f        cmp     x4, x0
> ffff00000809d54c:       91002001        add     x1, x0, #0x8
> ffff00000809d550:       9a800020        csel    x0, x1, x0, eq  // eq = none
> ffff00000809d554:       910003e1        mov     x1, sp
> ffff00000809d558:       d5384102        mrs     x2, sp_el0
> ffff00000809d55c:       f906e840        str     x0, [x2, #3536]
> ffff00000809d560:       cb000023        sub     x3, x1, x0
> ffff00000809d564:       d287ffe2        mov     x2, #0x3fff                     // #16383
> ffff00000809d568:       eb02007f        cmp     x3, x2
> ffff00000809d56c:       540001a8        b.hi    ffff00000809d5a0 <erase_kstack+0xc8>  // b.pmore
> ffff00000809d570:       9297ddc2        mov     x2, #0xffffffffffff4111         // #-48879
> ffff00000809d574:       eb01001f        cmp     x0, x1
> ffff00000809d578:       54000082        b.cs    ffff00000809d588 <erase_kstack+0xb0>  // b.hs, b.nlast
> ffff00000809d57c:       f8008402        str     x2, [x0], #8
> ffff00000809d580:       eb00003f        cmp     x1, x0
> ffff00000809d584:       54ffffc8        b.hi    ffff00000809d57c <erase_kstack+0xa4>  // b.pmore
> ffff00000809d588:       910003e1        mov     x1, sp
> ffff00000809d58c:       d5384100        mrs     x0, sp_el0
> ffff00000809d590:       f906e401        str     x1, [x0, #3528]
> ffff00000809d594:       a8c17bfd        ldp     x29, x30, [sp], #16
> ffff00000809d598:       d65f03c0        ret
> ffff00000809d59c:       d503201f        nop
> ffff00000809d5a0:       d4210000        brk     #0x800
> ffff00000809d5a4:       00000000        .inst   0x00000000 ; undefined
> 
> It looks to be okay although admittedly that's subject to compiler
> whims. It might be safer to save the stack pointer almost as soon as
> we get into the function and use that?

I think that's still potentially a problem. If the compiler expands the
stack frame after we've taken a snaphot of the stack pointer, we might
end up erasing portions of the active stackframe.

Maybe we should just document we rely on the compiler not doing that,
and if we end up seeing it in practice we rewrite this in asm? I can't
think of a simple way we can auto-detect if this happens. :/

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
  2018-05-04 11:09           ` Mark Rutland
@ 2018-05-06  8:22             ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-06  8:22 UTC (permalink / raw)
  To: Mark Rutland, Andy Lutomirski
  Cc: Laura Abbott, Kees Cook, Ard Biesheuvel, kernel-hardening,
	linux-arm-kernel, linux-kernel

On 04.05.2018 14:09, Mark Rutland wrote:
> On Thu, May 03, 2018 at 08:33:38PM +0300, Alexander Popov wrote:
>> Hello Mark and Laura,
>>
>> Let me join the discussion. Mark, thanks for your feedback!
>>
>> On 03.05.2018 10:19, Mark Rutland wrote:
>>> Hi Laura,
>>>
>>> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>>>
>>>> Implementation of stackleak based heavily on the x86 version
>>>>
>>>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>>>> ---
>>>> Now written in C instead of a bunch of assembly.
>>>
>>> This looks neat!
>>>
>>> I have a few minor comments below.
>>>
>>>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>>>> index bf825f38d206..0ceea613c65b 100644
>>>> --- a/arch/arm64/kernel/Makefile
>>>> +++ b/arch/arm64/kernel/Makefile
>>>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>>>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>>>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>>>  
>>>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>>>> +KASAN_SANITIZE_erase.o	:= n
>>>
>>> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
>>> GKOV, KASAN, UBSAN, and KCOV.
>>
>> I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
>> intentionally writes to the stack and causes KASAN false positive reports.
>>
>> But I didn't see any conflicts with other types of instrumentation that you
>> mentioned.
> 
> The rationale is that any of these can result in implicit calls to C
> functions at arbitrary points during erase_kstack(). That could
> interfere with the search for poison, and/or leave data on the stack
> which is not erased.
> 
> They won't result in hard failures, as KASAN would, but we should
> probably avoid them regardless.

Thanks, Mark! Agree about KCOV, I'll switch it off for that file.

And I think I should _not_ disable UBSAN for that file. I didn't make any
intentional UB, so if UBSAN finds anything, that will be a true positive report.

> [...]
> 
>>>> +asmlinkage void erase_kstack(void)
>>>> +{
>>>> +	unsigned long p = current->thread.lowest_stack;
>>>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>>>> +	unsigned long poison = 0;
>>>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>>>> +							sizeof(unsigned long);
>>>> +
>>>> +	/*
>>>> +	 * Let's search for the poison value in the stack.
>>>> +	 * Start from the lowest_stack and go to the bottom.
>>>> +	 */
>>>> +	while (p > boundary && poison <= check_depth) {
>>>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>>>> +			poison++;
>>>> +		else
>>>> +			poison = 0;
>>>> +
>>>> +		p -= sizeof(unsigned long);
>>>> +	}
>>>> +
>>>> +	/*
>>>> +	 * One long int at the bottom of the thread stack is reserved and
>>>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>>>> +	 */
>>>> +	if (p == boundary)
>>>> +		p += sizeof(unsigned long);
>>>
>>> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
>>> given that's supposed to return the last *usable* long on the stack, and we
>>> don't account for this elsewhere.
>>
>> I would be afraid to change the meaning of end_of_stack()... Currently it
>> considers that magic long as usable (include/linux/sched/task_stack.h):
>>
>> #define task_stack_end_corrupted(task) \
>> 		(*(end_of_stack(task)) != STACK_END_MAGIC)
>>
>>
>>> If we did, then IIUC we could do:
>>>
>>> 	unsigned long boundary = (unsigned long)end_of_stack(current);
>>>
>>> ... at the start of the function, and not have to worry about this explicitly.
>>
>> I should mention that erase_kstack() can be called from x86 trampoline stack.
>> That's why the boundary is calculated from the lowest_stack.
> 
> Ok. Under what circumstances does that happen?
> 
> It seems a little scary that curent::thread::lowest_stack might not be
> on current's task stack. 

Yes, indeed. That's why I check against that, please see BUG_ON() in
erase_kstack() for x86.

1. Calculate the boundary from the lowest_stack.
2. Search for poison between lowest_stack and boundary.
3. Now ready to write the poison.
4. Reset the boundary to current_stack_pointer if we are on the thread stack and
to current_top_of_stack otherwise (we are on the trampoline stack).
5. BUG_ON(boundary - p >= THREAD_SIZE);
6. Write poison till the boundary.

> Is that reset when transitioning to/from the
> trampoile stack?

We switch to the trampoline stack from the current thread stack just before
returning to the userspace. Please search for "trampoline stack" in
arch/x86/entry/entry_64.S.

> [...]
> 
>>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>>> +void __used check_alloca(unsigned long size)
>>>> +{
>>>> +	unsigned long sp, stack_left;
>>>> +
>>>> +	sp = current_stack_pointer;
>>>> +
>>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>>> +}
>>>
>>> Is this arbitrary, or is there something special about 256?
>>>
>>> Even if this is arbitrary, can we give it some mnemonic?
>>
>> It's just a reasonable number. We can introduce a macro for it.
> 
> I'm just not sure I see the point in the offset, given things like
> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
> bytes of stack, so it seems superfluous, as we'd be relying on stack
> overflow detection at that point.
> 
> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
> into account, though.

Mark, thank you for such an important remark!

In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
doesn't have VMAP_STACK at all but can have STACKLEAK.

[Adding Andy Lutomirski]

I've made some additional experiments: I exhaust the thread stack to have only
(MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
disabled, BUG_ON() handling causes stack depth overflow, which is detected by
SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:

[   43.543962] lkdtm: try a large alloca of 14647 bytes (sp 18446683600580263344)...
[   43.545188] BUG: stack guard page was hit at 00000000830608b8 (stack is 000000009375e943..00000000cb7f52d9)
[   43.545189] kernel stack overflow (double-fault): 0000 [#1] SMP PTI
[   43.545189] Dumping ftrace buffer:
[   43.545190]    (ftrace buffer empty)
[   43.545190] Modules linked in: lkdtm
[   43.545192] CPU: 0 PID: 2682 Comm: sh Not tainted 4.17.0-rc3+ #23
[   43.545192] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[   43.545193] RIP: 0010:mark_lock+0xe/0x540
[   43.545193] RSP: 0018:ffffc900009c0000 EFLAGS: 00010002
[   43.545194] RAX: 000000000000000c RBX: ffff880079b3b590 RCX: 0000000000000008
[   43.545194] RDX: 0000000000000008 RSI: ffff880079b3b590 RDI: ffff880079b3ad40
[   43.545195] RBP: ffffc900009c0100 R08: 0000000000000002 R09: 0000000000000000
[   43.545195] R10: ffffc900009c0118 R11: 0000000000000000 R12: 0000000000000000
[   43.545196] R13: ffff880079b3ad40 R14: ffff880079b3ad40 R15: ffffffff810cb8d7
[   43.545196] FS:  00007f544c7d8700(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000
[   43.545197] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   43.545200] CR2: ffffc900009bfff8 CR3: 0000000079194000 CR4: 00000000000006f0
[   43.545200] Call Trace:
[   43.545201]  ? vprintk_emit+0x67/0x440
[   43.545201]  __lock_acquire+0x2e0/0x13e0
[   43.545201]  ? lock_acquire+0x9d/0x1e0
[   43.545202]  lock_acquire+0x9d/0x1e0
[   43.545202]  ? vprintk_emit+0x67/0x440
[   43.545203]  _raw_spin_lock+0x20/0x30
[   43.545203]  ? vprintk_emit+0x67/0x440
[   43.545203]  vprintk_emit+0x67/0x440
[   43.545204]  ? check_alloca+0x9a/0xb0
[   43.545204]  printk+0x50/0x6f
[   43.545204]  ? __probe_kernel_read+0x34/0x60
[   43.545205]  ? check_alloca+0x9a/0xb0
[   43.545205]  report_bug+0xd3/0x110
[   43.545206]  fixup_bug.part.10+0x13/0x30
[   43.545206]  do_error_trap+0x158/0x190
[   43.545206]  ? trace_hardirqs_off_thunk+0x1a/0x1c
[   43.545207]  invalid_op+0x14/0x20
[   43.545207] RIP: 0010:check_alloca+0x9a/0xb0
[   43.545207] RSP: 0018:ffffc900009c0408 EFLAGS: 00010287
[   43.545208] RAX: 0000000000000008 RBX: 0000000000003936 RCX: 0000000000000001
[   43.545209] RDX: 0000000000000002 RSI: 0000000000000000 RDI: ffffc900009c0408
[   43.545209] RBP: ffffc900009c3da0 R08: 0000000000000000 R09: 0000000000000000
[   43.545210] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000003936
[   43.545210] R13: 0000000001ff0610 R14: 0000000000000011 R15: ffffc900009c3f08
[   43.545210]  ? check_alloca+0x64/0xb0
[   43.545211]  do_alloca+0x55/0x71b [lkdtm]
[   43.545211]  ? noop_count+0x10/0x10
[   43.545211]  ? check_usage+0xb1/0x4d0
[   43.545212]  ? noop_count+0x10/0x10
[   43.545212]  ? check_usage+0xb1/0x4d0
[   43.545213]  ? serial8250_console_write+0x253/0x2b0
[   43.545213]  ? serial8250_console_write+0x253/0x2b0
[   43.545213]  ? __lock_acquire+0x2e0/0x13e0
[   43.545214]  ? up+0xd/0x50
[   43.545214]  ? console_unlock+0x374/0x660
[   43.545215]  ? __lock_acquire+0x2e0/0x13e0
[   43.545215]  ? retint_kernel+0x10/0x10
[   43.545215]  ? trace_hardirqs_on_caller+0xed/0x180
[   43.545216]  ? find_held_lock+0x2d/0x90
[   43.545216]  ? mark_held_locks+0x4e/0x80
[   43.545216]  ? console_unlock+0x471/0x660
[   43.545217]  ? trace_hardirqs_on_caller+0xed/0x180
[   43.545217]  ? vprintk_emit+0x235/0x440
[   43.545218]  ? get_stack_info+0x32/0x160
[   43.545218]  ? check_alloca+0x64/0xb0
[   43.545218]  ? do_alloca+0x1f/0x71b [lkdtm]
[   43.545219]  lkdtm_STACKLEAK_ALLOCA+0x8f/0xb0 [lkdtm]
[   43.545219]  direct_entry+0xc5/0x110 [lkdtm]
[   43.545220]  full_proxy_write+0x51/0x80
[   43.545220]  __vfs_write+0x49/0x180
[   43.545220]  ? rcu_read_lock_sched_held+0x53/0x60
[   43.545221]  ? rcu_sync_lockdep_assert+0x29/0x50
[   43.545221]  ? __sb_start_write+0x110/0x160
[   43.545221]  ? vfs_write+0x172/0x190
[   43.545222]  vfs_write+0xa8/0x190
[   43.545222]  ksys_write+0x50/0xc0
[   43.545223]  do_syscall_64+0x51/0x1a0
[   43.545223]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
[   43.545223] RIP: 0033:0x7f544c306370
[   43.545224] RSP: 002b:00007ffc223bacb8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[   43.545225] RAX: ffffffffffffffda RBX: 0000000001ff0610 RCX: 00007f544c306370
[   43.545225] RDX: 0000000000000011 RSI: 0000000001ff0610 RDI: 0000000000000001
[   43.545225] RBP: 0000000000000011 R08: 41434f4c4c415f4b R09: 00007f544c5bce90
[   43.545226] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000001
[   43.545226] R13: 0000000000000011 R14: 7fffffffffffffff R15: 0000000000000000
[   43.545227] Code: 08 08 00 00 48 c7 c7 70 56 2d 82 5b 48 89 d1 e9 a4 48 01 00 66 0f 1f 84 00 00 00 00 00 41 57 41 56
89 d1 41 55 41 54 49 89 fd 55 <53> bb 01 00 00 00 d3 e3 48 89 f5 41 89 d4 48 83 ec 08 0f b7 46
[   43.545241] RIP: mark_lock+0xe/0x540 RSP: ffffc900009c0000
[   43.545241] ---[ end trace 63196de7418a092e ]---
[   43.545242] Kernel panic - not syncing: corrupted stack end detected inside scheduler
[   43.545242]


I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
Andy, can you give a clue?

I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
and x86_32. So I'm going to:
 - set MIN_STACK_LEFT to 2048;
 - improve the lkdtm test to cover this case.

Mark, Kees, Laura, does it sound good?

>>>> +EXPORT_SYMBOL(check_alloca);
>>>> +#endif
>>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>>> index a34e9290a699..25dd2a14560d 100644
>>>> --- a/drivers/firmware/efi/libstub/Makefile
>>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>>>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>>  				   -D__NO_FORTIFY \
>>>>  				   $(call cc-option,-ffreestanding) \
>>>> -				   $(call cc-option,-fno-stack-protector)
>>>> +				   $(call cc-option,-fno-stack-protector) \
>>>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>>>  
>>>>  GCOV_PROFILE			:= n
>>>>  KASAN_SANITIZE			:= n
>>>
>>> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
>>
>> Could you please give more details on that? Why STACKLEAK breaks it?
> 
> In the hyp/EL2 exception level, we only map the hyp text, and not the
> rest of the kernel. So erase_kstack and check_alloca won't be mapped,
> and attempt to branch to them will fault.

Here you mean track_stack() and not erase_kstack(), right?

> Even if it were mapped, things like BUG_ON(), get_current(), etc do not
> work at hyp.
> 
> Additionally, the hyp code is mapped as a different virtual address from
> the rest of the kernel, so if any of the STACKLEAK code happens to use
> an absolute address, this will not work correctly.

Thanks for the details. This quite old article [1] says:
  The code run in HYP mode is limited to a few hundred instructions and isolated
  to two assembly files: arch/arm/kvm/interrupts.S and arch/arm/kvm/interrupts_head.S.

Is all hyp code now localized in arch/arm64/kvm/hyp/?

[1]: https://lwn.net/Articles/557132/

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-06  8:22             ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-06  8:22 UTC (permalink / raw)
  To: linux-arm-kernel

On 04.05.2018 14:09, Mark Rutland wrote:
> On Thu, May 03, 2018 at 08:33:38PM +0300, Alexander Popov wrote:
>> Hello Mark and Laura,
>>
>> Let me join the discussion. Mark, thanks for your feedback!
>>
>> On 03.05.2018 10:19, Mark Rutland wrote:
>>> Hi Laura,
>>>
>>> On Wed, May 02, 2018 at 01:33:26PM -0700, Laura Abbott wrote:
>>>>
>>>> Implementation of stackleak based heavily on the x86 version
>>>>
>>>> Signed-off-by: Laura Abbott <labbott@redhat.com>
>>>> ---
>>>> Now written in C instead of a bunch of assembly.
>>>
>>> This looks neat!
>>>
>>> I have a few minor comments below.
>>>
>>>> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>>>> index bf825f38d206..0ceea613c65b 100644
>>>> --- a/arch/arm64/kernel/Makefile
>>>> +++ b/arch/arm64/kernel/Makefile
>>>> @@ -55,6 +55,9 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
>>>>  arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
>>>>  arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
>>>>  
>>>> +arm64-obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += erase.o
>>>> +KASAN_SANITIZE_erase.o	:= n
>>>
>>> I suspect we want to avoid the full set of instrumentation suspects here, e.g.
>>> GKOV, KASAN, UBSAN, and KCOV.
>>
>> I've disabled KASAN instrumentation for that file on x86 because erase_kstack()
>> intentionally writes to the stack and causes KASAN false positive reports.
>>
>> But I didn't see any conflicts with other types of instrumentation that you
>> mentioned.
> 
> The rationale is that any of these can result in implicit calls to C
> functions at arbitrary points during erase_kstack(). That could
> interfere with the search for poison, and/or leave data on the stack
> which is not erased.
> 
> They won't result in hard failures, as KASAN would, but we should
> probably avoid them regardless.

Thanks, Mark! Agree about KCOV, I'll switch it off for that file.

And I think I should _not_ disable UBSAN for that file. I didn't make any
intentional UB, so if UBSAN finds anything, that will be a true positive report.

> [...]
> 
>>>> +asmlinkage void erase_kstack(void)
>>>> +{
>>>> +	unsigned long p = current->thread.lowest_stack;
>>>> +	unsigned long boundary = p & ~(THREAD_SIZE - 1);
>>>> +	unsigned long poison = 0;
>>>> +	const unsigned long check_depth = STACKLEAK_POISON_CHECK_DEPTH /
>>>> +							sizeof(unsigned long);
>>>> +
>>>> +	/*
>>>> +	 * Let's search for the poison value in the stack.
>>>> +	 * Start from the lowest_stack and go to the bottom.
>>>> +	 */
>>>> +	while (p > boundary && poison <= check_depth) {
>>>> +		if (*(unsigned long *)p == STACKLEAK_POISON)
>>>> +			poison++;
>>>> +		else
>>>> +			poison = 0;
>>>> +
>>>> +		p -= sizeof(unsigned long);
>>>> +	}
>>>> +
>>>> +	/*
>>>> +	 * One long int at the bottom of the thread stack is reserved and
>>>> +	 * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK).
>>>> +	 */
>>>> +	if (p == boundary)
>>>> +		p += sizeof(unsigned long);
>>>
>>> I wonder if end_of_stack() should be taught about CONFIG_SCHED_STACK_END_CHECK,
>>> given that's supposed to return the last *usable* long on the stack, and we
>>> don't account for this elsewhere.
>>
>> I would be afraid to change the meaning of end_of_stack()... Currently it
>> considers that magic long as usable (include/linux/sched/task_stack.h):
>>
>> #define task_stack_end_corrupted(task) \
>> 		(*(end_of_stack(task)) != STACK_END_MAGIC)
>>
>>
>>> If we did, then IIUC we could do:
>>>
>>> 	unsigned long boundary = (unsigned long)end_of_stack(current);
>>>
>>> ... at the start of the function, and not have to worry about this explicitly.
>>
>> I should mention that erase_kstack() can be called from x86 trampoline stack.
>> That's why the boundary is calculated from the lowest_stack.
> 
> Ok. Under what circumstances does that happen?
> 
> It seems a little scary that curent::thread::lowest_stack might not be
> on current's task stack. 

Yes, indeed. That's why I check against that, please see BUG_ON() in
erase_kstack() for x86.

1. Calculate the boundary from the lowest_stack.
2. Search for poison between lowest_stack and boundary.
3. Now ready to write the poison.
4. Reset the boundary to current_stack_pointer if we are on the thread stack and
to current_top_of_stack otherwise (we are on the trampoline stack).
5. BUG_ON(boundary - p >= THREAD_SIZE);
6. Write poison till the boundary.

> Is that reset when transitioning to/from the
> trampoile stack?

We switch to the trampoline stack from the current thread stack just before
returning to the userspace. Please search for "trampoline stack" in
arch/x86/entry/entry_64.S.

> [...]
> 
>>>> +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>>>> +void __used check_alloca(unsigned long size)
>>>> +{
>>>> +	unsigned long sp, stack_left;
>>>> +
>>>> +	sp = current_stack_pointer;
>>>> +
>>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>>> +}
>>>
>>> Is this arbitrary, or is there something special about 256?
>>>
>>> Even if this is arbitrary, can we give it some mnemonic?
>>
>> It's just a reasonable number. We can introduce a macro for it.
> 
> I'm just not sure I see the point in the offset, given things like
> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
> bytes of stack, so it seems superfluous, as we'd be relying on stack
> overflow detection at that point.
> 
> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
> into account, though.

Mark, thank you for such an important remark!

In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
doesn't have VMAP_STACK at all but can have STACKLEAK.

[Adding Andy Lutomirski]

I've made some additional experiments: I exhaust the thread stack to have only
(MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
disabled, BUG_ON() handling causes stack depth overflow, which is detected by
SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:

[   43.543962] lkdtm: try a large alloca of 14647 bytes (sp 18446683600580263344)...
[   43.545188] BUG: stack guard page was hit at 00000000830608b8 (stack is 000000009375e943..00000000cb7f52d9)
[   43.545189] kernel stack overflow (double-fault): 0000 [#1] SMP PTI
[   43.545189] Dumping ftrace buffer:
[   43.545190]    (ftrace buffer empty)
[   43.545190] Modules linked in: lkdtm
[   43.545192] CPU: 0 PID: 2682 Comm: sh Not tainted 4.17.0-rc3+ #23
[   43.545192] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[   43.545193] RIP: 0010:mark_lock+0xe/0x540
[   43.545193] RSP: 0018:ffffc900009c0000 EFLAGS: 00010002
[   43.545194] RAX: 000000000000000c RBX: ffff880079b3b590 RCX: 0000000000000008
[   43.545194] RDX: 0000000000000008 RSI: ffff880079b3b590 RDI: ffff880079b3ad40
[   43.545195] RBP: ffffc900009c0100 R08: 0000000000000002 R09: 0000000000000000
[   43.545195] R10: ffffc900009c0118 R11: 0000000000000000 R12: 0000000000000000
[   43.545196] R13: ffff880079b3ad40 R14: ffff880079b3ad40 R15: ffffffff810cb8d7
[   43.545196] FS:  00007f544c7d8700(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000
[   43.545197] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   43.545200] CR2: ffffc900009bfff8 CR3: 0000000079194000 CR4: 00000000000006f0
[   43.545200] Call Trace:
[   43.545201]  ? vprintk_emit+0x67/0x440
[   43.545201]  __lock_acquire+0x2e0/0x13e0
[   43.545201]  ? lock_acquire+0x9d/0x1e0
[   43.545202]  lock_acquire+0x9d/0x1e0
[   43.545202]  ? vprintk_emit+0x67/0x440
[   43.545203]  _raw_spin_lock+0x20/0x30
[   43.545203]  ? vprintk_emit+0x67/0x440
[   43.545203]  vprintk_emit+0x67/0x440
[   43.545204]  ? check_alloca+0x9a/0xb0
[   43.545204]  printk+0x50/0x6f
[   43.545204]  ? __probe_kernel_read+0x34/0x60
[   43.545205]  ? check_alloca+0x9a/0xb0
[   43.545205]  report_bug+0xd3/0x110
[   43.545206]  fixup_bug.part.10+0x13/0x30
[   43.545206]  do_error_trap+0x158/0x190
[   43.545206]  ? trace_hardirqs_off_thunk+0x1a/0x1c
[   43.545207]  invalid_op+0x14/0x20
[   43.545207] RIP: 0010:check_alloca+0x9a/0xb0
[   43.545207] RSP: 0018:ffffc900009c0408 EFLAGS: 00010287
[   43.545208] RAX: 0000000000000008 RBX: 0000000000003936 RCX: 0000000000000001
[   43.545209] RDX: 0000000000000002 RSI: 0000000000000000 RDI: ffffc900009c0408
[   43.545209] RBP: ffffc900009c3da0 R08: 0000000000000000 R09: 0000000000000000
[   43.545210] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000003936
[   43.545210] R13: 0000000001ff0610 R14: 0000000000000011 R15: ffffc900009c3f08
[   43.545210]  ? check_alloca+0x64/0xb0
[   43.545211]  do_alloca+0x55/0x71b [lkdtm]
[   43.545211]  ? noop_count+0x10/0x10
[   43.545211]  ? check_usage+0xb1/0x4d0
[   43.545212]  ? noop_count+0x10/0x10
[   43.545212]  ? check_usage+0xb1/0x4d0
[   43.545213]  ? serial8250_console_write+0x253/0x2b0
[   43.545213]  ? serial8250_console_write+0x253/0x2b0
[   43.545213]  ? __lock_acquire+0x2e0/0x13e0
[   43.545214]  ? up+0xd/0x50
[   43.545214]  ? console_unlock+0x374/0x660
[   43.545215]  ? __lock_acquire+0x2e0/0x13e0
[   43.545215]  ? retint_kernel+0x10/0x10
[   43.545215]  ? trace_hardirqs_on_caller+0xed/0x180
[   43.545216]  ? find_held_lock+0x2d/0x90
[   43.545216]  ? mark_held_locks+0x4e/0x80
[   43.545216]  ? console_unlock+0x471/0x660
[   43.545217]  ? trace_hardirqs_on_caller+0xed/0x180
[   43.545217]  ? vprintk_emit+0x235/0x440
[   43.545218]  ? get_stack_info+0x32/0x160
[   43.545218]  ? check_alloca+0x64/0xb0
[   43.545218]  ? do_alloca+0x1f/0x71b [lkdtm]
[   43.545219]  lkdtm_STACKLEAK_ALLOCA+0x8f/0xb0 [lkdtm]
[   43.545219]  direct_entry+0xc5/0x110 [lkdtm]
[   43.545220]  full_proxy_write+0x51/0x80
[   43.545220]  __vfs_write+0x49/0x180
[   43.545220]  ? rcu_read_lock_sched_held+0x53/0x60
[   43.545221]  ? rcu_sync_lockdep_assert+0x29/0x50
[   43.545221]  ? __sb_start_write+0x110/0x160
[   43.545221]  ? vfs_write+0x172/0x190
[   43.545222]  vfs_write+0xa8/0x190
[   43.545222]  ksys_write+0x50/0xc0
[   43.545223]  do_syscall_64+0x51/0x1a0
[   43.545223]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
[   43.545223] RIP: 0033:0x7f544c306370
[   43.545224] RSP: 002b:00007ffc223bacb8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[   43.545225] RAX: ffffffffffffffda RBX: 0000000001ff0610 RCX: 00007f544c306370
[   43.545225] RDX: 0000000000000011 RSI: 0000000001ff0610 RDI: 0000000000000001
[   43.545225] RBP: 0000000000000011 R08: 41434f4c4c415f4b R09: 00007f544c5bce90
[   43.545226] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000001
[   43.545226] R13: 0000000000000011 R14: 7fffffffffffffff R15: 0000000000000000
[   43.545227] Code: 08 08 00 00 48 c7 c7 70 56 2d 82 5b 48 89 d1 e9 a4 48 01 00 66 0f 1f 84 00 00 00 00 00 41 57 41 56
89 d1 41 55 41 54 49 89 fd 55 <53> bb 01 00 00 00 d3 e3 48 89 f5 41 89 d4 48 83 ec 08 0f b7 46
[   43.545241] RIP: mark_lock+0xe/0x540 RSP: ffffc900009c0000
[   43.545241] ---[ end trace 63196de7418a092e ]---
[   43.545242] Kernel panic - not syncing: corrupted stack end detected inside scheduler
[   43.545242]


I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
Andy, can you give a clue?

I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
and x86_32. So I'm going to:
 - set MIN_STACK_LEFT to 2048;
 - improve the lkdtm test to cover this case.

Mark, Kees, Laura, does it sound good?

>>>> +EXPORT_SYMBOL(check_alloca);
>>>> +#endif
>>>> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
>>>> index a34e9290a699..25dd2a14560d 100644
>>>> --- a/drivers/firmware/efi/libstub/Makefile
>>>> +++ b/drivers/firmware/efi/libstub/Makefile
>>>> @@ -20,7 +20,8 @@ cflags-$(CONFIG_EFI_ARMSTUB)	+= -I$(srctree)/scripts/dtc/libfdt
>>>>  KBUILD_CFLAGS			:= $(cflags-y) -DDISABLE_BRANCH_PROFILING \
>>>>  				   -D__NO_FORTIFY \
>>>>  				   $(call cc-option,-ffreestanding) \
>>>> -				   $(call cc-option,-fno-stack-protector)
>>>> +				   $(call cc-option,-fno-stack-protector) \
>>>> +				   $(DISABLE_STACKLEAK_PLUGIN)
>>>>  
>>>>  GCOV_PROFILE			:= n
>>>>  KASAN_SANITIZE			:= n
>>>
>>> I believe we'll also need to do this for the KVM hyp code in arch/arm64/kvm/hyp/.
>>
>> Could you please give more details on that? Why STACKLEAK breaks it?
> 
> In the hyp/EL2 exception level, we only map the hyp text, and not the
> rest of the kernel. So erase_kstack and check_alloca won't be mapped,
> and attempt to branch to them will fault.

Here you mean track_stack() and not erase_kstack(), right?

> Even if it were mapped, things like BUG_ON(), get_current(), etc do not
> work at hyp.
> 
> Additionally, the hyp code is mapped as a different virtual address from
> the rest of the kernel, so if any of the STACKLEAK code happens to use
> an absolute address, this will not work correctly.

Thanks for the details. This quite old article [1] says:
  The code run in HYP mode is limited to a few hundred instructions and isolated
  to two assembly files: arch/arm/kvm/interrupts.S and arch/arm/kvm/interrupts_head.S.

Is all hyp code now localized in arch/arm64/kvm/hyp/?

[1]: https://lwn.net/Articles/557132/

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
  2018-05-02 21:02               ` Kees Cook
@ 2018-05-06 10:04                 ` Thomas Gleixner
  0 siblings, 0 replies; 70+ messages in thread
From: Thomas Gleixner @ 2018-05-06 10:04 UTC (permalink / raw)
  To: Kees Cook
  Cc: Dave Hansen, Linus Torvalds, Alexander Popov, Kernel Hardening,
	PaX Team, Brad Spengler, Ingo Molnar, Andy Lutomirski,
	Tycho Andersen, Laura Abbott, Mark Rutland, Ard Biesheuvel,
	Borislav Petkov, Richard Sandiford, H . Peter Anvin,
	Peter Zijlstra, Dmitry V . Levin, Emese Revfy, Jonathan Corbet,
	Andrey Ryabinin, Kirill A . Shutemov, Thomas Garnier,
	Andrew Morton, Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Greg Kroah-Hartman,
	Dan Williams, Mathias Krause, Vikas Shivappa, Kyle Huey,
	Dmitry Safonov, Will Deacon, Arnd Bergmann, Florian Weimer,
	Boris Lukashev, X86 ML, LKML

On Wed, 2 May 2018, Kees Cook wrote:
> On Wed, May 2, 2018 at 5:51 AM, Kees Cook <keescook@chromium.org> wrote:
> I think it'd be better for this to go via my tree with your Ack (where
> I can carry the plugin, lkdtm, x86, and arm64 changes). How does that
> sound?

Works for me.

Acked-by: Thomas Gleixner <tglx@linutronix.de>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-06  8:22             ` Alexander Popov
@ 2018-05-11 15:50               ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-11 15:50 UTC (permalink / raw)
  To: Mark Rutland, Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel
  Cc: kernel-hardening, linux-arm-kernel, linux-kernel

Hello everyone,

On 06.05.2018 11:22, Alexander Popov wrote:
> On 04.05.2018 14:09, Mark Rutland wrote:
>>>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>>>
>>>> Is this arbitrary, or is there something special about 256?
>>>>
>>>> Even if this is arbitrary, can we give it some mnemonic?
>>>
>>> It's just a reasonable number. We can introduce a macro for it.
>>
>> I'm just not sure I see the point in the offset, given things like
>> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
>> bytes of stack, so it seems superfluous, as we'd be relying on stack
>> overflow detection at that point.
>>
>> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
>> into account, though.
> 
> Mark, thank you for such an important remark!
> 
> In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
> doesn't have VMAP_STACK at all but can have STACKLEAK.
> 
> [Adding Andy Lutomirski]
> 
> I've made some additional experiments: I exhaust the thread stack to have only
> (MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
> disabled, BUG_ON() handling causes stack depth overflow, which is detected by
> SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
> handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:

[...]

> I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
> Andy, can you give a clue?
> 
> I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
> and x86_32. So I'm going to:
>  - set MIN_STACK_LEFT to 2048;
>  - improve the lkdtm test to cover this case.
> 
> Mark, Kees, Laura, does it sound good?


Could you have a look at the following changes in check_alloca() before I send
the next version?

If VMAP_STACK is enabled and alloca causes stack depth overflow, I write to
guard page below the thread stack to cause double fault and VMAP_STACK report.

If VMAP_STACK is disabled, I use MIN_STACK_LEFT = 2048, which seems to be enough
for BUG_ON() handling both on x86_32 and x86_64. Unfortunately, I can't
guarantee that it is always enough.


 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
-#define MIN_STACK_LEFT 256
+#define MIN_STACK_LEFT 2048

 void __used check_alloca(unsigned long size)
 {
        unsigned long sp = (unsigned long)&sp;
        struct stack_info stack_info = {0};
        unsigned long visit_mask = 0;
        unsigned long stack_left;

        BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask));

        stack_left = sp - (unsigned long)stack_info.begin;
+
+#ifdef CONFIG_VMAP_STACK
+       /*
+        * If alloca oversteps the thread stack boundary, we touch the guard
+        * page provided by VMAP_STACK to trigger handle_stack_overflow().
+        */
+       if (size >= stack_left)
+               *(stack_info.begin - 1) = 42;
+#else
        BUG_ON(stack_left < MIN_STACK_LEFT ||
                                size >= stack_left - MIN_STACK_LEFT);
+#endif
 }
 EXPORT_SYMBOL(check_alloca);
 #endif


Looking forward to your feedback.

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-11 15:50               ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-11 15:50 UTC (permalink / raw)
  To: linux-arm-kernel

Hello everyone,

On 06.05.2018 11:22, Alexander Popov wrote:
> On 04.05.2018 14:09, Mark Rutland wrote:
>>>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>>>
>>>> Is this arbitrary, or is there something special about 256?
>>>>
>>>> Even if this is arbitrary, can we give it some mnemonic?
>>>
>>> It's just a reasonable number. We can introduce a macro for it.
>>
>> I'm just not sure I see the point in the offset, given things like
>> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
>> bytes of stack, so it seems superfluous, as we'd be relying on stack
>> overflow detection at that point.
>>
>> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
>> into account, though.
> 
> Mark, thank you for such an important remark!
> 
> In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
> doesn't have VMAP_STACK at all but can have STACKLEAK.
> 
> [Adding Andy Lutomirski]
> 
> I've made some additional experiments: I exhaust the thread stack to have only
> (MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
> disabled, BUG_ON() handling causes stack depth overflow, which is detected by
> SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
> handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:

[...]

> I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
> Andy, can you give a clue?
> 
> I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
> and x86_32. So I'm going to:
>  - set MIN_STACK_LEFT to 2048;
>  - improve the lkdtm test to cover this case.
> 
> Mark, Kees, Laura, does it sound good?


Could you have a look at the following changes in check_alloca() before I send
the next version?

If VMAP_STACK is enabled and alloca causes stack depth overflow, I write to
guard page below the thread stack to cause double fault and VMAP_STACK report.

If VMAP_STACK is disabled, I use MIN_STACK_LEFT = 2048, which seems to be enough
for BUG_ON() handling both on x86_32 and x86_64. Unfortunately, I can't
guarantee that it is always enough.


 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
-#define MIN_STACK_LEFT 256
+#define MIN_STACK_LEFT 2048

 void __used check_alloca(unsigned long size)
 {
        unsigned long sp = (unsigned long)&sp;
        struct stack_info stack_info = {0};
        unsigned long visit_mask = 0;
        unsigned long stack_left;

        BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask));

        stack_left = sp - (unsigned long)stack_info.begin;
+
+#ifdef CONFIG_VMAP_STACK
+       /*
+        * If alloca oversteps the thread stack boundary, we touch the guard
+        * page provided by VMAP_STACK to trigger handle_stack_overflow().
+        */
+       if (size >= stack_left)
+               *(stack_info.begin - 1) = 42;
+#else
        BUG_ON(stack_left < MIN_STACK_LEFT ||
                                size >= stack_left - MIN_STACK_LEFT);
+#endif
 }
 EXPORT_SYMBOL(check_alloca);
 #endif


Looking forward to your feedback.

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-11 15:50               ` Alexander Popov
@ 2018-05-11 16:13                 ` Mark Rutland
  -1 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-11 16:13 UTC (permalink / raw)
  To: Alexander Popov
  Cc: Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel,
	kernel-hardening, linux-arm-kernel, linux-kernel

On Fri, May 11, 2018 at 06:50:09PM +0300, Alexander Popov wrote:
> Hello everyone,
> 
> On 06.05.2018 11:22, Alexander Popov wrote:
> > On 04.05.2018 14:09, Mark Rutland wrote:
> >>>>> +	stack_left = sp & (THREAD_SIZE - 1);
> >>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
> >>>>
> >>>> Is this arbitrary, or is there something special about 256?
> >>>>
> >>>> Even if this is arbitrary, can we give it some mnemonic?
> >>>
> >>> It's just a reasonable number. We can introduce a macro for it.
> >>
> >> I'm just not sure I see the point in the offset, given things like
> >> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
> >> bytes of stack, so it seems superfluous, as we'd be relying on stack
> >> overflow detection at that point.
> >>
> >> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
> >> into account, though.
> > 
> > Mark, thank you for such an important remark!
> > 
> > In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
> > doesn't have VMAP_STACK at all but can have STACKLEAK.
> > 
> > [Adding Andy Lutomirski]
> > 
> > I've made some additional experiments: I exhaust the thread stack to have only
> > (MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
> > disabled, BUG_ON() handling causes stack depth overflow, which is detected by
> > SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
> > handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:

I can't see why CONFIG_VMAP_STACK would only work in conjunction with
CONFIG_PROVE_LOCKING.

On arm64 at least, if we overflow the stack while handling a BUG(), we
*should* trigger the overflow handler as usual, and that should work,
unless I'm missing something.

Maybe it gets part-way into panic(), sets up some state,
stack-overflows, and we get wedged because we're already in a panic?
Perhaps CONFIG_PROVE_LOCKING causes more stack to be used, so it dies a
little earlier in panic(), before setting up some state that causes
wedging.

... which sounds like something best fixed in those code paths, and not
here.

> [...]
> 
> > I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
> > Andy, can you give a clue?
> > 
> > I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
> > and x86_32. So I'm going to:
> >  - set MIN_STACK_LEFT to 2048;
> >  - improve the lkdtm test to cover this case.
> > 
> > Mark, Kees, Laura, does it sound good?
> 
> 
> Could you have a look at the following changes in check_alloca() before I send
> the next version?
> 
> If VMAP_STACK is enabled and alloca causes stack depth overflow, I write to
> guard page below the thread stack to cause double fault and VMAP_STACK report.

On arm64 at least, writing to the guard page will not itself trigger a
stack overflow, but will trigger a data abort. I suspect similar is true
on x86, if the stack pointer is sufficiently far above the guard page.

> If VMAP_STACK is disabled, I use MIN_STACK_LEFT = 2048, which seems to be enough
> for BUG_ON() handling both on x86_32 and x86_64. Unfortunately, I can't
> guarantee that it is always enough.

I don't think that we can choose something that's guaranteed to be
sufficient for BUG() handling and also not wasting a tonne of space
under normal operation.

Let's figure out what's going wrong on x86 in the case that you mention,
and try to solve that.

Here I don't think we should reserve space at all -- it's completely
arbitrary, and as above we can't guarantee that it's sufficient anyway.

>  #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> -#define MIN_STACK_LEFT 256
> +#define MIN_STACK_LEFT 2048
> 
>  void __used check_alloca(unsigned long size)
>  {
>         unsigned long sp = (unsigned long)&sp;
>         struct stack_info stack_info = {0};
>         unsigned long visit_mask = 0;
>         unsigned long stack_left;
> 
>         BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask));
> 
>         stack_left = sp - (unsigned long)stack_info.begin;
> +
> +#ifdef CONFIG_VMAP_STACK
> +       /*
> +        * If alloca oversteps the thread stack boundary, we touch the guard
> +        * page provided by VMAP_STACK to trigger handle_stack_overflow().
> +        */
> +       if (size >= stack_left)
> +               *(stack_info.begin - 1) = 42;
> +#else

On arm64, this won't trigger our stack overflow handler, unless the SP
is already very close to the boundary.

Please just use BUG(). If there is an issue on x86, it would be good to
solve that in the x86 code.

>         BUG_ON(stack_left < MIN_STACK_LEFT ||
>                                 size >= stack_left - MIN_STACK_LEFT);

I really don't think we should bother with this arbitrary offset at all.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-11 16:13                 ` Mark Rutland
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-11 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 11, 2018 at 06:50:09PM +0300, Alexander Popov wrote:
> Hello everyone,
> 
> On 06.05.2018 11:22, Alexander Popov wrote:
> > On 04.05.2018 14:09, Mark Rutland wrote:
> >>>>> +	stack_left = sp & (THREAD_SIZE - 1);
> >>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
> >>>>
> >>>> Is this arbitrary, or is there something special about 256?
> >>>>
> >>>> Even if this is arbitrary, can we give it some mnemonic?
> >>>
> >>> It's just a reasonable number. We can introduce a macro for it.
> >>
> >> I'm just not sure I see the point in the offset, given things like
> >> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
> >> bytes of stack, so it seems superfluous, as we'd be relying on stack
> >> overflow detection at that point.
> >>
> >> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
> >> into account, though.
> > 
> > Mark, thank you for such an important remark!
> > 
> > In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
> > doesn't have VMAP_STACK at all but can have STACKLEAK.
> > 
> > [Adding Andy Lutomirski]
> > 
> > I've made some additional experiments: I exhaust the thread stack to have only
> > (MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
> > disabled, BUG_ON() handling causes stack depth overflow, which is detected by
> > SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
> > handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:

I can't see why CONFIG_VMAP_STACK would only work in conjunction with
CONFIG_PROVE_LOCKING.

On arm64 at least, if we overflow the stack while handling a BUG(), we
*should* trigger the overflow handler as usual, and that should work,
unless I'm missing something.

Maybe it gets part-way into panic(), sets up some state,
stack-overflows, and we get wedged because we're already in a panic?
Perhaps CONFIG_PROVE_LOCKING causes more stack to be used, so it dies a
little earlier in panic(), before setting up some state that causes
wedging.

... which sounds like something best fixed in those code paths, and not
here.

> [...]
> 
> > I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
> > Andy, can you give a clue?
> > 
> > I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
> > and x86_32. So I'm going to:
> >  - set MIN_STACK_LEFT to 2048;
> >  - improve the lkdtm test to cover this case.
> > 
> > Mark, Kees, Laura, does it sound good?
> 
> 
> Could you have a look at the following changes in check_alloca() before I send
> the next version?
> 
> If VMAP_STACK is enabled and alloca causes stack depth overflow, I write to
> guard page below the thread stack to cause double fault and VMAP_STACK report.

On arm64 at least, writing to the guard page will not itself trigger a
stack overflow, but will trigger a data abort. I suspect similar is true
on x86, if the stack pointer is sufficiently far above the guard page.

> If VMAP_STACK is disabled, I use MIN_STACK_LEFT = 2048, which seems to be enough
> for BUG_ON() handling both on x86_32 and x86_64. Unfortunately, I can't
> guarantee that it is always enough.

I don't think that we can choose something that's guaranteed to be
sufficient for BUG() handling and also not wasting a tonne of space
under normal operation.

Let's figure out what's going wrong on x86 in the case that you mention,
and try to solve that.

Here I don't think we should reserve space at all -- it's completely
arbitrary, and as above we can't guarantee that it's sufficient anyway.

>  #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
> -#define MIN_STACK_LEFT 256
> +#define MIN_STACK_LEFT 2048
> 
>  void __used check_alloca(unsigned long size)
>  {
>         unsigned long sp = (unsigned long)&sp;
>         struct stack_info stack_info = {0};
>         unsigned long visit_mask = 0;
>         unsigned long stack_left;
> 
>         BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask));
> 
>         stack_left = sp - (unsigned long)stack_info.begin;
> +
> +#ifdef CONFIG_VMAP_STACK
> +       /*
> +        * If alloca oversteps the thread stack boundary, we touch the guard
> +        * page provided by VMAP_STACK to trigger handle_stack_overflow().
> +        */
> +       if (size >= stack_left)
> +               *(stack_info.begin - 1) = 42;
> +#else

On arm64, this won't trigger our stack overflow handler, unless the SP
is already very close to the boundary.

Please just use BUG(). If there is an issue on x86, it would be good to
solve that in the x86 code.

>         BUG_ON(stack_left < MIN_STACK_LEFT ||
>                                 size >= stack_left - MIN_STACK_LEFT);

I really don't think we should bother with this arbitrary offset at all.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
  2018-05-11 16:13                 ` Mark Rutland
@ 2018-05-13  8:40                   ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-13  8:40 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel,
	kernel-hardening, linux-arm-kernel, linux-kernel

Hello Mark,

Thanks a lot for your reply!

On 11.05.2018 19:13, Mark Rutland wrote:
> On Fri, May 11, 2018 at 06:50:09PM +0300, Alexander Popov wrote:
>> On 06.05.2018 11:22, Alexander Popov wrote:
>>> On 04.05.2018 14:09, Mark Rutland wrote:
>>>>>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>>>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>>>>>
>>>>>> Is this arbitrary, or is there something special about 256?
>>>>>>
>>>>>> Even if this is arbitrary, can we give it some mnemonic?
>>>>>
>>>>> It's just a reasonable number. We can introduce a macro for it.
>>>>
>>>> I'm just not sure I see the point in the offset, given things like
>>>> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
>>>> bytes of stack, so it seems superfluous, as we'd be relying on stack
>>>> overflow detection at that point.
>>>>
>>>> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
>>>> into account, though.
>>>
>>> Mark, thank you for such an important remark!
>>>
>>> In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
>>> doesn't have VMAP_STACK at all but can have STACKLEAK.
>>>
>>> [Adding Andy Lutomirski]
>>>
>>> I've made some additional experiments: I exhaust the thread stack to have only
>>> (MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
>>> disabled, BUG_ON() handling causes stack depth overflow, which is detected by
>>> SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
>>> handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:
> 
> I can't see why CONFIG_VMAP_STACK would only work in conjunction with
> CONFIG_PROVE_LOCKING.
> 
> On arm64 at least, if we overflow the stack while handling a BUG(), we
> *should* trigger the overflow handler as usual, and that should work,
> unless I'm missing something.
> 
> Maybe it gets part-way into panic(), sets up some state,
> stack-overflows, and we get wedged because we're already in a panic?
> Perhaps CONFIG_PROVE_LOCKING causes more stack to be used, so it dies a
> little earlier in panic(), before setting up some state that causes
> wedging.

That seems likely. I later noticed that I had oops=panic kernel parameter.

> ... which sounds like something best fixed in those code paths, and not
> here.
> 
>> [...]
>>
>>> I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
>>> Andy, can you give a clue?
>>>
>>> I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
>>> and x86_32. So I'm going to:
>>>  - set MIN_STACK_LEFT to 2048;
>>>  - improve the lkdtm test to cover this case.
>>>
>>> Mark, Kees, Laura, does it sound good?
>>
>>
>> Could you have a look at the following changes in check_alloca() before I send
>> the next version?
>>
>> If VMAP_STACK is enabled and alloca causes stack depth overflow, I write to
>> guard page below the thread stack to cause double fault and VMAP_STACK report.
> 
> On arm64 at least, writing to the guard page will not itself trigger a
> stack overflow, but will trigger a data abort. I suspect similar is true
> on x86, if the stack pointer is sufficiently far above the guard page.

Yes, you are right, my mistake.

The comment about CONFIG_VMAP_STACK in arch/x86/kernel/traps.c says:
"If we overflow the stack into a guard page, the CPU will fail to deliver #PF
and will send #DF instead."

>> If VMAP_STACK is disabled, I use MIN_STACK_LEFT = 2048, which seems to be enough
>> for BUG_ON() handling both on x86_32 and x86_64. Unfortunately, I can't
>> guarantee that it is always enough.
> 
> I don't think that we can choose something that's guaranteed to be
> sufficient for BUG() handling and also not wasting a tonne of space
> under normal operation.
> 
> Let's figure out what's going wrong on x86 in the case that you mention,
> and try to solve that.
> 
> Here I don't think we should reserve space at all -- it's completely
> arbitrary, and as above we can't guarantee that it's sufficient anyway.
> 
>>  #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> -#define MIN_STACK_LEFT 256
>> +#define MIN_STACK_LEFT 2048
>>
>>  void __used check_alloca(unsigned long size)
>>  {
>>         unsigned long sp = (unsigned long)&sp;
>>         struct stack_info stack_info = {0};
>>         unsigned long visit_mask = 0;
>>         unsigned long stack_left;
>>
>>         BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask));
>>
>>         stack_left = sp - (unsigned long)stack_info.begin;
>> +
>> +#ifdef CONFIG_VMAP_STACK
>> +       /*
>> +        * If alloca oversteps the thread stack boundary, we touch the guard
>> +        * page provided by VMAP_STACK to trigger handle_stack_overflow().
>> +        */
>> +       if (size >= stack_left)
>> +               *(stack_info.begin - 1) = 42;
>> +#else
> 
> On arm64, this won't trigger our stack overflow handler, unless the SP
> is already very close to the boundary.
> 
> Please just use BUG(). If there is an issue on x86, it would be good to
> solve that in the x86 code.
> 
>>         BUG_ON(stack_left < MIN_STACK_LEFT ||
>>                                 size >= stack_left - MIN_STACK_LEFT);
> 
> I really don't think we should bother with this arbitrary offset at all.

Thanks. I agree with all your points.

I wrote a third lkdtm test for STACKLEAK which runs deep recursion with alloca.
If I have just BUG_ON(size >= stack_left) in check_alloca(), I get the following
nice report without any trouble:

[    8.407261] lkdtm: Performing direct entry STACKLEAK_RECURSION_WITH_ALLOCA
[    8.408641] lkdtm: checking unused part of the thread stack (15744 bytes)...
[    8.409936] lkdtm: first 744 bytes are unpoisoned
[    8.410751] lkdtm: the rest of the thread stack is properly erased
[    8.411760] lkdtm: try to overflow the thread stack using recursion & alloca
[    8.412914] BUG: stack guard page was hit at 00000000b993c2bc (stack is 00000000764adcd4..000000005b443f11)
[    8.414471] kernel stack overflow (double-fault): 0000 [#1] SMP PTI
[    8.415409] Dumping ftrace buffer:
[    8.415907]    (ftrace buffer empty)
[    8.416404] Modules linked in: lkdtm
[    8.416905] CPU: 0 PID: 2664 Comm: sh Not tainted 4.17.0-rc3+ #39
[    8.417766] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[    8.419088] RIP: 0010:do_error_trap+0x31/0x130
[    8.419647] RSP: 0018:ffffc900009b3fc0 EFLAGS: 00010046
[    8.420263] RAX: 0000000000000000 RBX: ffffc900009b4078 RCX: 0000000000000006
[    8.421322] RDX: ffffffff81fdbe4d RSI: 0000000000000000 RDI: ffffc900009b4078
[    8.422837] RBP: 0000000000000006 R08: 0000000000000004 R09: 0000000000000000
[    8.425095] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000004
[    8.427365] R13: ffffffff81fdbe4d R14: 0000000000000000 R15: 0000000000000000
[    8.430111] FS:  00007f7c340c1700(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000
[    8.432515] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    8.433132] CR2: ffffc900009b3fb8 CR3: 000000007b330000 CR4: 00000000000006f0
[    8.433904] Call Trace:
[    8.434180]  invalid_op+0x14/0x20
[    8.434546] RIP: 0010:check_alloca+0x8e/0xa0
[    8.434995] RSP: 0018:ffffc900009b4128 EFLAGS: 00010283
[    8.435555] RAX: 0000000000000128 RBX: 0000000000000190 RCX: 0000000000000001
[    8.436479] RDX: 0000000000000002 RSI: 0000000000000000 RDI: ffffc900009b4128
[    8.437871] RBP: ffffc900009b4180 R08: 000000000000018f R09: 0000000000000007
[    8.438661] R10: 0000000000000000 R11: 0000000000000030 R12: ffff88007a626000
[    8.439433] R13: 0000000001cf5610 R14: 0000000000000020 R15: ffffc900009b7f08
[    8.440329]  ? check_alloca+0x64/0xa0
[    8.440845]  do_alloca+0x20/0x60 [lkdtm]
[    8.441937]  recursion+0xa0/0xd0 [lkdtm]
[    8.443370]  ? vsnprintf+0xf2/0x4b0
[    8.444289]  ? get_stack_info+0x32/0x160
[    8.445359]  ? check_alloca+0x64/0xa0
[    8.445995]  ? do_alloca+0x20/0x60 [lkdtm]
[    8.446449]  recursion+0xbb/0xd0 [lkdtm]
[    8.446881]  ? vsnprintf+0xf2/0x4b0
[    8.447259]  ? get_stack_info+0x32/0x160
[    8.447693]  ? check_alloca+0x64/0xa0
[    8.448088]  ? do_alloca+0x20/0x60 [lkdtm]
[    8.448539]  recursion+0xbb/0xd0 [lkdtm]
...

It seems that previously I was very "lucky" to accidentally have those MIN_STACK_LEFT,
call trace depth and oops=panic together to experience a hang on stack overflow
during BUG().


When I run my test in a loop _without_ VMAP_STACK, I manage to corrupt the neighbour
processes with BUG() handling overstepping the stack boundary. It's a pity, but
I have an idea.

In kernel/sched/core.c we already have:

#ifdef CONFIG_SCHED_STACK_END_CHECK
  	if (task_stack_end_corrupted(prev))
		panic("corrupted stack end detected inside scheduler\n");
#endif

So what would you think if I do the following in check_alloca():

	if (size >= stack_left) {
#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
		panic("alloca over the kernel stack boundary\n");
#else
		BUG();
#endif

I think that fits well to the CONFIG_SCHED_STACK_END_CHECK policy.

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-13  8:40                   ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-13  8:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

Thanks a lot for your reply!

On 11.05.2018 19:13, Mark Rutland wrote:
> On Fri, May 11, 2018 at 06:50:09PM +0300, Alexander Popov wrote:
>> On 06.05.2018 11:22, Alexander Popov wrote:
>>> On 04.05.2018 14:09, Mark Rutland wrote:
>>>>>>> +	stack_left = sp & (THREAD_SIZE - 1);
>>>>>>> +	BUG_ON(stack_left < 256 || size >= stack_left - 256);
>>>>>>
>>>>>> Is this arbitrary, or is there something special about 256?
>>>>>>
>>>>>> Even if this is arbitrary, can we give it some mnemonic?
>>>>>
>>>>> It's just a reasonable number. We can introduce a macro for it.
>>>>
>>>> I'm just not sure I see the point in the offset, given things like
>>>> VMAP_STACK exist. BUG_ON() handling will likely require *more* than 256
>>>> bytes of stack, so it seems superfluous, as we'd be relying on stack
>>>> overflow detection at that point.
>>>>
>>>> I can see that we should take the CONFIG_SCHED_STACK_END_CHECK offset
>>>> into account, though.
>>>
>>> Mark, thank you for such an important remark!
>>>
>>> In Kconfig STACKLEAK implies but doesn't depend on VMAP_STACK. In fact x86_32
>>> doesn't have VMAP_STACK at all but can have STACKLEAK.
>>>
>>> [Adding Andy Lutomirski]
>>>
>>> I've made some additional experiments: I exhaust the thread stack to have only
>>> (MIN_STACK_LEFT - 1) bytes left and then force alloca. If VMAP_STACK is
>>> disabled, BUG_ON() handling causes stack depth overflow, which is detected by
>>> SCHED_STACK_END_CHECK. If VMAP_STACK is enabled, the kernel hangs on BUG_ON()
>>> handling! Enabling CONFIG_PROVE_LOCKING gives the needed report from VMAP_STACK:
> 
> I can't see why CONFIG_VMAP_STACK would only work in conjunction with
> CONFIG_PROVE_LOCKING.
> 
> On arm64 at least, if we overflow the stack while handling a BUG(), we
> *should* trigger the overflow handler as usual, and that should work,
> unless I'm missing something.
> 
> Maybe it gets part-way into panic(), sets up some state,
> stack-overflows, and we get wedged because we're already in a panic?
> Perhaps CONFIG_PROVE_LOCKING causes more stack to be used, so it dies a
> little earlier in panic(), before setting up some state that causes
> wedging.

That seems likely. I later noticed that I had oops=panic kernel parameter.

> ... which sounds like something best fixed in those code paths, and not
> here.
> 
>> [...]
>>
>>> I can't say why VMAP_STACK report hangs during BUG_ON() handling on defconfig.
>>> Andy, can you give a clue?
>>>
>>> I see that MIN_STACK_LEFT = 2048 is enough for BUG_ON() handling on both x86_64
>>> and x86_32. So I'm going to:
>>>  - set MIN_STACK_LEFT to 2048;
>>>  - improve the lkdtm test to cover this case.
>>>
>>> Mark, Kees, Laura, does it sound good?
>>
>>
>> Could you have a look at the following changes in check_alloca() before I send
>> the next version?
>>
>> If VMAP_STACK is enabled and alloca causes stack depth overflow, I write to
>> guard page below the thread stack to cause double fault and VMAP_STACK report.
> 
> On arm64 at least, writing to the guard page will not itself trigger a
> stack overflow, but will trigger a data abort. I suspect similar is true
> on x86, if the stack pointer is sufficiently far above the guard page.

Yes, you are right, my mistake.

The comment about CONFIG_VMAP_STACK in arch/x86/kernel/traps.c says:
"If we overflow the stack into a guard page, the CPU will fail to deliver #PF
and will send #DF instead."

>> If VMAP_STACK is disabled, I use MIN_STACK_LEFT = 2048, which seems to be enough
>> for BUG_ON() handling both on x86_32 and x86_64. Unfortunately, I can't
>> guarantee that it is always enough.
> 
> I don't think that we can choose something that's guaranteed to be
> sufficient for BUG() handling and also not wasting a tonne of space
> under normal operation.
> 
> Let's figure out what's going wrong on x86 in the case that you mention,
> and try to solve that.
> 
> Here I don't think we should reserve space at all -- it's completely
> arbitrary, and as above we can't guarantee that it's sufficient anyway.
> 
>>  #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
>> -#define MIN_STACK_LEFT 256
>> +#define MIN_STACK_LEFT 2048
>>
>>  void __used check_alloca(unsigned long size)
>>  {
>>         unsigned long sp = (unsigned long)&sp;
>>         struct stack_info stack_info = {0};
>>         unsigned long visit_mask = 0;
>>         unsigned long stack_left;
>>
>>         BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask));
>>
>>         stack_left = sp - (unsigned long)stack_info.begin;
>> +
>> +#ifdef CONFIG_VMAP_STACK
>> +       /*
>> +        * If alloca oversteps the thread stack boundary, we touch the guard
>> +        * page provided by VMAP_STACK to trigger handle_stack_overflow().
>> +        */
>> +       if (size >= stack_left)
>> +               *(stack_info.begin - 1) = 42;
>> +#else
> 
> On arm64, this won't trigger our stack overflow handler, unless the SP
> is already very close to the boundary.
> 
> Please just use BUG(). If there is an issue on x86, it would be good to
> solve that in the x86 code.
> 
>>         BUG_ON(stack_left < MIN_STACK_LEFT ||
>>                                 size >= stack_left - MIN_STACK_LEFT);
> 
> I really don't think we should bother with this arbitrary offset at all.

Thanks. I agree with all your points.

I wrote a third lkdtm test for STACKLEAK which runs deep recursion with alloca.
If I have just BUG_ON(size >= stack_left) in check_alloca(), I get the following
nice report without any trouble:

[    8.407261] lkdtm: Performing direct entry STACKLEAK_RECURSION_WITH_ALLOCA
[    8.408641] lkdtm: checking unused part of the thread stack (15744 bytes)...
[    8.409936] lkdtm: first 744 bytes are unpoisoned
[    8.410751] lkdtm: the rest of the thread stack is properly erased
[    8.411760] lkdtm: try to overflow the thread stack using recursion & alloca
[    8.412914] BUG: stack guard page was hit at 00000000b993c2bc (stack is 00000000764adcd4..000000005b443f11)
[    8.414471] kernel stack overflow (double-fault): 0000 [#1] SMP PTI
[    8.415409] Dumping ftrace buffer:
[    8.415907]    (ftrace buffer empty)
[    8.416404] Modules linked in: lkdtm
[    8.416905] CPU: 0 PID: 2664 Comm: sh Not tainted 4.17.0-rc3+ #39
[    8.417766] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[    8.419088] RIP: 0010:do_error_trap+0x31/0x130
[    8.419647] RSP: 0018:ffffc900009b3fc0 EFLAGS: 00010046
[    8.420263] RAX: 0000000000000000 RBX: ffffc900009b4078 RCX: 0000000000000006
[    8.421322] RDX: ffffffff81fdbe4d RSI: 0000000000000000 RDI: ffffc900009b4078
[    8.422837] RBP: 0000000000000006 R08: 0000000000000004 R09: 0000000000000000
[    8.425095] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000004
[    8.427365] R13: ffffffff81fdbe4d R14: 0000000000000000 R15: 0000000000000000
[    8.430111] FS:  00007f7c340c1700(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000
[    8.432515] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    8.433132] CR2: ffffc900009b3fb8 CR3: 000000007b330000 CR4: 00000000000006f0
[    8.433904] Call Trace:
[    8.434180]  invalid_op+0x14/0x20
[    8.434546] RIP: 0010:check_alloca+0x8e/0xa0
[    8.434995] RSP: 0018:ffffc900009b4128 EFLAGS: 00010283
[    8.435555] RAX: 0000000000000128 RBX: 0000000000000190 RCX: 0000000000000001
[    8.436479] RDX: 0000000000000002 RSI: 0000000000000000 RDI: ffffc900009b4128
[    8.437871] RBP: ffffc900009b4180 R08: 000000000000018f R09: 0000000000000007
[    8.438661] R10: 0000000000000000 R11: 0000000000000030 R12: ffff88007a626000
[    8.439433] R13: 0000000001cf5610 R14: 0000000000000020 R15: ffffc900009b7f08
[    8.440329]  ? check_alloca+0x64/0xa0
[    8.440845]  do_alloca+0x20/0x60 [lkdtm]
[    8.441937]  recursion+0xa0/0xd0 [lkdtm]
[    8.443370]  ? vsnprintf+0xf2/0x4b0
[    8.444289]  ? get_stack_info+0x32/0x160
[    8.445359]  ? check_alloca+0x64/0xa0
[    8.445995]  ? do_alloca+0x20/0x60 [lkdtm]
[    8.446449]  recursion+0xbb/0xd0 [lkdtm]
[    8.446881]  ? vsnprintf+0xf2/0x4b0
[    8.447259]  ? get_stack_info+0x32/0x160
[    8.447693]  ? check_alloca+0x64/0xa0
[    8.448088]  ? do_alloca+0x20/0x60 [lkdtm]
[    8.448539]  recursion+0xbb/0xd0 [lkdtm]
...

It seems that previously I was very "lucky" to accidentally have those MIN_STACK_LEFT,
call trace depth and oops=panic together to experience a hang on stack overflow
during BUG().


When I run my test in a loop _without_ VMAP_STACK, I manage to corrupt the neighbour
processes with BUG() handling overstepping the stack boundary. It's a pity, but
I have an idea.

In kernel/sched/core.c we already have:

#ifdef CONFIG_SCHED_STACK_END_CHECK
  	if (task_stack_end_corrupted(prev))
		panic("corrupted stack end detected inside scheduler\n");
#endif

So what would you think if I do the following in check_alloca():

	if (size >= stack_left) {
#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
		panic("alloca over the kernel stack boundary\n");
#else
		BUG();
#endif

I think that fits well to the CONFIG_SCHED_STACK_END_CHECK policy.

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-13  8:40                   ` Alexander Popov
@ 2018-05-14  5:15                     ` Mark Rutland
  -1 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-14  5:15 UTC (permalink / raw)
  To: Alexander Popov
  Cc: Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel,
	kernel-hardening, linux-arm-kernel, linux-kernel

On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
> It seems that previously I was very "lucky" to accidentally have those MIN_STACK_LEFT,
> call trace depth and oops=panic together to experience a hang on stack overflow
> during BUG().
> 
> 
> When I run my test in a loop _without_ VMAP_STACK, I manage to corrupt the neighbour
> processes with BUG() handling overstepping the stack boundary. It's a pity, but
> I have an idea.

I think that in the absence of VMAP_STACK, there will always be cases where we
*could* corrupt a neighbouring stack, but I agree that trying to minimize that
possibility would be good.

> In kernel/sched/core.c we already have:
> 
> #ifdef CONFIG_SCHED_STACK_END_CHECK
>   	if (task_stack_end_corrupted(prev))
> 		panic("corrupted stack end detected inside scheduler\n");
> #endif
> 
> So what would you think if I do the following in check_alloca():
> 
> 	if (size >= stack_left) {
> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
> 		panic("alloca over the kernel stack boundary\n");
> #else
> 		BUG();
> #endif

Given this is already out-of-line, how about we always use panic(), regardless
of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just

	if (unlikely(size >= stack_left))
		panic("alloca over the kernel stack boundary");

If we have VMAP_STACK selected, and overflow during the panic, it's the same as
if we overflowed during the BUG(). It's likely that panic() will use less stack
space than BUG(), and the compiler can put the call in a slow path that
shouldn't affect most calls, so in all cases it's likely preferable.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-14  5:15                     ` Mark Rutland
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-14  5:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
> It seems that previously I was very "lucky" to accidentally have those MIN_STACK_LEFT,
> call trace depth and oops=panic together to experience a hang on stack overflow
> during BUG().
> 
> 
> When I run my test in a loop _without_ VMAP_STACK, I manage to corrupt the neighbour
> processes with BUG() handling overstepping the stack boundary. It's a pity, but
> I have an idea.

I think that in the absence of VMAP_STACK, there will always be cases where we
*could* corrupt a neighbouring stack, but I agree that trying to minimize that
possibility would be good.

> In kernel/sched/core.c we already have:
> 
> #ifdef CONFIG_SCHED_STACK_END_CHECK
>   	if (task_stack_end_corrupted(prev))
> 		panic("corrupted stack end detected inside scheduler\n");
> #endif
> 
> So what would you think if I do the following in check_alloca():
> 
> 	if (size >= stack_left) {
> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
> 		panic("alloca over the kernel stack boundary\n");
> #else
> 		BUG();
> #endif

Given this is already out-of-line, how about we always use panic(), regardless
of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just

	if (unlikely(size >= stack_left))
		panic("alloca over the kernel stack boundary");

If we have VMAP_STACK selected, and overflow during the panic, it's the same as
if we overflowed during the BUG(). It's likely that panic() will use less stack
space than BUG(), and the compiler can put the call in a slow path that
shouldn't affect most calls, so in all cases it's likely preferable.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-14  5:15                     ` Mark Rutland
@ 2018-05-14  9:35                       ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-14  9:35 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel,
	kernel-hardening, linux-arm-kernel, linux-kernel

On 14.05.2018 08:15, Mark Rutland wrote:
> On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
>> It seems that previously I was very "lucky" to accidentally have those MIN_STACK_LEFT,
>> call trace depth and oops=panic together to experience a hang on stack overflow
>> during BUG().
>>
>>
>> When I run my test in a loop _without_ VMAP_STACK, I manage to corrupt the neighbour
>> processes with BUG() handling overstepping the stack boundary. It's a pity, but
>> I have an idea.
> 
> I think that in the absence of VMAP_STACK, there will always be cases where we
> *could* corrupt a neighbouring stack, but I agree that trying to minimize that
> possibility would be good.

Ok!

>> In kernel/sched/core.c we already have:
>>
>> #ifdef CONFIG_SCHED_STACK_END_CHECK
>>   	if (task_stack_end_corrupted(prev))
>> 		panic("corrupted stack end detected inside scheduler\n");
>> #endif
>>
>> So what would you think if I do the following in check_alloca():
>>
>> 	if (size >= stack_left) {
>> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
>> 		panic("alloca over the kernel stack boundary\n");
>> #else
>> 		BUG();
>> #endif
> 
> Given this is already out-of-line, how about we always use panic(), regardless
> of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just
> 
> 	if (unlikely(size >= stack_left))
> 		panic("alloca over the kernel stack boundary");
> 
> If we have VMAP_STACK selected, and overflow during the panic, it's the same as
> if we overflowed during the BUG(). It's likely that panic() will use less stack
> space than BUG(), and the compiler can put the call in a slow path that
> shouldn't affect most calls, so in all cases it's likely preferable.

I'm sure that maintainers and Linus will strongly dislike my patch if I always
use panic() here. panic() kills the whole kernel and we shouldn't use it when we
can safely continue to work.

Let me describe my logic. So let's have size >= stack_left on a thread stack.

1. If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). Even if BUG()
handling overflows the thread stack into the guard page, handle_stack_overflow()
is called and the neighbour memory is not corrupted. The kernel can proceed to live.

2. If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt the neighbour
kernel memory and cause the undefined behaviour of the whole kernel. I see it on
my lkdtm test. That is a cogent reason for panic().

2.a. If CONFIG_SCHED_STACK_END_CHECK is enabled, the kernel already does panic()
when STACK_END_MAGIC is corrupted. So we will _not_ break the safety policy if
we do panic() in a similar situation in check_alloca().

2.b. If CONFIG_SCHED_STACK_END_CHECK is disabled, the user has some real reasons
not to do panic() when the kernel stack is corrupted. So we should not do it in
check_alloca() as well, just use BUG() and hope for the best.

That logic can be expressed this way:

	if (size >= stack_left) {
#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
		panic("alloca over the kernel stack boundary\n");
#else
		BUG();
#endif

I think I should add a proper comment to describe it.

Thank you.

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-14  9:35                       ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-14  9:35 UTC (permalink / raw)
  To: linux-arm-kernel

On 14.05.2018 08:15, Mark Rutland wrote:
> On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
>> It seems that previously I was very "lucky" to accidentally have those MIN_STACK_LEFT,
>> call trace depth and oops=panic together to experience a hang on stack overflow
>> during BUG().
>>
>>
>> When I run my test in a loop _without_ VMAP_STACK, I manage to corrupt the neighbour
>> processes with BUG() handling overstepping the stack boundary. It's a pity, but
>> I have an idea.
> 
> I think that in the absence of VMAP_STACK, there will always be cases where we
> *could* corrupt a neighbouring stack, but I agree that trying to minimize that
> possibility would be good.

Ok!

>> In kernel/sched/core.c we already have:
>>
>> #ifdef CONFIG_SCHED_STACK_END_CHECK
>>   	if (task_stack_end_corrupted(prev))
>> 		panic("corrupted stack end detected inside scheduler\n");
>> #endif
>>
>> So what would you think if I do the following in check_alloca():
>>
>> 	if (size >= stack_left) {
>> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
>> 		panic("alloca over the kernel stack boundary\n");
>> #else
>> 		BUG();
>> #endif
> 
> Given this is already out-of-line, how about we always use panic(), regardless
> of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just
> 
> 	if (unlikely(size >= stack_left))
> 		panic("alloca over the kernel stack boundary");
> 
> If we have VMAP_STACK selected, and overflow during the panic, it's the same as
> if we overflowed during the BUG(). It's likely that panic() will use less stack
> space than BUG(), and the compiler can put the call in a slow path that
> shouldn't affect most calls, so in all cases it's likely preferable.

I'm sure that maintainers and Linus will strongly dislike my patch if I always
use panic() here. panic() kills the whole kernel and we shouldn't use it when we
can safely continue to work.

Let me describe my logic. So let's have size >= stack_left on a thread stack.

1. If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). Even if BUG()
handling overflows the thread stack into the guard page, handle_stack_overflow()
is called and the neighbour memory is not corrupted. The kernel can proceed to live.

2. If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt the neighbour
kernel memory and cause the undefined behaviour of the whole kernel. I see it on
my lkdtm test. That is a cogent reason for panic().

2.a. If CONFIG_SCHED_STACK_END_CHECK is enabled, the kernel already does panic()
when STACK_END_MAGIC is corrupted. So we will _not_ break the safety policy if
we do panic() in a similar situation in check_alloca().

2.b. If CONFIG_SCHED_STACK_END_CHECK is disabled, the user has some real reasons
not to do panic() when the kernel stack is corrupted. So we should not do it in
check_alloca() as well, just use BUG() and hope for the best.

That logic can be expressed this way:

	if (size >= stack_left) {
#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
		panic("alloca over the kernel stack boundary\n");
#else
		BUG();
#endif

I think I should add a proper comment to describe it.

Thank you.

Best regards,
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-14  9:35                       ` Alexander Popov
@ 2018-05-14 10:06                         ` Mark Rutland
  -1 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-14 10:06 UTC (permalink / raw)
  To: Alexander Popov
  Cc: Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel,
	kernel-hardening, linux-arm-kernel, linux-kernel

On Mon, May 14, 2018 at 12:35:25PM +0300, Alexander Popov wrote:
> On 14.05.2018 08:15, Mark Rutland wrote:
> > On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
> >> So what would you think if I do the following in check_alloca():
> >>
> >> 	if (size >= stack_left) {
> >> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
> >> 		panic("alloca over the kernel stack boundary\n");
> >> #else
> >> 		BUG();
> >> #endif
> > 
> > Given this is already out-of-line, how about we always use panic(), regardless
> > of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just
> > 
> > 	if (unlikely(size >= stack_left))
> > 		panic("alloca over the kernel stack boundary");
> > 
> > If we have VMAP_STACK selected, and overflow during the panic, it's the same as
> > if we overflowed during the BUG(). It's likely that panic() will use less stack
> > space than BUG(), and the compiler can put the call in a slow path that
> > shouldn't affect most calls, so in all cases it's likely preferable.
> 
> I'm sure that maintainers and Linus will strongly dislike my patch if I always
> use panic() here. panic() kills the whole kernel and we shouldn't use it when we
> can safely continue to work.
> 
> Let me describe my logic. So let's have size >= stack_left on a thread stack.
> 
> 1. If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). Even if BUG()
> handling overflows the thread stack into the guard page, handle_stack_overflow()
> is called and the neighbour memory is not corrupted. The kernel can proceed to live.

On arm64 with CONFIG_VMAP_STACK, a stack overflow will result in a
panic(). My understanding was that the same is true on x86.

> 2. If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt the neighbour
> kernel memory and cause the undefined behaviour of the whole kernel. I see it on
> my lkdtm test. That is a cogent reason for panic().

In this case, panic() can also corrupt the neighbour stack, and could
also fail.

When CONFIG_VMAP_STACK is not selected, a stack overflow simply cannot
be handled reliably -- while panic() may be more likely to succeed, it
is not gauranteed to.

> 2.a. If CONFIG_SCHED_STACK_END_CHECK is enabled, the kernel already does panic()
> when STACK_END_MAGIC is corrupted. So we will _not_ break the safety policy if
> we do panic() in a similar situation in check_alloca().

Sure, I'm certainly happy with panic() here.

> 2.b. If CONFIG_SCHED_STACK_END_CHECK is disabled, the user has some real reasons
> not to do panic() when the kernel stack is corrupted. 

I believe that CONFIG_SCHED_STACK_END_CHECK is seen as a debug feature,
and hence people don't select it. I strongly doubt that people have
reasons to disable it other than not wanting the overhead associated
with debug features.

I think it is reasonable to panic() here even with CONFIG_VMAP_STACK
selected.

> So we should not do it in check_alloca() as well, just use BUG() and
> hope for the best.

Regardless of whether we BUG() or panic(), we're hoping for the best.

Consistently using panic() here will keep things simpler, so any failure
reported will be easier to reason about, and easier to debug.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-14 10:06                         ` Mark Rutland
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-14 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 14, 2018 at 12:35:25PM +0300, Alexander Popov wrote:
> On 14.05.2018 08:15, Mark Rutland wrote:
> > On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
> >> So what would you think if I do the following in check_alloca():
> >>
> >> 	if (size >= stack_left) {
> >> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
> >> 		panic("alloca over the kernel stack boundary\n");
> >> #else
> >> 		BUG();
> >> #endif
> > 
> > Given this is already out-of-line, how about we always use panic(), regardless
> > of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just
> > 
> > 	if (unlikely(size >= stack_left))
> > 		panic("alloca over the kernel stack boundary");
> > 
> > If we have VMAP_STACK selected, and overflow during the panic, it's the same as
> > if we overflowed during the BUG(). It's likely that panic() will use less stack
> > space than BUG(), and the compiler can put the call in a slow path that
> > shouldn't affect most calls, so in all cases it's likely preferable.
> 
> I'm sure that maintainers and Linus will strongly dislike my patch if I always
> use panic() here. panic() kills the whole kernel and we shouldn't use it when we
> can safely continue to work.
> 
> Let me describe my logic. So let's have size >= stack_left on a thread stack.
> 
> 1. If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). Even if BUG()
> handling overflows the thread stack into the guard page, handle_stack_overflow()
> is called and the neighbour memory is not corrupted. The kernel can proceed to live.

On arm64 with CONFIG_VMAP_STACK, a stack overflow will result in a
panic(). My understanding was that the same is true on x86.

> 2. If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt the neighbour
> kernel memory and cause the undefined behaviour of the whole kernel. I see it on
> my lkdtm test. That is a cogent reason for panic().

In this case, panic() can also corrupt the neighbour stack, and could
also fail.

When CONFIG_VMAP_STACK is not selected, a stack overflow simply cannot
be handled reliably -- while panic() may be more likely to succeed, it
is not gauranteed to.

> 2.a. If CONFIG_SCHED_STACK_END_CHECK is enabled, the kernel already does panic()
> when STACK_END_MAGIC is corrupted. So we will _not_ break the safety policy if
> we do panic() in a similar situation in check_alloca().

Sure, I'm certainly happy with panic() here.

> 2.b. If CONFIG_SCHED_STACK_END_CHECK is disabled, the user has some real reasons
> not to do panic() when the kernel stack is corrupted. 

I believe that CONFIG_SCHED_STACK_END_CHECK is seen as a debug feature,
and hence people don't select it. I strongly doubt that people have
reasons to disable it other than not wanting the overhead associated
with debug features.

I think it is reasonable to panic() here even with CONFIG_VMAP_STACK
selected.

> So we should not do it in check_alloca() as well, just use BUG() and
> hope for the best.

Regardless of whether we BUG() or panic(), we're hoping for the best.

Consistently using panic() here will keep things simpler, so any failure
reported will be easier to reason about, and easier to debug.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-14 10:06                         ` Mark Rutland
@ 2018-05-14 13:53                           ` Alexander Popov
  -1 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-14 13:53 UTC (permalink / raw)
  To: Mark Rutland
  Cc: Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel,
	kernel-hardening, linux-arm-kernel, linux-kernel

On 14.05.2018 13:06, Mark Rutland wrote:
> On Mon, May 14, 2018 at 12:35:25PM +0300, Alexander Popov wrote:
>> On 14.05.2018 08:15, Mark Rutland wrote:
>>> On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
>>>> So what would you think if I do the following in check_alloca():
>>>>
>>>> 	if (size >= stack_left) {
>>>> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
>>>> 		panic("alloca over the kernel stack boundary\n");
>>>> #else
>>>> 		BUG();
>>>> #endif
>>>
>>> Given this is already out-of-line, how about we always use panic(), regardless
>>> of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just
>>>
>>> 	if (unlikely(size >= stack_left))
>>> 		panic("alloca over the kernel stack boundary");
>>>
>>> If we have VMAP_STACK selected, and overflow during the panic, it's the same as
>>> if we overflowed during the BUG(). It's likely that panic() will use less stack
>>> space than BUG(), and the compiler can put the call in a slow path that
>>> shouldn't affect most calls, so in all cases it's likely preferable.
>>
>> I'm sure that maintainers and Linus will strongly dislike my patch if I always
>> use panic() here. panic() kills the whole kernel and we shouldn't use it when we
>> can safely continue to work.
>>
>> Let me describe my logic. So let's have size >= stack_left on a thread stack.
>>
>> 1. If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). Even if BUG()
>> handling overflows the thread stack into the guard page, handle_stack_overflow()
>> is called and the neighbour memory is not corrupted. The kernel can proceed to live.
> 
> On arm64 with CONFIG_VMAP_STACK, a stack overflow will result in a
> panic(). My understanding was that the same is true on x86.

No, x86 CONFIG_VMAP_STACK only kills the offending process. I see it on my deep
recursion test, the kernel continues to live. handle_stack_overflow() in
arch/x86/kernel/traps.c calls die().

>> 2. If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt the neighbour
>> kernel memory and cause the undefined behaviour of the whole kernel. I see it on
>> my lkdtm test. That is a cogent reason for panic().
> 
> In this case, panic() can also corrupt the neighbour stack, and could
> also fail.
> 
> When CONFIG_VMAP_STACK is not selected, a stack overflow simply cannot
> be handled reliably -- while panic() may be more likely to succeed, it
> is not gauranteed to.
>
>> 2.a. If CONFIG_SCHED_STACK_END_CHECK is enabled, the kernel already does panic()
>> when STACK_END_MAGIC is corrupted. So we will _not_ break the safety policy if
>> we do panic() in a similar situation in check_alloca().
> 
> Sure, I'm certainly happy with panic() here.

Ok!

>> 2.b. If CONFIG_SCHED_STACK_END_CHECK is disabled, the user has some real reasons
>> not to do panic() when the kernel stack is corrupted. 
> 
> I believe that CONFIG_SCHED_STACK_END_CHECK is seen as a debug feature,
> and hence people don't select it. 

I see CONFIG_SCHED_STACK_END_CHECK enabled by default in Ubuntu config...

> I strongly doubt that people have
> reasons to disable it other than not wanting the overhead associated
> with debug features.

I think it's not a question of performance here. There are cases when a system
must live as long as possible (even partially corrupted) and must not die
entirely. Oops is ok for those systems, but panic (full DoS) is not.

> I think it is reasonable to panic() here even with CONFIG_VMAP_STACK
> selected.

It's too tough for CONFIG_VMAP_STACK on x86 - the system can proceed to live.
Anyway, the check_alloca() code will not be shared between x86 and arm64, I've
described the reasons in this thread. So I can have BUG() for CONFIG_VMAP_STACK
on x86 and Laura can consistently use panic() on arm64.

>> So we should not do it in check_alloca() as well, just use BUG() and
>> hope for the best.
> 
> Regardless of whether we BUG() or panic(), we're hoping for the best.
> 
> Consistently using panic() here will keep things simpler, so any failure
> reported will be easier to reason about, and easier to debug.

Let me keep BUG() for !CONFIG_SCHED_STACK_END_CHECK. I beware of using panic()
by default, let distro/user decide this. I remember very well how I was shouted
at, when this one was merged:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce6fa91b93630396ca220c33dd38ffc62686d499


Mark, I'm really grateful to you for such a nice code review!
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-14 13:53                           ` Alexander Popov
  0 siblings, 0 replies; 70+ messages in thread
From: Alexander Popov @ 2018-05-14 13:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 14.05.2018 13:06, Mark Rutland wrote:
> On Mon, May 14, 2018 at 12:35:25PM +0300, Alexander Popov wrote:
>> On 14.05.2018 08:15, Mark Rutland wrote:
>>> On Sun, May 13, 2018 at 11:40:07AM +0300, Alexander Popov wrote:
>>>> So what would you think if I do the following in check_alloca():
>>>>
>>>> 	if (size >= stack_left) {
>>>> #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
>>>> 		panic("alloca over the kernel stack boundary\n");
>>>> #else
>>>> 		BUG();
>>>> #endif
>>>
>>> Given this is already out-of-line, how about we always use panic(), regardless
>>> of VMAP_STACK and SCHED_STACK_END_CHECK? i.e. just
>>>
>>> 	if (unlikely(size >= stack_left))
>>> 		panic("alloca over the kernel stack boundary");
>>>
>>> If we have VMAP_STACK selected, and overflow during the panic, it's the same as
>>> if we overflowed during the BUG(). It's likely that panic() will use less stack
>>> space than BUG(), and the compiler can put the call in a slow path that
>>> shouldn't affect most calls, so in all cases it's likely preferable.
>>
>> I'm sure that maintainers and Linus will strongly dislike my patch if I always
>> use panic() here. panic() kills the whole kernel and we shouldn't use it when we
>> can safely continue to work.
>>
>> Let me describe my logic. So let's have size >= stack_left on a thread stack.
>>
>> 1. If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). Even if BUG()
>> handling overflows the thread stack into the guard page, handle_stack_overflow()
>> is called and the neighbour memory is not corrupted. The kernel can proceed to live.
> 
> On arm64 with CONFIG_VMAP_STACK, a stack overflow will result in a
> panic(). My understanding was that the same is true on x86.

No, x86 CONFIG_VMAP_STACK only kills the offending process. I see it on my deep
recursion test, the kernel continues to live. handle_stack_overflow() in
arch/x86/kernel/traps.c calls die().

>> 2. If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt the neighbour
>> kernel memory and cause the undefined behaviour of the whole kernel. I see it on
>> my lkdtm test. That is a cogent reason for panic().
> 
> In this case, panic() can also corrupt the neighbour stack, and could
> also fail.
> 
> When CONFIG_VMAP_STACK is not selected, a stack overflow simply cannot
> be handled reliably -- while panic() may be more likely to succeed, it
> is not gauranteed to.
>
>> 2.a. If CONFIG_SCHED_STACK_END_CHECK is enabled, the kernel already does panic()
>> when STACK_END_MAGIC is corrupted. So we will _not_ break the safety policy if
>> we do panic() in a similar situation in check_alloca().
> 
> Sure, I'm certainly happy with panic() here.

Ok!

>> 2.b. If CONFIG_SCHED_STACK_END_CHECK is disabled, the user has some real reasons
>> not to do panic() when the kernel stack is corrupted. 
> 
> I believe that CONFIG_SCHED_STACK_END_CHECK is seen as a debug feature,
> and hence people don't select it. 

I see CONFIG_SCHED_STACK_END_CHECK enabled by default in Ubuntu config...

> I strongly doubt that people have
> reasons to disable it other than not wanting the overhead associated
> with debug features.

I think it's not a question of performance here. There are cases when a system
must live as long as possible (even partially corrupted) and must not die
entirely. Oops is ok for those systems, but panic (full DoS) is not.

> I think it is reasonable to panic() here even with CONFIG_VMAP_STACK
> selected.

It's too tough for CONFIG_VMAP_STACK on x86 - the system can proceed to live.
Anyway, the check_alloca() code will not be shared between x86 and arm64, I've
described the reasons in this thread. So I can have BUG() for CONFIG_VMAP_STACK
on x86 and Laura can consistently use panic() on arm64.

>> So we should not do it in check_alloca() as well, just use BUG() and
>> hope for the best.
> 
> Regardless of whether we BUG() or panic(), we're hoping for the best.
> 
> Consistently using panic() here will keep things simpler, so any failure
> reported will be easier to reason about, and easier to debug.

Let me keep BUG() for !CONFIG_SCHED_STACK_END_CHECK. I beware of using panic()
by default, let distro/user decide this. I remember very well how I was shouted
at, when this one was merged:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce6fa91b93630396ca220c33dd38ffc62686d499


Mark, I'm really grateful to you for such a nice code review!
Alexander

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/2] arm64: Clear the stack
  2018-05-14 13:53                           ` Alexander Popov
@ 2018-05-14 14:07                             ` Mark Rutland
  -1 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-14 14:07 UTC (permalink / raw)
  To: Alexander Popov
  Cc: Andy Lutomirski, Laura Abbott, Kees Cook, Ard Biesheuvel,
	kernel-hardening, linux-arm-kernel, linux-kernel

On Mon, May 14, 2018 at 04:53:12PM +0300, Alexander Popov wrote:
> On 14.05.2018 13:06, Mark Rutland wrote:
> > I think it is reasonable to panic() here even with CONFIG_VMAP_STACK
> > selected.
> 
> It's too tough for CONFIG_VMAP_STACK on x86 - the system can proceed to live.
> Anyway, the check_alloca() code will not be shared between x86 and arm64, I've
> described the reasons in this thread. So I can have BUG() for CONFIG_VMAP_STACK
> on x86 and Laura can consistently use panic() on arm64.

If we need arch-specific implementations anyway, then that's fine by me.

> >> So we should not do it in check_alloca() as well, just use BUG() and
> >> hope for the best.
> > 
> > Regardless of whether we BUG() or panic(), we're hoping for the best.
> > 
> > Consistently using panic() here will keep things simpler, so any failure
> > reported will be easier to reason about, and easier to debug.
> 
> Let me keep BUG() for !CONFIG_SCHED_STACK_END_CHECK. I beware of using panic()
> by default, let distro/user decide this. I remember very well how I was shouted
> at, when this one was merged:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce6fa91b93630396ca220c33dd38ffc62686d499

Sure; my comments needn't hold up your patches.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/2] arm64: Clear the stack
@ 2018-05-14 14:07                             ` Mark Rutland
  0 siblings, 0 replies; 70+ messages in thread
From: Mark Rutland @ 2018-05-14 14:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 14, 2018 at 04:53:12PM +0300, Alexander Popov wrote:
> On 14.05.2018 13:06, Mark Rutland wrote:
> > I think it is reasonable to panic() here even with CONFIG_VMAP_STACK
> > selected.
> 
> It's too tough for CONFIG_VMAP_STACK on x86 - the system can proceed to live.
> Anyway, the check_alloca() code will not be shared between x86 and arm64, I've
> described the reasons in this thread. So I can have BUG() for CONFIG_VMAP_STACK
> on x86 and Laura can consistently use panic() on arm64.

If we need arch-specific implementations anyway, then that's fine by me.

> >> So we should not do it in check_alloca() as well, just use BUG() and
> >> hope for the best.
> > 
> > Regardless of whether we BUG() or panic(), we're hoping for the best.
> > 
> > Consistently using panic() here will keep things simpler, so any failure
> > reported will be easier to reason about, and easier to debug.
> 
> Let me keep BUG() for !CONFIG_SCHED_STACK_END_CHECK. I beware of using panic()
> by default, let distro/user decide this. I remember very well how I was shouted
> at, when this one was merged:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce6fa91b93630396ca220c33dd38ffc62686d499

Sure; my comments needn't hold up your patches.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it
  2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
                   ` (6 preceding siblings ...)
  2018-05-02 20:33   ` Laura Abbott
@ 2018-05-14 18:55 ` Laura Abbott
  7 siblings, 0 replies; 70+ messages in thread
From: Laura Abbott @ 2018-05-14 18:55 UTC (permalink / raw)
  To: Alexander Popov, kernel-hardening, Kees Cook, PaX Team,
	Brad Spengler, Ingo Molnar, Andy Lutomirski, Tycho Andersen,
	Mark Rutland, Ard Biesheuvel, Borislav Petkov, Richard Sandiford,
	Thomas Gleixner, H . Peter Anvin, Peter Zijlstra,
	Dmitry V . Levin, Emese Revfy, Jonathan Corbet, Andrey Ryabinin,
	Kirill A . Shutemov, Thomas Garnier, Andrew Morton,
	Alexei Starovoitov, Josef Bacik, Masami Hiramatsu,
	Nicholas Piggin, Al Viro, David S . Miller, Ding Tianhong,
	David Woodhouse, Josh Poimboeuf, Steven Rostedt,
	Dominik Brodowski, Juergen Gross, Linus Torvalds,
	Greg Kroah-Hartman, Dan Williams, Dave Hansen, Mathias Krause,
	Vikas Shivappa, Kyle Huey, Dmitry Safonov, Will Deacon,
	Arnd Bergmann, Florian Weimer, Boris Lukashev, x86, linux-kernel

On 04/06/2018 07:22 AM, Alexander Popov wrote:
> This is the 11th version of the patch series introducing STACKLEAK to the
> mainline kernel. The 9th version raised a fervent discussion[0].
> The assembly code introduced by that version irritated the reviewers.
> 
> I've found the way to bypass the obstacles[1] of the C implementation.
> So I dare come again. Let me ask you to look at this code without
> preconception.
> 
> Motivation
> ==========
> 
> STACKLEAK (initially developed by PaX Team):
> 
>   1. reduces the information that can be revealed through kernel stack leak bugs.
>      The idea of erasing the thread stack at the end of syscalls is similar to
>      CONFIG_PAGE_POISONING and memzero_explicit() in kernel crypto, which all
>      comply with FDP_RIP.2 (Full Residual Information Protection) of the
>      Common Criteria standard.
> 
>   2. blocks some uninitialized stack variable attacks (e.g. CVE-2017-17712,
>      CVE-2010-2963). That kind of bugs should be killed by improving C compilers
>      in future, which might take a long time.
> 
>   3. blocks stack depth overflow caused by alloca (aka Stack Clash attack).
>      That is orthogonal to the mainline kernel VLA cleanup and protects
>      un-upstreamed code.
> 
> Performance impact
> ==================
> 
> Hardware: Intel Core i7-4770, 16 GB RAM
> 
> Test #1: building the Linux kernel on a single core
> 	0.91% slowdown
> 
> Test #2: hackbench -s 4096 -l 2000 -g 15 -f 25 -P
> 	4.2% slowdown
> 
> So the STACKLEAK description in Kconfig includes:
> "The tradeoff is the performance impact: on a single CPU system kernel
> compilation sees a 1% slowdown, other systems and workloads may vary and you are
> advised to test this feature on your expected workload before deploying it".
> 
> Links
> =====
> 
> [0] http://www.openwall.com/lists/kernel-hardening/2018/03/03/7
> [1] http://www.openwall.com/lists/kernel-hardening/2018/03/21/4
> 
> 
> Alexander Popov (6):
>    gcc-plugins: Clean up the cgraph_create_edge* macros
>    x86/entry: Add STACKLEAK erasing the kernel stack at the end of
>      syscalls
>    gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack
>    lkdtm: Add a test for STACKLEAK
>    fs/proc: Show STACKLEAK metrics in the /proc file system
>    doc: self-protection: Add information about STACKLEAK feature
> 
>   Documentation/security/self-protection.rst |  23 +-
>   Documentation/x86/x86_64/mm.txt            |   2 +
>   arch/Kconfig                               |  53 ++++
>   arch/x86/Kconfig                           |   1 +
>   arch/x86/entry/Makefile                    |   3 +
>   arch/x86/entry/calling.h                   |  14 +
>   arch/x86/entry/entry_32.S                  |   7 +
>   arch/x86/entry/entry_64.S                  |   3 +
>   arch/x86/entry/entry_64_compat.S           |   5 +
>   arch/x86/entry/erase.c                     |  58 ++++
>   arch/x86/include/asm/processor.h           |   7 +
>   arch/x86/kernel/dumpstack.c                |  19 ++
>   arch/x86/kernel/process_32.c               |   8 +
>   arch/x86/kernel/process_64.c               |   8 +
>   drivers/misc/Makefile                      |   3 +
>   drivers/misc/lkdtm.h                       |   4 +
>   drivers/misc/lkdtm_core.c                  |   2 +
>   drivers/misc/lkdtm_stackleak.c             | 141 +++++++++
>   fs/proc/base.c                             |  18 ++
>   include/linux/compiler.h                   |   4 +
>   mm/util.c                                  |  33 ++
>   scripts/Makefile.gcc-plugins               |   3 +
>   scripts/gcc-plugins/gcc-common.h           |  26 +-
>   scripts/gcc-plugins/stackleak_plugin.c     | 470 +++++++++++++++++++++++++++++
>   24 files changed, 896 insertions(+), 19 deletions(-)
>   create mode 100644 arch/x86/entry/erase.c
>   create mode 100644 drivers/misc/lkdtm_stackleak.c
>   create mode 100644 scripts/gcc-plugins/stackleak_plugin.c
> 

As a point of reference, this series mitigates the leak
fixed by https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0a0b98734479aa5b3c671d5190e86273372cab95

Thanks,
Laura

^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2018-05-14 18:55 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-06 14:22 [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Alexander Popov
2018-04-06 14:22 ` [PATCH v11 1/6] gcc-plugins: Clean up the cgraph_create_edge* macros Alexander Popov
2018-04-06 14:22 ` [PATCH v11 2/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls Alexander Popov
2018-04-16 18:29   ` Kees Cook
2018-04-18 18:33     ` Laura Abbott
2018-04-18 18:50     ` Dave Hansen
2018-04-24  1:03       ` Kees Cook
2018-04-24  4:23   ` Dave Hansen
2018-04-30 23:48     ` Kees Cook
2018-05-02  8:42       ` Thomas Gleixner
2018-05-02 12:38         ` Kees Cook
2018-05-02 12:39           ` Thomas Gleixner
2018-05-02 12:51             ` Kees Cook
2018-05-02 21:02               ` Kees Cook
2018-05-06 10:04                 ` Thomas Gleixner
2018-04-06 14:22 ` [PATCH v11 3/6] gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack Alexander Popov
2018-04-06 14:22 ` [PATCH v11 4/6] lkdtm: Add a test for STACKLEAK Alexander Popov
2018-04-06 14:22 ` [PATCH v11 5/6] fs/proc: Show STACKLEAK metrics in the /proc file system Alexander Popov
2018-04-06 14:22 ` [PATCH v11 6/6] doc: self-protection: Add information about STACKLEAK feature Alexander Popov
2018-05-02 20:33 ` [PATCH 0/2] Stackleak for arm64 Laura Abbott
2018-05-02 20:33   ` Laura Abbott
2018-05-02 20:33   ` [PATCH 1/2] stackleak: Update " Laura Abbott
2018-05-02 20:33     ` Laura Abbott
2018-05-02 20:33   ` [PATCH 2/2] arm64: Clear the stack Laura Abbott
2018-05-02 20:33     ` Laura Abbott
2018-05-02 21:31     ` Kees Cook
2018-05-02 21:31       ` Kees Cook
2018-05-02 23:07       ` Laura Abbott
2018-05-02 23:07         ` Laura Abbott
2018-05-02 23:37         ` Kees Cook
2018-05-02 23:37           ` Kees Cook
2018-05-03 16:05         ` Alexander Popov
2018-05-03 16:05           ` Alexander Popov
2018-05-03 16:45           ` Kees Cook
2018-05-03 16:45             ` Kees Cook
2018-05-03  7:19     ` Mark Rutland
2018-05-03  7:19       ` Mark Rutland
2018-05-03 11:37       ` Ard Biesheuvel
2018-05-03 11:37         ` Ard Biesheuvel
2018-05-03 17:33       ` Alexander Popov
2018-05-03 17:33         ` Alexander Popov
2018-05-03 19:09         ` Laura Abbott
2018-05-03 19:09           ` Laura Abbott
2018-05-04  8:30           ` Alexander Popov
2018-05-04  8:30             ` Alexander Popov
2018-05-04 11:09         ` Mark Rutland
2018-05-04 11:09           ` Mark Rutland
2018-05-06  8:22           ` Alexander Popov
2018-05-06  8:22             ` Alexander Popov
2018-05-11 15:50             ` Alexander Popov
2018-05-11 15:50               ` Alexander Popov
2018-05-11 16:13               ` Mark Rutland
2018-05-11 16:13                 ` Mark Rutland
2018-05-13  8:40                 ` Alexander Popov
2018-05-13  8:40                   ` Alexander Popov
2018-05-14  5:15                   ` Mark Rutland
2018-05-14  5:15                     ` Mark Rutland
2018-05-14  9:35                     ` Alexander Popov
2018-05-14  9:35                       ` Alexander Popov
2018-05-14 10:06                       ` Mark Rutland
2018-05-14 10:06                         ` Mark Rutland
2018-05-14 13:53                         ` Alexander Popov
2018-05-14 13:53                           ` Alexander Popov
2018-05-14 14:07                           ` Mark Rutland
2018-05-14 14:07                             ` Mark Rutland
2018-05-03 19:00       ` Laura Abbott
2018-05-03 19:00         ` Laura Abbott
2018-05-04 11:16         ` Mark Rutland
2018-05-04 11:16           ` Mark Rutland
2018-05-14 18:55 ` [PATCH v11 0/6] Introduce the STACKLEAK feature and a test for it Laura Abbott

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.