Skip to contentSkip to conversation

Introduce the STACKLEAK feature and a test for it

PATCH Superseded Alexander Popov 8 participants 50 messages Jul 11, 2018 – Jul 26, 2018

Subsystems (17)
Documentation x86 Kbuild MM core +13 more subsystems

v14 of 15 Superseded Latest is v15 →

lore
Share & tools
lore
#0 lore raw

v14 cover Introduce the STACKLEAK feature and a test for it

This is the 14th version of the patch series introducing STACKLEAK to the mainline kernel for x86. This version comes with style changes according to the feedback from Ingo Molnar and includes Laura Abbott's modifications of scripts/Makefile.gcc-plugins.

Previous version discussion:
http://www.openwall.com/lists/kernel-hardening/2018/06/22/8

arm64 support will come in a separate patch from Laura Abbott.

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".

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/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/kernel/dumpstack.c                |  31 ++
 drivers/misc/lkdtm/Makefile                |   2 +
 drivers/misc/lkdtm/core.c                  |   3 +
 drivers/misc/lkdtm/lkdtm.h                 |   5 +
 drivers/misc/lkdtm/stackleak.c             | 146 +++++++++
 fs/proc/base.c                             |  18 ++
 include/linux/sched.h                      |   5 +
 include/linux/stackleak.h                  |  26 ++
 kernel/Makefile                            |   4 +
 kernel/fork.c                              |   3 +
 kernel/stackleak.c                         |  94 ++++++
 scripts/Makefile.gcc-plugins               |   8 +-
 scripts/gcc-plugins/gcc-common.h           |  26 +-
 scripts/gcc-plugins/stackleak_plugin.c     | 480 +++++++++++++++++++++++++++++
 22 files changed, 939 insertions(+), 20 deletions(-)
 create mode 100644 drivers/misc/lkdtm/stackleak.c
 create mode 100644 include/linux/stackleak.h
 create mode 100644 kernel/stackleak.c
 create mode 100644 scripts/gcc-plugins/stackleak_plugin.c
Signature · 1 line
2.7.4
#1 lore raw

PATCH v14 1/6 gcc-plugins: Clean up the cgraph_create_edge* macros

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

---
 scripts/gcc-plugins/gcc-common.h | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)
scripts/gcc-plugins/gcc-common.h +16 -10
index f467500..552d5ef 100644
@@ -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;
Signature · 1 line
2.7.4
#2 lore raw

PATCH v14 2/6 x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls

In reply to Alexander 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 blocks kernel stack depth overflow caused by alloca(), aka Stack Clash attack.

This commit introduces the code filling the used part of the kernel stack with a poison value before returning to 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.

---
 Documentation/x86/x86_64/mm.txt  |  2 ++
 arch/Kconfig                     | 27 +++++++++++++++++
 arch/x86/Kconfig                 |  1 +
 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 ++++
 include/linux/sched.h            |  4 +++
 include/linux/stackleak.h        | 23 +++++++++++++++
 kernel/Makefile                  |  4 +++
 kernel/fork.c                    |  3 ++
 kernel/stackleak.c               | 62 ++++++++++++++++++++++++++++++++++++++++
 12 files changed, 155 insertions(+)
 create mode 100644 include/linux/stackleak.h
 create mode 100644 kernel/stackleak.c
Documentation/x86/x86_64/mm.txt +2 -0
index 5432a96..600bc2a 100644
@@ -24,6 +24,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
 
 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
arch/Kconfig +27 -0
index 1aa5906..07c8929 100644
@@ -414,6 +414,13 @@ config PLUGIN_HOSTCC
 	  Host compiler used to build GCC plugins.  This can be $(HOSTCXX),
 	  $(HOSTCC), or a null string if GCC plugin is unsupported.
 
+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
@@ -549,6 +556,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
+	  returning from system calls. That reduces the information which
+	  kernel stack leak bugs can reveal and blocks some uninitialized
+	  stack variable attacks. This option also blocks kernel stack depth
+	  overflow caused by alloca(), aka Stack Clash attack.
+
+	  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_STACKPROTECTOR
 	bool
 	help
arch/x86/Kconfig +1 -0
index f1dbb4e..3718d70 100644
@@ -125,6 +125,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
arch/x86/entry/calling.h +14 -0
index 352e70c..20d0885 100644
@@ -329,8 +329,22 @@ For 32-bit we have the following conventions - kernel is built with
 
 #endif
 
+.macro STACKLEAK_ERASE_NOCLOBBER
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	PUSH_AND_CLEAR_REGS
+	call stackleak_erase
+	POP_REGS
+#endif
+.endm
+
 #endif /* CONFIG_X86_64 */
 
+.macro STACKLEAK_ERASE
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	call stackleak_erase
+#endif
+.endm
+
 /*
  * This does 'call enter_from_user_mode' unless we can avoid it based on
  * kernel config or using the static jump infrastructure.
arch/x86/entry/entry_32.S +7 -0
index c371bfe..8dc1580 100644
@@ -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
+	STACKLEAK_ERASE
 	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
 
+	STACKLEAK_ERASE
+
 /* 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:
 
+	STACKLEAK_ERASE
+
 restore_all:
 	TRACE_IRQS_IRET
 .Lrestore_all_notrace:
arch/x86/entry/entry_64.S +3 -0
index 73a522d..408101b 100644
@@ -329,6 +329,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.
 	 */
+	STACKLEAK_ERASE_NOCLOBBER
+
 	SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
 
 	popq	%rdi
@@ -687,6 +689,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.
 	 */
+	STACKLEAK_ERASE_NOCLOBBER
 
 	SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
 
arch/x86/entry/entry_64_compat.S +5 -0
index 7d0df78..8eaf895 100644
@@ -261,6 +261,11 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe)
 
 	/* Opportunistic SYSRET */
 sysret32_from_system_call:
+	/*
+	 * We are not going to return to userspace from the trampoline
+	 * stack. So let's erase the thread stack right now.
+	 */
+	STACKLEAK_ERASE
 	TRACE_IRQS_ON			/* User mode traces as IRQs on. */
 	movq	RBX(%rsp), %rbx		/* pt_regs->rbx */
 	movq	RBP(%rsp), %rbp		/* pt_regs->rbp */
include/linux/sched.h +4 -0
index 43731fe..bf773d1 100644
@@ -1180,6 +1180,10 @@ struct task_struct {
 	void				*security;
 #endif
 
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	unsigned long			lowest_stack;
+#endif
+
 	/*
 	 * New fields for task_struct should be added above here, so that
 	 * they are included in the randomized portion of task_struct.
include/linux/stackleak.h new file +23 -0
new file mode 100644
index 0000000..d2560159
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_STACKLEAK_H
+#define _LINUX_STACKLEAK_H
+
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+
+/*
+ * Check that the poison value points to the unused hole in the
+ * virtual memory map for your platform.
+ */
+#define STACKLEAK_POISON -0xBEEF
+
+#define STACKLEAK_SEARCH_DEPTH 128
+
+static inline void stackleak_task_init(struct task_struct *t)
+{
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long);
+#endif
+}
+
+#endif
kernel/Makefile +4 -0
index 04bc07c..e5dc293 100644
@@ -117,6 +117,10 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o
 obj-$(CONFIG_ZONE_DEVICE) += memremap.o
 obj-$(CONFIG_RSEQ) += rseq.o
 
+obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
+KASAN_SANITIZE_stackleak.o := n
+KCOV_INSTRUMENT_stackleak.o := n
+
 $(obj)/configs.o: $(obj)/config_data.h
 
 targets += config_data.gz
kernel/fork.c +3 -0
index 9440d61..1bc86ba 100644
@@ -91,6 +91,7 @@
 #include <linux/kcov.h>
 #include <linux/livepatch.h>
 #include <linux/thread_info.h>
+#include <linux/stackleak.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -1813,6 +1814,8 @@ static __latent_entropy struct task_struct *copy_process(
 	if (retval)
 		goto bad_fork_cleanup_io;
 
+	stackleak_task_init(p);
+
 	if (pid != &init_struct_pid) {
 		pid = alloc_pid(p->nsproxy->pid_ns_for_children);
 		if (IS_ERR(pid)) {
kernel/stackleak.c new file +62 -0

62 lines not on this page · raw message

Signature · 1 line
2.7.4
#3 lore raw

PATCH v14 3/6 gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack

In reply to Alexander 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 blocks kernel stack depth overflow caused by alloca(), aka Stack Clash attack.

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 stackleak_check_alloca() call before alloca() and the

    stackleak_track_stack() call after it;

- the stackleak_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.

---
 arch/Kconfig                           |  14 +
 arch/x86/kernel/dumpstack.c            |  31 +++
 kernel/stackleak.c                     |  28 ++
 scripts/Makefile.gcc-plugins           |   8 +-
 scripts/gcc-plugins/stackleak_plugin.c | 480 +++++++++++++++++++++++++++++++++
 5 files changed, 560 insertions(+), 1 deletion(-)
 create mode 100644 scripts/gcc-plugins/stackleak_plugin.c
arch/Kconfig +14 -0
index 07c8929..aa9374b 100644
@@ -560,6 +560,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
 	  returning from system calls. That reduces the information which
@@ -576,6 +578,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 stackleak_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_STACKPROTECTOR
 	bool
 	help
arch/x86/kernel/dumpstack.c +31 -0
index 666a284..df6f61c 100644
@@ -418,3 +418,34 @@ void show_regs(struct pt_regs *regs)
 	if (!user_mode(regs))
 		show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT);
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_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;
+
+	if (size >= stack_left) {
+		/*
+		 * Kernel stack depth overflow is detected, let's report that.
+		 * If CONFIG_VMAP_STACK is enabled, we can safely use BUG().
+		 * If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt
+		 * the neighbour memory. CONFIG_SCHED_STACK_END_CHECK calls
+		 * panic() in a similar situation, so let's do the same if that
+		 * option is on. Otherwise just use BUG() and hope for the best.
+		 */
+#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
+		panic("alloca() over the kernel stack boundary\n");
+#else
+		BUG();
+#endif
+	}
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
kernel/stackleak.c +28 -0
index bc3ad49..659dcc9 100644
@@ -60,3 +60,31 @@ asmlinkage void stackleak_erase(void)
 	current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64;
 }
 
+void __used stackleak_track_stack(void)
+{
+	/*
+	 * N.B. stackleak_erase() fills the kernel stack with the poison value,
+	 * which has the register width. That code assumes that the value
+	 * of '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_SEARCH_DEPTH makes the poison search in
+	 * stackleak_erase() unreliable. Let's prevent that.
+	 */
+	BUILD_BUG_ON(CONFIG_STACKLEAK_TRACK_MIN_SIZE > STACKLEAK_SEARCH_DEPTH);
+
+	if (sp < current->lowest_stack &&
+	    sp >= (unsigned long)task_stack_page(current) +
+						sizeof(unsigned long)) {
+		current->lowest_stack = sp;
+	}
+}
+EXPORT_SYMBOL(stackleak_track_stack);
scripts/Makefile.gcc-plugins +7 -1
index c961b9a..091fd33 100644
@@ -17,10 +17,16 @@ gcc-plugin-$(CONFIG_GCC_PLUGIN_RANDSTRUCT)	+= randomize_layout_plugin.so
 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)
+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 GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
-export DISABLE_LATENT_ENTROPY_PLUGIN
+export DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
 
 # sancov_plugin.so can be only in CFLAGS_KCOV because avoid duplication.
 GCC_PLUGINS_CFLAGS := $(filter-out %/sancov_plugin.so, $(GCC_PLUGINS_CFLAGS))
scripts/gcc-plugins/stackleak_plugin.c new file +480 -0

480 lines not on this page · raw message

Signature · 1 line
2.7.4
#4 lore raw

PATCH v14 4/6 lkdtm: Add a test for STACKLEAK

In reply to Alexander Popov

Introduce lkdtm tests for the STACKLEAK feature.

First, all of them check that the current task stack is properly erased (filled with STACKLEAK_POISON).

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

STACKLEAK_BIG_ALLOCA and STACKLEAK_RECURSION_WITH_ALLOCA test that alloca() calls which overflow the kernel stack hit BUG()/panic() in stackleak_check_alloca().

---
 drivers/misc/lkdtm/Makefile    |   2 +
 drivers/misc/lkdtm/core.c      |   3 +
 drivers/misc/lkdtm/lkdtm.h     |   5 ++
 drivers/misc/lkdtm/stackleak.c | 146 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 156 insertions(+)
 create mode 100644 drivers/misc/lkdtm/stackleak.c
drivers/misc/lkdtm/Makefile +2 -0
index 3370a41..951c984 100644
@@ -8,7 +8,9 @@ lkdtm-$(CONFIG_LKDTM)		+= perms.o
 lkdtm-$(CONFIG_LKDTM)		+= refcount.o
 lkdtm-$(CONFIG_LKDTM)		+= rodata_objcopy.o
 lkdtm-$(CONFIG_LKDTM)		+= usercopy.o
+lkdtm-$(CONFIG_LKDTM)		+= stackleak.o
 
+KASAN_SANITIZE_stackleak.o	:= n
 KCOV_INSTRUMENT_rodata.o	:= n
 
 OBJCOPYFLAGS :=
drivers/misc/lkdtm/core.c +3 -0
index 2154d1b..9d0324a 100644
@@ -183,6 +183,9 @@ static const struct crashtype crashtypes[] = {
 	CRASHTYPE(USERCOPY_STACK_FRAME_FROM),
 	CRASHTYPE(USERCOPY_STACK_BEYOND),
 	CRASHTYPE(USERCOPY_KERNEL),
+	CRASHTYPE(STACKLEAK_BIG_ALLOCA),
+	CRASHTYPE(STACKLEAK_DEEP_RECURSION),
+	CRASHTYPE(STACKLEAK_RECURSION_WITH_ALLOCA),
 };
 
 
drivers/misc/lkdtm/lkdtm.h +5 -0
index 9e513dc..865a6c3 100644
@@ -83,4 +83,9 @@ void lkdtm_USERCOPY_STACK_FRAME_FROM(void);
 void lkdtm_USERCOPY_STACK_BEYOND(void);
 void lkdtm_USERCOPY_KERNEL(void);
 
+/* lkdtm_stackleak.c */
+void lkdtm_STACKLEAK_BIG_ALLOCA(void);
+void lkdtm_STACKLEAK_DEEP_RECURSION(void);
+void lkdtm_STACKLEAK_RECURSION_WITH_ALLOCA(void);
+
 #endif
drivers/misc/lkdtm/stackleak.c new file +146 -0

146 lines not on this page · raw message

Signature · 1 line
2.7.4
#5 lore raw

PATCH v14 5/6 fs/proc: Show STACKLEAK metrics in the /proc file system

In reply to Alexander 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.

---
 arch/Kconfig              | 12 ++++++++++++
 fs/proc/base.c            | 18 ++++++++++++++++++
 include/linux/sched.h     |  1 +
 include/linux/stackleak.h |  3 +++
 kernel/stackleak.c        |  4 ++++
 5 files changed, 38 insertions(+)
arch/Kconfig +12 -0
index aa9374b..227a6eb 100644
@@ -590,6 +590,18 @@ config STACKLEAK_TRACK_MIN_SIZE
 	  a stack 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_STACKPROTECTOR
 	bool
 	help
fs/proc/base.c +18 -0
index aaffc0c..66ee5b7 100644
@@ -2893,6 +2893,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->prev_lowest_stack & (THREAD_SIZE - 1));
+	unsigned long depth = THREAD_SIZE -
+				(task->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
  */
@@ -2994,6 +3009,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)
include/linux/sched.h +1 -0
index bf773d1..5c729ed 100644
@@ -1182,6 +1182,7 @@ struct task_struct {
 
 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
 	unsigned long			lowest_stack;
+	unsigned long			prev_lowest_stack;
 #endif
 
 	/*
include/linux/stackleak.h +3 -0
index d2560159..b62f133 100644
@@ -17,6 +17,9 @@ static inline void stackleak_task_init(struct task_struct *t)
 {
 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK
 	t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long);
+# ifdef CONFIG_STACKLEAK_METRICS
+	t->prev_lowest_stack = t->lowest_stack;
+# endif
 #endif
 }
 
kernel/stackleak.c +4 -0
index 659dcc9..f5c4111 100644
@@ -39,6 +39,10 @@ asmlinkage void stackleak_erase(void)
 	if (kstack_ptr == boundary)
 		kstack_ptr += sizeof(unsigned long);
 
+#ifdef CONFIG_STACKLEAK_METRICS
+	current->prev_lowest_stack = kstack_ptr;
+#endif
+
 	/*
 	 * Now write the poison value to the kernel stack. Start from
 	 * 'kstack_ptr' and move up till the new 'boundary'. We assume that
Signature · 1 line
2.7.4
#6 lore raw

PATCH v14 6/6 doc: self-protection: Add information about STACKLEAK feature

In reply to Alexander Popov

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

---
 Documentation/security/self-protection.rst | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)
Documentation/security/self-protection.rst +14 -9
index e1ca698..452bc75 100644
@@ -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
 --------------------
Signature · 1 line
2.7.4
#7 lore raw
In reply to Alexander Popov
Quoted 5 lines · Alexander Popov

This is the 14th version of the patch series introducing STACKLEAK to the mainline kernel for x86. This version comes with style changes according to the feedback from Ingo Molnar and includes Laura Abbott's modifications of scripts/Makefile.gcc-plugins.

As before, I will repeat that I think 1% on a kernel compile (which has almost no actual kernel footprint) is a huge cost, and other - more kernel-centric - loads will likely see much worse performance.

And I still think that this is stupid, and peoples time would be *much* better spent on plugins that have much lower costs and get pretty much the same advanytages (ie the whole "just initialize all automatics to zero" thing or similar).

But whatever. I'll just continue to tell people not to use silly config options that have a high cost-to-point ratio. If people *want* to waste their CPU, that's their problem.

Linus

#8 lore raw
Quoted 11 lines · Linus Torvalds
Quoted 5 lines · Alexander Popov

This is the 14th version of the patch series introducing STACKLEAK to the mainline kernel for x86. This version comes with style changes according to the feedback from Ingo Molnar and includes Laura Abbott's modifications of scripts/Makefile.gcc-plugins.

As before, I will repeat that I think 1% on a kernel compile (which has almost no actual kernel footprint) is a huge cost, and other - more kernel-centric - loads will likely see much worse performance.

Quoted 8 lines

And I still think that this is stupid, and peoples time would be *much* better spent on plugins that have much lower costs and get pretty much the same advanytages (ie the whole "just initialize all automatics to zero" thing or similar).

But whatever. I'll just continue to tell people not to use silly config options that have a high cost-to-point ratio. If people *want* to waste their CPU, that's their problem.

So I agree that it's pretty stupid. Since 95%+ of our users just use what the distro configured - so at minimum I'd like to see a runtime patching based way for root to disable it, so that people (who care or who are hurt by it) can turn the check off - which should be the most expensive part causing much of the 1% overhead.

Shouldn't be too hard to implement, this is just a checking method called for every system call that does nothing substantial in 99.99999% of the cases.

Thanks,

Ingo

#9 lore raw
Quoted 23 lines · Ingo Molnar
Quoted 11 lines · Linus Torvalds
Quoted 5 lines · Alexander Popov

This is the 14th version of the patch series introducing STACKLEAK to the mainline kernel for x86. This version comes with style changes according to the feedback from Ingo Molnar and includes Laura Abbott's modifications of scripts/Makefile.gcc-plugins.

As before, I will repeat that I think 1% on a kernel compile (which has almost no actual kernel footprint) is a huge cost, and other - more kernel-centric - loads will likely see much worse performance.

Quoted 8 lines

And I still think that this is stupid, and peoples time would be *much* better spent on plugins that have much lower costs and get pretty much the same advanytages (ie the whole "just initialize all automatics to zero" thing or similar).

But whatever. I'll just continue to tell people not to use silly config options that have a high cost-to-point ratio. If people *want* to waste their CPU, that's their problem.

Quoted 8 lines

So I agree that it's pretty stupid. Since 95%+ of our users just use what the distro configured - so at minimum I'd like to see a runtime patching based way for root to disable it, so that people (who care or who are hurt by it) can turn the check off - which should be the most expensive part causing much of the 1% overhead.

Shouldn't be too hard to implement, this is just a checking method called for every system call that does nothing substantial in 99.99999% of the cases.

I'd like to not have this be required for initial inclusion. Most people interested in this feature build their own kernels. Improving on this to make it available to all distros will be excellent, and I think we can do it with static keys or alternatives (?). I'd prefer this feature request not block the merge for 4.19, though. Would that be reasonable?

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#10 lore raw
Quoted 6 lines · Kees Cook
Quoted 6 lines

So I agree that it's pretty stupid. Since 95%+ of our users just use what the distro configured - so at minimum I'd like to see a runtime patching based way for root to disable it, so that people (who care or who are hurt by it) can turn the check off - which should be the most expensive part causing much of the 1% overhead.

Quoted 6 lines
Quoted 2 lines

Shouldn't be too hard to implement, this is just a checking method called for every system call that does nothing substantial in 99.99999% of the cases.

I'd like to not have this be required for initial inclusion.

[...] Most people interested in this feature build their own kernels.

If no distro is going to enable this then why are we even pursuing this feature?

What I'm asking for is probably just a single trivial static key in the first iteration, patched to NOP by default ... Am I missing something?

I'm 90% certain that much of the kernel build time overhead will go away with flipping that switch.

Quoted 3 lines

[...] Improving on this to make it available to all distros will be excellent, and I think we can do it with static keys or alternatives (?). I'd prefer this feature request not block the merge for 4.19, though. Would that be reasonable?

If no distro is pursuing this then there's no rush to merge this whatsoever:
people who build their own kernels can apply patches or pull in trees just as well, right?

This whole feature is a pretty horrible idea that I can see distros enabling due to security theatre. Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Thanks,

Ingo

#11 lore raw

Hello Ingo,

Quoted 3 lines · Ingo Molnar

Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Would you like the following solution?

I'll create the CONFIG_STACKLEAK_RUNTIME_DISABLE config option, which would be similar to CONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disabling stackleak_erase(), which provides the most of performance penalty.

I can design this as a 7th patch in this series.

Best regards,
Alexander

#12 lore raw
Quoted 10 lines · Alexander Popov
Quoted 3 lines · Ingo Molnar

Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Would you like the following solution?

I'll create the CONFIG_STACKLEAK_RUNTIME_DISABLE config option, which would be similar to CONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disabling stackleak_erase(), which provides the most of performance penalty.

I don't think CONFIG/sysctl is the way to go. I'd recommend making it a boot arg and using a static key, similar to what's happening to hardened_usercopy:

https://patchwork.kernel.org/patch/10505059/

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#13 lore raw
Quoted 12 lines · Kees Cook
Quoted 10 lines · Alexander Popov
Quoted 3 lines · Ingo Molnar

Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Would you like the following solution?

I'll create the CONFIG_STACKLEAK_RUNTIME_DISABLE config option, which would be similar to CONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disabling stackleak_erase(), which provides the most of performance penalty.

Quoted 5 lines

I don't think CONFIG/sysctl is the way to go. I'd recommend making it a boot arg and using a static key, similar to what's happening to hardened_usercopy:

https://patchwork.kernel.org/patch/10505059/

Ok, now I see, thanks. I'll do that.

Best regards,
Alexander

#14 lore raw
In reply to Kees Cook
Quoted 8 lines · Kees Cook
Quoted 7 lines · Alexander Popov
Quoted 3 lines · Ingo Molnar

Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Would you like the following solution?

Quoted 7 lines
Quoted 3 lines

I'll create the CONFIG_STACKLEAK_RUNTIME_DISABLE config option, which would be similar to CONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disabling stackleak_erase(), which provides the most of performance penalty.

I don't think CONFIG/sysctl is the way to go. I'd recommend making it a boot arg and using a static key, similar to what's happening to hardened_usercopy:

Why no sysctl? It's a PITA to reboot systems just to turn a stupid knob off.

Also, it's _much_ easier to measure performance impact when there's a sysctl.

Thanks,

Ingo

#15 lore raw
Quoted 15 lines · Ingo Molnar
Quoted 12 lines · Kees Cook
Quoted 10 lines · Alexander Popov
Quoted 3 lines · Ingo Molnar

Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Would you like the following solution?

I'll create the CONFIG_STACKLEAK_RUNTIME_DISABLE config option, which would be similar to CONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disabling stackleak_erase(), which provides the most of performance penalty.

Quoted 7 lines
Quoted 3 lines

I don't think CONFIG/sysctl is the way to go. I'd recommend making it a boot arg and using a static key, similar to what's happening to hardened_usercopy:

Why no sysctl? It's a PITA to reboot systems just to turn a stupid knob off.

Also, it's _much_ easier to measure performance impact when there's a sysctl.

Yes, you are right.

But I looked carefully and now see the troubles which sysctl would bring us. Each 'task_struct' has 'lowest_stack', which must be initialized before enabling STACKLEAK. So runtime enabling via sysctl is not plain and may bring race conditions.

On the other hand, a boot param + static key that can only disable STACKLEAK during __init are much more robust. I'm preparing the patch and performance measurements.

Best regards,
Alexander

#16 lore raw
Quoted 22 lines · Alexander Popov
Quoted 21 lines · Ingo Molnar
Quoted 8 lines · Kees Cook
Quoted 7 lines · Alexander Popov
Quoted 3 lines · Ingo Molnar

Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Would you like the following solution?

Quoted 7 lines
Quoted 3 lines

I'll create the CONFIG_STACKLEAK_RUNTIME_DISABLE config option, which would be similar to CONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disabling stackleak_erase(), which provides the most of performance penalty.

I don't think CONFIG/sysctl is the way to go. I'd recommend making it a boot arg and using a static key, similar to what's happening to hardened_usercopy:

Why no sysctl? It's a PITA to reboot systems just to turn a stupid knob off.

Quoted 8 lines
Quoted 1 line

Also, it's _much_ easier to measure performance impact when there's a sysctl.

Yes, you are right.

But I looked carefully and now see the troubles which sysctl would bring us. Each 'task_struct' has 'lowest_stack', which must be initialized before enabling STACKLEAK. So runtime enabling via sysctl is not plain and may bring race conditions.

Firstly, a sysctl could still allow it to be *disabled*, once - which is the most important usecase of the sysctl anyway.

Secondly, in the first iteration this could be kept included unconditionally:

	current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64;

... which would keep it initialized and wouldn't be racy, right?

I.e. only the most expensive part of the function, the scanning, would be turned off via the sysctl. I submit that this will avoid all measurable aspects of the 1% kbuild performance overhead.

A more involved approach can be done in the future if warranted, and the feature could be disabled/enabled more thoroughly - but the runtime sysctl would be acceptable for me for now, as a first iteration.

Quoted 3 lines

On the other hand, a boot param + static key that can only disable STACKLEAK during __init are much more robust. I'm preparing the patch and performance measurements.

A boot parameter does *not* address the concern I outlined. Why force admins reboot a box just to disable something that causes overhead?

Thanks,

Ingo

#17 lore raw
Quoted 29 lines · Ingo Molnar
Quoted 26 lines · Alexander Popov
Quoted 15 lines · Ingo Molnar
Quoted 12 lines · Kees Cook
Quoted 10 lines · Alexander Popov
Quoted 3 lines · Ingo Molnar

Let's make sure informed users have an easy runtime way out from the worst of the overhead that doesn't involve "recompile your distro kernel". Also, make it easier to measure and fingerpoint the overhead...

Would you like the following solution?

I'll create the CONFIG_STACKLEAK_RUNTIME_DISABLE config option, which would be similar to CONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disabling stackleak_erase(), which provides the most of performance penalty.

Quoted 7 lines
Quoted 3 lines

I don't think CONFIG/sysctl is the way to go. I'd recommend making it a boot arg and using a static key, similar to what's happening to hardened_usercopy:

Why no sysctl? It's a PITA to reboot systems just to turn a stupid knob off.

Also, it's _much_ easier to measure performance impact when there's a sysctl.

Yes, you are right.

Quoted 7 lines
Quoted 4 lines

But I looked carefully and now see the troubles which sysctl would bring us. Each 'task_struct' has 'lowest_stack', which must be initialized before enabling STACKLEAK. So runtime enabling via sysctl is not plain and may bring race conditions.

Firstly, a sysctl could still allow it to be *disabled*, once - which is the most important usecase of the sysctl anyway.

Yes, agree, such a fuse will work fine.

Quoted 5 lines

Secondly, in the first iteration this could be kept included unconditionally:

	current->lowest_stack = current_top_of_stack() - THREAD_SIZE/64;

... which would keep it initialized and wouldn't be racy, right?

No, this will break the poison search logic.

'lowest_stack' is updated during syscall handling (the gcc plugin instrumentation is responsible for that) and is reset in stackleak_erase() at the end of syscall. The stackleak_erase() searching and erasing logic _depends_ on that fact. Enabling STACKLEAK at some arbitrary moment would not be trivial - let's avoid that for now please.

Quoted 3 lines

I.e. only the most expensive part of the function, the scanning, would be turned off via the sysctl. I submit that this will avoid all measurable aspects of the 1% kbuild performance overhead.

Yes, I've made an experiment - skipping stackleak_erase() cuts almost all performance penalty of the feature.

Quoted 3 lines

A more involved approach can be done in the future if warranted, and the feature could be disabled/enabled more thoroughly - but the runtime sysctl would be acceptable for me for now, as a first iteration.

Thanks, I see your point.
I'll return with an additional patch introducing sysctl knob + static key for one-time disabling of stack erasing.

Best regards,
Alexander

Acked-by
#18 lore raw
Quoted 3 lines · Alexander Popov
Quoted 1 line

... which would keep it initialized and wouldn't be racy, right?

No, this will break the poison search logic.

Ok.

Quoted 6 lines
Quoted 3 lines

I.e. only the most expensive part of the function, the scanning, would be turned off via the sysctl. I submit that this will avoid all measurable aspects of the 1% kbuild performance overhead.

Yes, I've made an experiment - skipping stackleak_erase() cuts almost all performance penalty of the feature.

Ok, that's good!

Quoted 7 lines
Quoted 3 lines

A more involved approach can be done in the future if warranted, and the feature could be disabled/enabled more thoroughly - but the runtime sysctl would be acceptable for me for now, as a first iteration.

Thanks, I see your point.
I'll return with an additional patch introducing sysctl knob + static key for one-time disabling of stack erasing.

Thank you!

I'll have a final review of the patch but I guess it will be fine, so if that's the only change then you can also include my:

... to not hold the patch up for v4.19, and this can go upstream via Kees's tree?

Ingo

#19 lore raw
Quoted 6 lines · Ingo Molnar
Quoted 3 lines · Alexander Popov

Thanks, I see your point.
I'll return with an additional patch introducing sysctl knob + static key for one-time disabling of stack erasing.

Quoted 8 lines

Thank you!

I'll have a final review of the patch but I guess it will be fine, so if that's the only change then you can also include my:

... to not hold the patch up for v4.19, and this can go upstream via Kees's tree?

Thanks for your reviews, Ingo! Is this Ack for the entire series or a specific subset?

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#20 lore raw
Quoted 12 lines · Kees Cook
Quoted 11 lines · Ingo Molnar
Quoted 3 lines · Alexander Popov

Thanks, I see your point.
I'll return with an additional patch introducing sysctl knob + static key for one-time disabling of stack erasing.

Thank you!

I'll have a final review of the patch but I guess it will be fine, so if that's the only change then you can also include my:

Quoted 6 lines
Quoted 3 lines

... to not hold the patch up for v4.19, and this can go upstream via Kees's tree?

Thanks for your reviews, Ingo! Is this Ack for the entire series or a specific subset?

For the x86 entry bits mainly.

Thanks,

Ingo

#21 lore raw

PATCH v14 7/7 stackleak, sysctl: Allow runtime disabling of kernel stack erasing

In reply to Ingo Molnar

Introduce CONFIG_STACKLEAK_RUNTIME_DISABLE option, which provides 'stack_erasing_bypass' sysctl. It can be used in runtime to disable kernel stack erasing for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain disabled and STACKLEAK_METRICS will not be updated until the next boot.

---
 Documentation/sysctl/kernel.txt | 19 +++++++++++++++++++
 include/linux/stackleak.h       |  6 ++++++
 kernel/stackleak.c              | 40 ++++++++++++++++++++++++++++++++++++++++
 kernel/sysctl.c                 | 15 ++++++++++++++-
 scripts/gcc-plugins/Kconfig     | 10 ++++++++++
 5 files changed, 89 insertions(+), 1 deletion(-)
Documentation/sysctl/kernel.txt +19 -0
index eded671d..63b7493 100644
@@ -87,6 +87,7 @@ show up in /proc/sys/kernel:
 - shmmni
 - softlockup_all_cpu_backtrace
 - soft_watchdog
+- stack_erasing_bypass
 - stop-a                      [ SPARC only ]
 - sysrq                       ==> Documentation/admin-guide/sysrq.rst
 - sysctl_writes_strict
@@ -962,6 +963,24 @@ detect a hard lockup condition.
 
 ==============================================================
 
+stack_erasing_bypass
+
+This parameter can be used to disable kernel stack erasing at the end
+of syscalls for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK.
+
+That erasing reduces the information which kernel stack leak bugs
+can reveal and blocks some uninitialized stack variable attacks.
+The tradeoff is the performance impact: on a single CPU system kernel
+compilation sees a 1% slowdown, other systems and workloads may vary.
+
+  0: do nothing - stack erasing is enabled by default.
+
+  1: enable stack erasing bypass - stack erasing will then remain
+     disabled and STACKLEAK_METRICS will not be updated until the
+     next boot.
+
+==============================================================
+
 tainted:
 
 Non-zero if the kernel has been tainted. Numeric values, which can be
include/linux/stackleak.h +6 -0
index b911b97..e1fc3d1 100644
@@ -22,6 +22,12 @@ static inline void stackleak_task_init(struct task_struct *t)
 	t->prev_lowest_stack = t->lowest_stack;
 # endif
 }
+
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+int stack_erasing_bypass_sysctl(struct ctl_table *table, int write,
+			void __user *buffer, size_t *lenp, loff_t *ppos);
+#endif
+
 #else /* !CONFIG_GCC_PLUGIN_STACKLEAK */
 static inline void stackleak_task_init(struct task_struct *t) { }
 #endif
kernel/stackleak.c +40 -0
index f5c4111..f731c9a 100644
@@ -14,6 +14,36 @@
 
 #include <linux/stackleak.h>
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+#include <linux/jump_label.h>
+
+static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass);
+
+int stack_erasing_bypass_sysctl(struct ctl_table *table, int write,
+			void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	int ret = 0;
+	int state = static_branch_unlikely(&stack_erasing_bypass);
+
+	table->data = &state;
+	table->maxlen = sizeof(int);
+	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	if (ret || !write)
+		return ret;
+
+	/* Stack erasing re-enabling is not supported */
+	if (static_branch_unlikely(&stack_erasing_bypass))
+		return -EOPNOTSUPP;
+
+	if (state) {
+		static_branch_enable(&stack_erasing_bypass);
+		pr_warn("stackleak: stack erasing is disabled until reboot\n");
+	}
+
+	return ret;
+}
+#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
+
 asmlinkage void stackleak_erase(void)
 {
 	/* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
@@ -22,6 +52,11 @@ asmlinkage void stackleak_erase(void)
 	unsigned int poison_count = 0;
 	const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+	if (static_branch_unlikely(&stack_erasing_bypass))
+		return;
+#endif
+
 	/* Search for the poison value in the kernel stack */
 	while (kstack_ptr > boundary && poison_count <= depth) {
 		if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)
@@ -78,6 +113,11 @@ void __used stackleak_track_stack(void)
 	 */
 	unsigned long sp = (unsigned long)&sp;
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+	if (static_branch_unlikely(&stack_erasing_bypass))
+		return;
+#endif
+
 	/*
 	 * Having CONFIG_STACKLEAK_TRACK_MIN_SIZE larger than
 	 * STACKLEAK_SEARCH_DEPTH makes the poison search in
kernel/sysctl.c +14 -1
index 2d9837c..0ac25ca 100644
@@ -91,7 +91,9 @@
 #ifdef CONFIG_CHR_DEV_SG
 #include <scsi/sg.h>
 #endif
-
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+#include <linux/stackleak.h>
+#endif
 #ifdef CONFIG_LOCKUP_DETECTOR
 #include <linux/nmi.h>
 #endif
@@ -1230,6 +1232,17 @@ static struct ctl_table kern_table[] = {
 		.extra2		= &one,
 	},
 #endif
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+	{
+		.procname	= "stack_erasing_bypass",
+		.data		= NULL,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= stack_erasing_bypass_sysctl,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+#endif
 	{ }
 };
 
scripts/gcc-plugins/Kconfig +10 -0
index 292161d..0028945 100644
@@ -182,4 +182,14 @@ config STACKLEAK_METRICS
 	  can be useful for estimating the STACKLEAK performance impact for
 	  your workloads.
 
+config STACKLEAK_RUNTIME_DISABLE
+	bool "Allow runtime disabling of kernel stack erasing"
+	depends on GCC_PLUGIN_STACKLEAK
+	help
+	  This option provides 'stack_erasing_bypass' sysctl, which can be
+	  used in runtime to disable kernel stack erasing for kernels built
+	  with CONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain
+	  disabled and STACKLEAK_METRICS will not be updated until the
+	  next boot.
+
 endif
Signature · 1 line
2.7.4
#22 lore raw

Re: [PATCH v14 7/7] stackleak, sysctl: Allow runtime disabling of kernel stack erasing

Documentation/sysctl/kernel.txt detect a hard lockup condition. · line 974

Quoted 12 lines · Alexander Popov

Introduce CONFIG_STACKLEAK_RUNTIME_DISABLE option, which provides 'stack_erasing_bypass' sysctl. It can be used in runtime to disable kernel stack erasing for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain disabled and STACKLEAK_METRICS will not be updated until the next boot.

Documentation/sysctl/kernel.txt +4 -0
+That erasing reduces the information which kernel stack leak bugs
+can reveal and blocks some uninitialized stack variable attacks.
+The tradeoff is the performance impact: on a single CPU system kernel
+compilation sees a 1% slowdown, other systems and workloads may vary.

I continue to have a hard time measuring even the 1% impact. Clearly I need some better workloads. :)

kernel/stackleak.c asmlinkage void stackleak_erase(void) · line 58

Quoted 12 lines

[...]
asmlinkage void stackleak_erase(void)

 {
        /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
kernel/stackleak.c +4 -0
@@ -22,6 +52,11 @@ asmlinkage void stackleak_erase(void)
        unsigned int poison_count = 0;
        const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       if (static_branch_unlikely(&stack_erasing_bypass))
+               return;
+#endif

I collapsed this into a macro (and took your other fix) and will push this to my -next tree:

+#define skip_erasing() static_branch_unlikely(&stack_erasing_bypass) +#else
+#define skip_erasing() false
+#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
...
+ if (skip_erasing())
+ return;
+

kernel/stackleak.c void __used stackleak_track_stack(void) · line 119

Quoted 12 lines
kernel/stackleak.c +5 -0
+
        /* Search for the poison value in the kernel stack */
        while (kstack_ptr > boundary && poison_count <= depth) {
                if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)
@@ -78,6 +113,11 @@ void __used stackleak_track_stack(void)
         */
        unsigned long sp = (unsigned long)&sp;
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       if (static_branch_unlikely(&stack_erasing_bypass))
+               return;
+#endif

I would expect stackleak_erase() to be the expensive part, not the tracking part? Shouldn't timings be unchanged by leaving this in unconditionally, which would mean the sysctl could be re-enabled?

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#23 lore raw

Re: [PATCH v14 7/7] stackleak, sysctl: Allow runtime disabling of kernel stack erasing

Quoted 33 lines · Kees Cook

Documentation/sysctl/kernel.txt detect a hard lockup condition. · line 974

Quoted 12 lines · Alexander Popov

Introduce CONFIG_STACKLEAK_RUNTIME_DISABLE option, which provides 'stack_erasing_bypass' sysctl. It can be used in runtime to disable kernel stack erasing for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain disabled and STACKLEAK_METRICS will not be updated until the next boot.

Documentation/sysctl/kernel.txt +4 -0
+That erasing reduces the information which kernel stack leak bugs
+can reveal and blocks some uninitialized stack variable attacks.
+The tradeoff is the performance impact: on a single CPU system kernel
+compilation sees a 1% slowdown, other systems and workloads may vary.

I continue to have a hard time measuring even the 1% impact. Clearly I need some better workloads. :)

kernel/stackleak.c asmlinkage void stackleak_erase(void) · line 58

Quoted 12 lines

[...]
asmlinkage void stackleak_erase(void)

 {
        /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
kernel/stackleak.c +4 -0
@@ -22,6 +52,11 @@ asmlinkage void stackleak_erase(void)
        unsigned int poison_count = 0;
        const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       if (static_branch_unlikely(&stack_erasing_bypass))
+               return;
+#endif

I collapsed this into a macro (and took your other fix) and will push this to my -next tree:

Review

Quoted 8 lines
(quoted diff) +7 -0
+#define skip_erasing() static_branch_unlikely(&stack_erasing_bypass)
+#else
+#define skip_erasing() false
+#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
...
+       if (skip_erasing())
+               return;
+

That's nice! Thank you, I'll test it tomorrow.

Quoted 8 lines
Quoted 8 lines
kernel/stackleak.c +1 -0
+
        /* Search for the poison value in the kernel stack */
        while (kstack_ptr > boundary && poison_count <= depth) {
                if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)
@@ -78,6 +113,11 @@ void __used stackleak_track_stack(void)
         */
        unsigned long sp = (unsigned long)&sp;
 
Quoted 8 lines

kernel/stackleak.c asmlinkage void stackleak_erase(void) · line 58

Quoted 4 lines
kernel/stackleak.c +4 -0
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       if (static_branch_unlikely(&stack_erasing_bypass))
+               return;
+#endif

I would expect stackleak_erase() to be the expensive part, not the tracking part? Shouldn't timings be unchanged by leaving this in unconditionally, which would mean the sysctl could be re-enabled?

Dropping the bypass in stackleak_track_stack() will not help against the troubles with re-enabling stack erasing (tracking and erasing depend on each other). Moreover, it will also make the STACKLEAK_METRICS show insane values. So I think we should have the bypass in both functions.

Best regards,
Alexander

#24 lore raw

Re: [PATCH v14 7/7] stackleak, sysctl: Allow runtime disabling of kernel stack erasing

Quoted 62 lines · Alexander Popov
Quoted 33 lines · Kees Cook

Documentation/sysctl/kernel.txt detect a hard lockup condition. · line 974

Quoted 12 lines · Alexander Popov

Introduce CONFIG_STACKLEAK_RUNTIME_DISABLE option, which provides 'stack_erasing_bypass' sysctl. It can be used in runtime to disable kernel stack erasing for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain disabled and STACKLEAK_METRICS will not be updated until the next boot.

Documentation/sysctl/kernel.txt +4 -0
+That erasing reduces the information which kernel stack leak bugs
+can reveal and blocks some uninitialized stack variable attacks.
+The tradeoff is the performance impact: on a single CPU system kernel
+compilation sees a 1% slowdown, other systems and workloads may vary.

I continue to have a hard time measuring even the 1% impact. Clearly I need some better workloads. :)

kernel/stackleak.c asmlinkage void stackleak_erase(void) · line 58

Quoted 12 lines

[...]
asmlinkage void stackleak_erase(void)

 {
        /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
kernel/stackleak.c +4 -0
@@ -22,6 +52,11 @@ asmlinkage void stackleak_erase(void)
        unsigned int poison_count = 0;
        const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       if (static_branch_unlikely(&stack_erasing_bypass))
+               return;
+#endif

I collapsed this into a macro (and took your other fix) and will push this to my -next tree:

Review

Quoted 8 lines
(quoted diff) +7 -0
+#define skip_erasing() static_branch_unlikely(&stack_erasing_bypass)
+#else
+#define skip_erasing() false
+#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
...
+       if (skip_erasing())
+               return;
+

That's nice! Thank you, I'll test it tomorrow.

Quoted 8 lines
Quoted 8 lines
kernel/stackleak.c +1 -0
+
        /* Search for the poison value in the kernel stack */
        while (kstack_ptr > boundary && poison_count <= depth) {
                if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)
@@ -78,6 +113,11 @@ void __used stackleak_track_stack(void)
         */
        unsigned long sp = (unsigned long)&sp;
 
Quoted 8 lines

kernel/stackleak.c asmlinkage void stackleak_erase(void) · line 58

Quoted 4 lines
kernel/stackleak.c +4 -0
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       if (static_branch_unlikely(&stack_erasing_bypass))
+               return;
+#endif

I would expect stackleak_erase() to be the expensive part, not the tracking part? Shouldn't timings be unchanged by leaving this in unconditionally, which would mean the sysctl could be re-enabled?

Quoted 2 lines

Dropping the bypass in stackleak_track_stack() will not help against the troubles with re-enabling stack erasing (tracking and erasing depend on each

Isn't the tracking checking "sp < current->lowest_stack", so if erasure was off, lowest_stack would only ever get further into the stack? And when erasure was turned back on, it would start getting reset correctly again. Or is the concern the poison searching could break? It seems like it would still work right? I must be missing something. :)

Quoted 2 lines

other). Moreover, it will also make the STACKLEAK_METRICS show insane values. So I think we should have the bypass in both functions.

I left it as-is for now. It should appear in -next tomorrow.

Thanks!

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#25 lore raw

Re: [PATCH v14 7/7] stackleak, sysctl: Allow runtime disabling of kernel stack erasing

Quoted 19 lines · Kees Cook
Quoted 15 lines · Alexander Popov
Quoted 5 lines · Kees Cook
Quoted 4 lines · Alexander Popov
kernel/stackleak.c +0 -0
@@ -78,6 +113,11 @@ void __used stackleak_track_stack(void)
         */
        unsigned long sp = (unsigned long)&sp;
 
Quoted 8 lines

kernel/stackleak.c asmlinkage void stackleak_erase(void) · line 58

Quoted 4 lines
kernel/stackleak.c +4 -0
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       if (static_branch_unlikely(&stack_erasing_bypass))
+               return;
+#endif

I would expect stackleak_erase() to be the expensive part, not the tracking part? Shouldn't timings be unchanged by leaving this in unconditionally, which would mean the sysctl could be re-enabled?

Quoted 2 lines

Dropping the bypass in stackleak_track_stack() will not help against the troubles with re-enabling stack erasing (tracking and erasing depend on each

Quoted 6 lines

Isn't the tracking checking "sp < current->lowest_stack", so if erasure was off, lowest_stack would only ever get further into the stack? And when erasure was turned back on, it would start getting reset correctly again. Or is the concern the poison searching could break? It seems like it would still work right? I must be missing something. :)

Umm.. You are right, that would be a solution. Let's assume that we:
- allow stackleak_track_stack() to work,
- skip stackleak_erase() giving most of performance penalty.
When we enable the 'stack_erasing_bypass', the 'lowest_stack' is not reset at the end of syscall, it just continues to go down at next syscalls (because of enabled tracking). In some sense it is similar to having a very long syscall. Now if we re-enable erasing, the poison search in stackleak_erase() starts from the _valid_ 'lowest_stack', which should work fine.

I'll send the improved version of the patch soon. Thanks!

Best regards,
Alexander

#26 lore raw

PATCH v14 7/7 stackleak: Allow runtime disabling of kernel stack erasing

Introduce CONFIG_STACKLEAK_RUNTIME_DISABLE option, which provides 'stack_erasing' sysctl. It can be used in runtime to control kernel stack erasing for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK.

---
 Documentation/sysctl/kernel.txt | 18 ++++++++++++++++++
 include/linux/stackleak.h       |  6 ++++++
 kernel/stackleak.c              | 38 ++++++++++++++++++++++++++++++++++++++
 kernel/sysctl.c                 | 15 ++++++++++++++-
 scripts/gcc-plugins/Kconfig     |  8 ++++++++
 5 files changed, 84 insertions(+), 1 deletion(-)
Documentation/sysctl/kernel.txt +18 -0
index eded671d..1feae79 100644
@@ -87,6 +87,7 @@ show up in /proc/sys/kernel:
 - shmmni
 - softlockup_all_cpu_backtrace
 - soft_watchdog
+- stack_erasing
 - stop-a                      [ SPARC only ]
 - sysrq                       ==> Documentation/admin-guide/sysrq.rst
 - sysctl_writes_strict
@@ -962,6 +963,23 @@ detect a hard lockup condition.
 
 ==============================================================
 
+stack_erasing
+
+This parameter can be used to control kernel stack erasing at the end
+of syscalls for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK.
+
+That erasing reduces the information which kernel stack leak bugs
+can reveal and blocks some uninitialized stack variable attacks.
+The tradeoff is the performance impact: on a single CPU system kernel
+compilation sees a 1% slowdown, other systems and workloads may vary.
+
+  0: kernel stack erasing is disabled, STACKLEAK_METRICS are not updated.
+
+  1: kernel stack erasing is enabled (default), it is performed before
+     returning to the userspace at the end of syscalls.
+
+==============================================================
+
 tainted:
 
 Non-zero if the kernel has been tainted. Numeric values, which can be
include/linux/stackleak.h +6 -0
index b911b97..3d5c327 100644
@@ -22,6 +22,12 @@ static inline void stackleak_task_init(struct task_struct *t)
 	t->prev_lowest_stack = t->lowest_stack;
 # endif
 }
+
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+int stack_erasing_sysctl(struct ctl_table *table, int write,
+			void __user *buffer, size_t *lenp, loff_t *ppos);
+#endif
+
 #else /* !CONFIG_GCC_PLUGIN_STACKLEAK */
 static inline void stackleak_task_init(struct task_struct *t) { }
 #endif
kernel/stackleak.c +38 -0
index f5c4111..2d21372 100644
@@ -14,6 +14,41 @@
 
 #include <linux/stackleak.h>
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+#include <linux/jump_label.h>
+#include <linux/sysctl.h>
+
+static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass);
+
+int stack_erasing_sysctl(struct ctl_table *table, int write,
+			void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	int ret = 0;
+	int state = !static_branch_unlikely(&stack_erasing_bypass);
+	int prev_state = state;
+
+	table->data = &state;
+	table->maxlen = sizeof(int);
+	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	state = !!state;
+	if (ret || !write || state == prev_state)
+		return ret;
+
+	if (state)
+		static_branch_disable(&stack_erasing_bypass);
+	else
+		static_branch_enable(&stack_erasing_bypass);
+
+	pr_warn("stackleak: kernel stack erasing is %s\n",
+					state ? "enabled" : "disabled");
+	return ret;
+}
+
+#define skip_erasing()	static_branch_unlikely(&stack_erasing_bypass)
+#else
+#define skip_erasing()	false
+#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
+
 asmlinkage void stackleak_erase(void)
 {
 	/* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */
@@ -22,6 +57,9 @@ asmlinkage void stackleak_erase(void)
 	unsigned int poison_count = 0;
 	const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
 
+	if (skip_erasing())
+		return;
+
 	/* Search for the poison value in the kernel stack */
 	while (kstack_ptr > boundary && poison_count <= depth) {
 		if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)
kernel/sysctl.c +14 -1
index 2d9837c..8d7e128 100644
@@ -91,7 +91,9 @@
 #ifdef CONFIG_CHR_DEV_SG
 #include <scsi/sg.h>
 #endif
-
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+#include <linux/stackleak.h>
+#endif
 #ifdef CONFIG_LOCKUP_DETECTOR
 #include <linux/nmi.h>
 #endif
@@ -1230,6 +1232,17 @@ static struct ctl_table kern_table[] = {
 		.extra2		= &one,
 	},
 #endif
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+	{
+		.procname	= "stack_erasing",
+		.data		= NULL,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= stack_erasing_sysctl,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+#endif
 	{ }
 };
 
scripts/gcc-plugins/Kconfig +8 -0
index 2535b9d..eb358c6 100644
@@ -185,4 +185,12 @@ config STACKLEAK_METRICS
 	  can be useful for estimating the STACKLEAK performance impact for
 	  your workloads.
 
+config STACKLEAK_RUNTIME_DISABLE
+	bool "Allow runtime disabling of kernel stack erasing"
+	depends on GCC_PLUGIN_STACKLEAK
+	help
+	  This option provides 'stack_erasing' sysctl, which can be used in
+	  runtime to control kernel stack erasing for kernels built with
+	  CONFIG_GCC_PLUGIN_STACKLEAK.
+
 endif
Signature · 1 line
2.7.4
#27 lore raw

Re: [PATCH v14 7/7] stackleak: Allow runtime disabling of kernel stack erasing

Quoted 19 lines · Alexander Popov

Introduce CONFIG_STACKLEAK_RUNTIME_DISABLE option, which provides 'stack_erasing' sysctl. It can be used in runtime to control kernel stack erasing for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK.

---
 Documentation/sysctl/kernel.txt | 18 ++++++++++++++++++
 include/linux/stackleak.h       |  6 ++++++
 kernel/stackleak.c              | 38 ++++++++++++++++++++++++++++++++++++++
 kernel/sysctl.c                 | 15 ++++++++++++++-
 scripts/gcc-plugins/Kconfig     |  8 ++++++++
 5 files changed, 84 insertions(+), 1 deletion(-)
Documentation/sysctl/kernel.txt +0 -0
index eded671d..1feae79 100644

Documentation/sysctl/kernel.txt show up in /proc/sys/kernel: · line 90

Quoted 4 lines
Documentation/sysctl/kernel.txt +1 -0
@@ -87,6 +87,7 @@ show up in /proc/sys/kernel:
 - shmmni
 - softlockup_all_cpu_backtrace
 - soft_watchdog
+- stack_erasing

I like the renaming to avoid the double-negative. ("disable bypassing" is not as clear as "feature enabled or not")

Quoted 69 lines
Documentation/sysctl/kernel.txt +17 -0
 - stop-a                      [ SPARC only ]
 - sysrq                       ==> Documentation/admin-guide/sysrq.rst
 - sysctl_writes_strict
@@ -962,6 +963,23 @@ detect a hard lockup condition.
 
 ==============================================================
 
+stack_erasing
+
+This parameter can be used to control kernel stack erasing at the end
+of syscalls for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK.
+
+That erasing reduces the information which kernel stack leak bugs
+can reveal and blocks some uninitialized stack variable attacks.
+The tradeoff is the performance impact: on a single CPU system kernel
+compilation sees a 1% slowdown, other systems and workloads may vary.
+
+  0: kernel stack erasing is disabled, STACKLEAK_METRICS are not updated.
+
+  1: kernel stack erasing is enabled (default), it is performed before
+     returning to the userspace at the end of syscalls.
+
+==============================================================
+
 tainted:
 
 Non-zero if the kernel has been tainted. Numeric values, which can be
include/linux/stackleak.h +6 -0
index b911b97..3d5c327 100644
@@ -22,6 +22,12 @@ static inline void stackleak_task_init(struct task_struct *t)
        t->prev_lowest_stack = t->lowest_stack;
 # endif
 }
+
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+int stack_erasing_sysctl(struct ctl_table *table, int write,
+                       void __user *buffer, size_t *lenp, loff_t *ppos);
+#endif
+
 #else /* !CONFIG_GCC_PLUGIN_STACKLEAK */
 static inline void stackleak_task_init(struct task_struct *t) { }
 #endif
kernel/stackleak.c +17 -0
index f5c4111..2d21372 100644
@@ -14,6 +14,41 @@
 
 #include <linux/stackleak.h>
 
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+#include <linux/jump_label.h>
+#include <linux/sysctl.h>
+
+static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass);
+
+int stack_erasing_sysctl(struct ctl_table *table, int write,
+                       void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int ret = 0;
+       int state = !static_branch_unlikely(&stack_erasing_bypass);
+       int prev_state = state;
+
+       table->data = &state;
+       table->maxlen = sizeof(int);
+       ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+       state = !!state;

kernel/stackleak.c line 43

Quoted 10 lines
kernel/stackleak.c +10 -0
+       if (ret || !write || state == prev_state)
+               return ret;
+
+       if (state)
+               static_branch_disable(&stack_erasing_bypass);
+       else
+               static_branch_enable(&stack_erasing_bypass);
+
+       pr_warn("stackleak: kernel stack erasing is %s\n",
+                                       state ? "enabled" : "disabled");

Looks good to me. I've updated the patch for -next.

Quoted 12 lines
kernel/stackleak.c +8 -0
+       return ret;
+}
+
+#define skip_erasing() static_branch_unlikely(&stack_erasing_bypass)
+#else
+#define skip_erasing() false
+#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */
+
 asmlinkage void stackleak_erase(void)
 {
        /* It would be nice not to have 'kstack_ptr' and 'boundary' on stack */

kernel/stackleak.c asmlinkage void stackleak_erase(void) · line 65

Quoted 9 lines
kernel/stackleak.c +3 -0
@@ -22,6 +57,9 @@ asmlinkage void stackleak_erase(void)
        unsigned int poison_count = 0;
        const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
 
+       if (skip_erasing())
+               return;
+
        /* Search for the poison value in the kernel stack */
        while (kstack_ptr > boundary && poison_count <= depth) {
                if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)

Thanks!

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#28 lore raw

cover Stackleak for arm64

In reply to Alexander Popov

Hi,

This is the new version of stackleak for arm64 to go with v14 of the series for x86. I have a cover letter and few more cc's to go along with some of the prep work.

I also apologize for terrible versioning on these series. Usually I try to just reply to the top level patch with this addition and not actually put a version on it. I negelcted to do that last time but as mentioned this is inteded to go with v14.

Laura Abbott (2):
arm64: Introduce current_stack_type
arm64: Clear the stack

 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 17 +++++
 arch/arm64/include/asm/sdei.h         |  8 ++-
 arch/arm64/include/asm/stacktrace.h   | 94 ++++++++++++++++++++++-----
 arch/arm64/kernel/entry.S             |  7 ++
 arch/arm64/kernel/process.c           | 32 +++++++++
 arch/arm64/kernel/ptrace.c            |  2 +-
 arch/arm64/kernel/sdei.c              | 21 +++++-
 arch/arm64/kvm/hyp/Makefile           |  3 +-
 drivers/firmware/efi/libstub/Makefile |  3 +-
 include/linux/stackleak.h             |  1 +
 11 files changed, 165 insertions(+), 24 deletions(-)
Signature · 1 line
2.17.1
#29 lore raw

PATCH 1/2 arm64: Introduce current_stack_type

In preparation for enabling the stackleak plugin on arm64, we need a way to get the bounds of the current stack. Introduce a new primitive current_stack_type which is similar to x86's get_stack_info. Utilize that to rework on_accessible_stack slightly as well.

---
So this did end up looking quite a bit like get_stack_info but I didn't really see the need to integrate this more than this. I do think actually enumerating the types makes things a bit cleaner.

---
 arch/arm64/include/asm/sdei.h       |  8 ++-
 arch/arm64/include/asm/stacktrace.h | 94 ++++++++++++++++++++++++-----
 arch/arm64/kernel/ptrace.c          |  2 +-
 arch/arm64/kernel/sdei.c            | 21 ++++++-
 4 files changed, 103 insertions(+), 22 deletions(-)
arch/arm64/include/asm/sdei.h +5 -3
index e073e6886685..34f7b203845b 100644
@@ -40,15 +40,17 @@ asmlinkage unsigned long __sdei_handler(struct pt_regs *regs,
 unsigned long sdei_arch_get_entry_point(int conduit);
 #define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)
 
-bool _on_sdei_stack(unsigned long sp);
-static inline bool on_sdei_stack(unsigned long sp)
+bool _on_sdei_stack(unsigned long sp, unsigned long *, unsigned long *);
+static inline bool on_sdei_stack(unsigned long sp,
+				unsigned long *stack_low,
+				unsigned long *stack_high)
 {
 	if (!IS_ENABLED(CONFIG_VMAP_STACK))
 		return false;
 	if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
 		return false;
 	if (in_nmi())
-		return _on_sdei_stack(sp);
+		return _on_sdei_stack(sp, stack_low, stack_high);
 
 	return false;
 }
arch/arm64/include/asm/stacktrace.h +79 -15

133 lines not on this page · raw message

arch/arm64/kernel/ptrace.c +1 -1
index 5c338ce5a7fa..a6b3a2be7561 100644
@@ -132,7 +132,7 @@ static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
 {
 	return ((addr & ~(THREAD_SIZE - 1))  ==
 		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
-		on_irq_stack(addr);
+		on_irq_stack(addr, NULL, NULL);
 }
 
 /**
arch/arm64/kernel/sdei.c +18 -3
index 6b8d90d5ceae..8e18913a53fd 100644
@@ -88,7 +88,9 @@ static int init_sdei_stacks(void)
 	return err;
 }
 
-bool _on_sdei_stack(unsigned long sp)
+bool _on_sdei_stack(unsigned long sp,
+		unsigned long *stack_low,
+		unsigned long *stack_high)
 {
 	unsigned long low, high;
 
@@ -98,13 +100,26 @@ bool _on_sdei_stack(unsigned long sp)
 	low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr);
 	high = low + SDEI_STACK_SIZE;
 
-	if (low <= sp && sp < high)
+	if (low <= sp && sp < high) {
+		if (stack_low && stack_high) {
+			*stack_low = low;
+			*stack_high = high;
+		}
 		return true;
+	}
 
 	low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
 	high = low + SDEI_STACK_SIZE;
 
-	return (low <= sp && sp < high);
+	if (low <= sp && sp < high) {
+		if (stack_low && stack_high) {
+			*stack_low = low;
+			*stack_high = high;
+		}
+		return true;
+	}
+
+	return false;
 }
 
 unsigned long sdei_arch_get_entry_point(int conduit)
Signature · 1 line
2.17.1
reviewer
#30 lore raw

Re: [PATCH 1/2] arm64: Introduce current_stack_type

Hi Laura,

Quoted 64 lines · Laura Abbott

In preparation for enabling the stackleak plugin on arm64, we need a way to get the bounds of the current stack. Introduce a new primitive current_stack_type which is similar to x86's get_stack_info. Utilize that to rework on_accessible_stack slightly as well.

---
So this did end up looking quite a bit like get_stack_info but I didn't really see the need to integrate this more than this. I do think actually enumerating the types makes things a bit cleaner.

---
 arch/arm64/include/asm/sdei.h       |  8 ++-
 arch/arm64/include/asm/stacktrace.h | 94 ++++++++++++++++++++++++-----
 arch/arm64/kernel/ptrace.c          |  2 +-
 arch/arm64/kernel/sdei.c            | 21 ++++++-
 4 files changed, 103 insertions(+), 22 deletions(-)
arch/arm64/include/asm/sdei.h +5 -3
index e073e6886685..34f7b203845b 100644
@@ -40,15 +40,17 @@ asmlinkage unsigned long __sdei_handler(struct pt_regs *regs,
 unsigned long sdei_arch_get_entry_point(int conduit);
 #define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)
 
-bool _on_sdei_stack(unsigned long sp);
-static inline bool on_sdei_stack(unsigned long sp)
+bool _on_sdei_stack(unsigned long sp, unsigned long *, unsigned long *);
+static inline bool on_sdei_stack(unsigned long sp,
+				unsigned long *stack_low,
+				unsigned long *stack_high)
 {
 	if (!IS_ENABLED(CONFIG_VMAP_STACK))
 		return false;
 	if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
 		return false;
 	if (in_nmi())
-		return _on_sdei_stack(sp);
+		return _on_sdei_stack(sp, stack_low, stack_high);
 
 	return false;
 }
arch/arm64/include/asm/stacktrace.h +3 -2
index 902f9edacbea..9855a0425e64 100644
@@ -39,7 +39,9 @@ extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk);
 
 DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
 
-static inline bool on_irq_stack(unsigned long sp)
+static inline bool on_irq_stack(unsigned long sp,
+				unsigned long *stack_low,
+				unsigned long *stack_high)
 {
 	unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
 	unsigned long high = low + IRQ_STACK_SIZE;
@@ -47,47 +49,109 @@ static inline bool on_irq_stack(unsigned long sp)
 	if (!low)
 		return false;
 
-	return (low <= sp && sp < high);

arch/arm64/include/asm/stacktrace.h static inline bool on_irq_stack(unsigned long sp) · line 61

Quoted 10 lines
arch/arm64/include/asm/stacktrace.h +9 -0
+	if (sp < low || sp >= high)
+		return false;
+
+	if (stack_low && stack_high) {
+		*stack_low = low;
+		*stack_high = high;
+	}
+
+	return true;
 }

Rather than having to pass separete pointers to low/high, could we please wrap them up as a struct, e.g.

struct stack_info {
	unsigned long low, high;
	stack_type type;
}

... and pass a single pointer to that? e.g.

static inline bool on_irq_stack(unsigned long sp, struct stack_info *info)

{
	unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
	unsigned long high = low + IRQ_STACK_SIZE;

	if (!low)
		return false;
	
	if (sp < low || sp >= high)
		return false;
	
	if (info) {
		info->low = low;
		info->high = high;
		info->type = STACK_TYPE_IRQ;
	}

	return true;
}

That simplified the prototypes, and will allow us to distinguish the two SDEI stacks (which we'll need for making stack unwiding robust to cross-stack loops).

Quoted 46 lines
arch/arm64/include/asm/stacktrace.h +26 -5
-static inline bool on_task_stack(struct task_struct *tsk, unsigned long sp)
+static inline bool on_task_stack(struct task_struct *tsk, unsigned long sp,
+				unsigned long *stack_low,
+				unsigned long *stack_high)
 {
 	unsigned long low = (unsigned long)task_stack_page(tsk);
 	unsigned long high = low + THREAD_SIZE;
 
-	return (low <= sp && sp < high);
+	if (sp < low || sp >= high)
+		return false;
+
+	if (stack_low && stack_high) {
+		*stack_low = low;
+		*stack_high = high;
+	}
+
+	return true;
 }
 
 #ifdef CONFIG_VMAP_STACK
 DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack);
 
-static inline bool on_overflow_stack(unsigned long sp)
+static inline bool on_overflow_stack(unsigned long sp,
+				unsigned long *stack_low,
+				unsigned long *stack_high)
 {
 	unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack);
 	unsigned long high = low + OVERFLOW_STACK_SIZE;
 
-	return (low <= sp && sp < high);
+	if (sp < low || sp >= high)
+		return false;
+
+	if (stack_low && stack_high) {
+		*stack_low = low;
+		*stack_high = high;
+	}
+
+	return true;
 }
 #else
-static inline bool on_overflow_stack(unsigned long sp) { return false; }
+static inline bool on_overflow_stack(unsigned long sp,
+			unsigned long *stack_low,

arch/arm64/include/asm/stacktrace.h static inline bool on_irq_stack(unsigned long sp) · line 113

Quoted 10 lines
arch/arm64/include/asm/stacktrace.h +8 -0
+			unsigned long *stack_high) { return false; }
 #endif
 
+enum stack_type {
+	STACK_TYPE_UNKNOWN,
+	STACK_TYPE_TASK,
+	STACK_TYPE_IRQ,
+	STACK_TYPE_OVERFLOW,
+	STACK_TYPE_SDEI,
+};

For SDEI we'll need STACK_TYPE_SDEI_NORMAL and STACK_TYPE_SDEI_CRITICAL, at least for stack unwinding.

Quoted 42 lines
arch/arm64/include/asm/stacktrace.h +32 -2
+
+static inline enum stack_type current_stack_type(struct task_struct *tsk,
+						unsigned long sp,
+						unsigned long *stack_low,
+						unsigned long *stack_high)
+{
+	if (on_task_stack(tsk, sp, stack_low, stack_high))
+		return STACK_TYPE_TASK;
+	if (on_irq_stack(sp, stack_low, stack_high))
+		return STACK_TYPE_IRQ;
+	if (on_overflow_stack(sp, stack_low, stack_high))
+		return STACK_TYPE_OVERFLOW;
+	if (on_sdei_stack(sp, stack_low, stack_high))
+		return STACK_TYPE_SDEI;
+	return STACK_TYPE_UNKNOWN;
+}
+
 /*
  * We can only safely access per-cpu stacks from current in a non-preemptible
  * context.
  */
 static inline bool on_accessible_stack(struct task_struct *tsk, unsigned long sp)
 {
-	if (on_task_stack(tsk, sp))
+	enum stack_type type;
+	unsigned long low, high;
+
+	type = current_stack_type(tsk, sp, &low, &high);
+
+	switch (type) {
+	case STACK_TYPE_TASK:
 		return true;
-	if (tsk != current || preemptible())
+	case STACK_TYPE_IRQ:
+	case STACK_TYPE_OVERFLOW:
+	case STACK_TYPE_SDEI:
+		if (tsk != current || preemptible())
+			return false;
+		else
+			return true;
+	case STACK_TYPE_UNKNOWN:
 		return false;

arch/arm64/include/asm/stacktrace.h static inline bool on_irq_stack(unsigned long sp) · line 157

Quoted 10 lines
arch/arm64/include/asm/stacktrace.h +1 -6
-	if (on_irq_stack(sp))
-		return true;
-	if (on_overflow_stack(sp))
-		return true;
-	if (on_sdei_stack(sp))
-		return true;
+	}
 
 	return false;
 }

With the stacut stack_info, I think we can leave the logic of on_accessible_stack() as-is, modulo a new info parameter that we pass on into each on_<foo>_stack(), and then we don't neeed a separate current_stack_type() function.

Quoted 45 lines
arch/arm64/kernel/ptrace.c +1 -1
index 5c338ce5a7fa..a6b3a2be7561 100644
@@ -132,7 +132,7 @@ static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
 {
 	return ((addr & ~(THREAD_SIZE - 1))  ==
 		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
-		on_irq_stack(addr);
+		on_irq_stack(addr, NULL, NULL);
 }
 
 /**
arch/arm64/kernel/sdei.c +9 -3
index 6b8d90d5ceae..8e18913a53fd 100644
@@ -88,7 +88,9 @@ static int init_sdei_stacks(void)
 	return err;
 }
 
-bool _on_sdei_stack(unsigned long sp)
+bool _on_sdei_stack(unsigned long sp,
+		unsigned long *stack_low,
+		unsigned long *stack_high)
 {
 	unsigned long low, high;
 
@@ -98,13 +100,26 @@ bool _on_sdei_stack(unsigned long sp)
 	low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr);
 	high = low + SDEI_STACK_SIZE;
 
-	if (low <= sp && sp < high)
+	if (low <= sp && sp < high) {
+		if (stack_low && stack_high) {
+			*stack_low = low;
+			*stack_high = high;
+		}
 		return true;
+	}
 
 	low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
 	high = low + SDEI_STACK_SIZE;
 
-	return (low <= sp && sp < high);

arch/arm64/kernel/sdei.c bool _on_sdei_stack(unsigned long sp) · line 123

Quoted 10 lines
arch/arm64/kernel/sdei.c +9 -0
+	if (low <= sp && sp < high) {
+		if (stack_low && stack_high) {
+			*stack_low = low;
+			*stack_high = high;
+		}
+		return true;
+	}
+
+	return false;
 }

We should probably split this into separate on_sdei_normal_stack() and on_sdei_critical_stack() functions.

Then we can do:

bool on_sdei_<foo>_stack(...)

{
	if (<out of bounds>)
		return false;
	
	if (info) {
		<assign info fields>;
	}

	return true;
}

bool _on_sdei_stack(unsigned long sp, struct stack_info *info)

{
	if (on_sedi_critical_stack(sp, info))
		return true;
	if (on_sdei_normal_stack(sp, info))
		return true;
	
	return false;
}

... which is a little nicer for legibility.

Otherwise, this looks good to me.

Thanks,
Mark.

#31 lore raw

PATCH 2/2 arm64: Clear the stack

In reply to Laura Abbott

Implementation of stackleak based heavily on the x86 version

---
Since last time: Minor style cleanups. Re-wrote check_alloca to correctly handle all stack types. While doing that, I also realized current_top_of_stack was incorrect so I fixed that as well.

---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 17 ++++++++++++++
 arch/arm64/kernel/entry.S             |  7 ++++++
 arch/arm64/kernel/process.c           | 32 +++++++++++++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 include/linux/stackleak.h             |  1 +
 7 files changed, 62 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +17 -0
index a73ae1e49200..4f3062ee22c6 100644
@@ -266,5 +266,22 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)	sve_set_current_vl(arg)
 #define SVE_GET_VL()	sve_get_current_vl()
 
+/*
+ * For CONFIG_STACKLEAK
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.
+ */
+
+#define current_top_of_stack()		\
+({					\
+	unsigned long _low = 0;		\
+	unsigned long _high = 0;	\
+					\
+	current_stack_type(current, current_stack_pointer, &_low, &_high); \
+	_high;	\
+})
+#define on_thread_stack()	(on_task_stack(current, current_stack_pointer, NULL, NULL))
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	stackleak_erase
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
 	and	x2, x1, #_TIF_WORK_MASK
 	cbnz	x2, work_pending
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ret_fast_syscall_trace:
 	enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +32 -0
index e10bc363f533..904defa36689 100644
@@ -493,3 +493,35 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)
+{
+	unsigned long stack_left;
+	enum stack_type type;
+	unsigned long current_sp = current_stack_pointer;
+	unsigned long low, high;
+
+	type = current_stack_type(current, current_sp, &low, &high);
+	BUG_ON(type == STACK_TYPE_UNKNOWN);
+
+	stack_left = current_sp - low;
+
+	if (size >= stack_left) {
+		/*
+		 * Kernel stack depth overflow is detected, let's report that.
+		 * If CONFIG_VMAP_STACK is enabled, we can safely use BUG().
+		 * If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt
+		 * the neighbour memory. CONFIG_SCHED_STACK_END_CHECK calls
+		 * panic() in a similar situation, so let's do the same if that
+		 * option is on. Otherwise just use BUG() and hope for the best.
+		 */
+#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
+		panic("alloca() over the kernel stack boundary\n");
+#else
+		BUG();
+#endif
+	}
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+		$(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -1
index a34e9290a699..25dd2a14560d 100644
@@ -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
include/linux/stackleak.h +1 -0
index b911b973d328..08420ec6b7c3 100644
@@ -5,6 +5,7 @@
 #include <linux/sched.h>
 #include <linux/sched/task_stack.h>
 
+#include <asm/stacktrace.h>
 /*
  * Check that the poison value points to the unused hole in the
  * virtual memory map for your platform.
Signature · 1 line
2.17.1
Reviewed-by on v1 2/2
#32 lore raw

Re: [PATCH 2/2] arm64: Clear the stack

Quoted 8 lines · Laura Abbott

Implementation of stackleak based heavily on the x86 version

---
Since last time: Minor style cleanups. Re-wrote check_alloca to correctly handle all stack types. While doing that, I also realized current_top_of_stack was incorrect so I fixed that as well.

Can you drop the include/linux/stackleak.h change from this series? I've included that in the v14 in linux-next already, so that these patches can land entirely separately in the arm64 tree (which was the request).

Otherwise, this looks great!

-Kees

Quoted 164 lines
---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 17 ++++++++++++++
 arch/arm64/kernel/entry.S             |  7 ++++++
 arch/arm64/kernel/process.c           | 32 +++++++++++++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 include/linux/stackleak.h             |  1 +
 7 files changed, 62 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +17 -0
index a73ae1e49200..4f3062ee22c6 100644
@@ -266,5 +266,22 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)        sve_set_current_vl(arg)
 #define SVE_GET_VL()   sve_get_current_vl()
 
+/*
+ * For CONFIG_STACKLEAK
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.
+ */
+
+#define current_top_of_stack()         \
+({                                     \
+       unsigned long _low = 0;         \
+       unsigned long _high = 0;        \
+                                       \
+       current_stack_type(current, current_stack_pointer, &_low, &_high); \
+       _high;  \
+})
+#define on_thread_stack()      (on_task_stack(current, current_stack_pointer, NULL, NULL))
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk        .req    x28             // current thread_info
 
        .text
 
+       .macro  stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+       bl      stackleak_erase
+#endif
+       .endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
        and     x2, x1, #_TIF_WORK_MASK
        cbnz    x2, work_pending
        enable_step_tsk x1, x2
+       stackleak_erase
        kernel_exit 0
 ret_fast_syscall_trace:
        enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
        cbnz    x2, work_pending
 finish_ret_to_user:
        enable_step_tsk x1, x2
+       stackleak_erase
        kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +32 -0
index e10bc363f533..904defa36689 100644
@@ -493,3 +493,35 @@ void arch_setup_new_exec(void)
 {
        current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)
+{
+       unsigned long stack_left;
+       enum stack_type type;
+       unsigned long current_sp = current_stack_pointer;
+       unsigned long low, high;
+
+       type = current_stack_type(current, current_sp, &low, &high);
+       BUG_ON(type == STACK_TYPE_UNKNOWN);
+
+       stack_left = current_sp - low;
+
+       if (size >= stack_left) {
+               /*
+                * Kernel stack depth overflow is detected, let's report that.
+                * If CONFIG_VMAP_STACK is enabled, we can safely use BUG().
+                * If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt
+                * the neighbour memory. CONFIG_SCHED_STACK_END_CHECK calls
+                * panic() in a similar situation, so let's do the same if that
+                * option is on. Otherwise just use BUG() and hope for the best.
+                */
+#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
+               panic("alloca() over the kernel stack boundary\n");
+#else
+               BUG();
+#endif
+       }
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+               $(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -1
index a34e9290a699..25dd2a14560d 100644
@@ -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
include/linux/stackleak.h +1 -1
index b911b973d328..08420ec6b7c3 100644
@@ -5,6 +5,7 @@
 #include <linux/sched.h>
 #include <linux/sched/task_stack.h>
 
+#include <asm/stacktrace.h>
 /*
  * Check that the poison value points to the unused hole in the
  * virtual memory map for your platform.
--

2.17.1

Signature · 2 lines
Kees Cook
Pixel Security
#33 lore raw

Re: [PATCH 2/2] arm64: Clear the stack

In reply to Laura Abbott

Hello Laura,

Thanks again for your work.
Please see some comments below.

Quoted 34 lines · Laura Abbott

Implementation of stackleak based heavily on the x86 version

---
Since last time: Minor style cleanups. Re-wrote check_alloca to correctly handle all stack types. While doing that, I also realized current_top_of_stack was incorrect so I fixed that as well.

---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 17 ++++++++++++++
 arch/arm64/kernel/entry.S             |  7 ++++++
 arch/arm64/kernel/process.c           | 32 +++++++++++++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 include/linux/stackleak.h             |  1 +
 7 files changed, 62 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +0 -0
index a73ae1e49200..4f3062ee22c6 100644

arch/arm64/include/asm/processor.h extern void __init minsigstksz_setup(void); · line 270

Quoted 5 lines
arch/arm64/include/asm/processor.h +2 -0
@@ -266,5 +266,22 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)	sve_set_current_vl(arg)
 #define SVE_GET_VL()	sve_get_current_vl()
 
+/*
+ * For CONFIG_STACKLEAK

Our config option is called CONFIG_GCC_PLUGIN_STACKLEAK.

arch/arm64/include/asm/processor.h extern void __init minsigstksz_setup(void); · line 283

Quoted 13 lines
arch/arm64/include/asm/processor.h +13 -0
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.
+ */
+
+#define current_top_of_stack()		\
+({					\
+	unsigned long _low = 0;		\
+	unsigned long _high = 0;	\
+					\
+	current_stack_type(current, current_stack_pointer, &_low, &_high); \
+	_high;	\
+})

Do you really need _low here? Ah, I see this in the previous patch:
+ if (stack_low && stack_high) {
+ *stack_low = low;
+ *stack_high = high;
+ }

How about checking them against NULL separately? That would allow
+ current_stack_type(current, current_stack_pointer, NULL, &_high);

Also a minor comment - how about aligning backslashes?

Quoted 62 lines
arch/arm64/include/asm/processor.h +2 -0
+#define on_thread_stack()	(on_task_stack(current, current_stack_pointer, NULL, NULL))
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	stackleak_erase
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
 	and	x2, x1, #_TIF_WORK_MASK
 	cbnz	x2, work_pending
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ret_fast_syscall_trace:
 	enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +18 -0
index e10bc363f533..904defa36689 100644
@@ -493,3 +493,35 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)
+{
+	unsigned long stack_left;
+	enum stack_type type;
+	unsigned long current_sp = current_stack_pointer;
+	unsigned long low, high;
+
+	type = current_stack_type(current, current_sp, &low, &high);
+	BUG_ON(type == STACK_TYPE_UNKNOWN);
+
+	stack_left = current_sp - low;
+
+	if (size >= stack_left) {
+		/*
+		 * Kernel stack depth overflow is detected, let's report that.
+		 * If CONFIG_VMAP_STACK is enabled, we can safely use BUG().

arch/arm64/kernel/process.c void arch_setup_new_exec(void) · line 523

Quoted 10 lines
arch/arm64/kernel/process.c +10 -0
+		 * If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt
+		 * the neighbour memory. CONFIG_SCHED_STACK_END_CHECK calls
+		 * panic() in a similar situation, so let's do the same if that
+		 * option is on. Otherwise just use BUG() and hope for the best.
+		 */
+#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
+		panic("alloca() over the kernel stack boundary\n");
+#else
+		BUG();
+#endif

This comment and #if logic should be dropped, we should always use panic() here on arm64. Mark Rutland and I have worked out the solution for arm64 in this thread:
http://openwall.com/lists/kernel-hardening/2018/05/11/12

Rationale: on arm64 with CONFIG_VMAP_STACK, a stack overflow results in panic() anyway.

Quoted 45 lines
arch/arm64/kernel/process.c +4 -0
+	}
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+		$(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -1
index a34e9290a699..25dd2a14560d 100644
@@ -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
include/linux/stackleak.h +1 -0
index b911b973d328..08420ec6b7c3 100644
@@ -5,6 +5,7 @@
 #include <linux/sched.h>
 #include <linux/sched/task_stack.h>
 
+#include <asm/stacktrace.h>
 /*
  * Check that the poison value points to the unused hole in the
  * virtual memory map for your platform.
reviewer
#34 lore raw

Re: [PATCH 2/2] arm64: Clear the stack

In reply to Laura Abbott
Quoted 43 lines · Laura Abbott

Implementation of stackleak based heavily on the x86 version

---
Since last time: Minor style cleanups. Re-wrote check_alloca to correctly handle all stack types. While doing that, I also realized current_top_of_stack was incorrect so I fixed that as well.

---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 17 ++++++++++++++
 arch/arm64/kernel/entry.S             |  7 ++++++
 arch/arm64/kernel/process.c           | 32 +++++++++++++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 include/linux/stackleak.h             |  1 +
 7 files changed, 62 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +5 -0
index a73ae1e49200..4f3062ee22c6 100644
@@ -266,5 +266,22 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)	sve_set_current_vl(arg)
 #define SVE_GET_VL()	sve_get_current_vl()
 
+/*
+ * For CONFIG_STACKLEAK
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.

arch/arm64/include/asm/processor.h extern void __init minsigstksz_setup(void); · line 283

Quoted 10 lines
arch/arm64/include/asm/processor.h +10 -0
+ */
+
+#define current_top_of_stack()		\
+({					\
+	unsigned long _low = 0;		\
+	unsigned long _high = 0;	\
+					\
+	current_stack_type(current, current_stack_pointer, &_low, &_high); \
+	_high;	\
+})

... with the info changes, this could be:

#define current_top_of_stack() \

({									\
	struct stack_info _info;					\
	BUG_ON(!on_accessible_stack(current_stack_pinter, &_info));	\
	_info->high;							\
})

arch/arm64/include/asm/processor.h extern void __init minsigstksz_setup(void); · line 284

Quoted 1 line
arch/arm64/include/asm/processor.h +1 -0
+#define on_thread_stack()	(on_task_stack(current, current_stack_pointer, NULL, NULL))

... and one fewer NULL here.

Quoted 46 lines
arch/arm64/include/asm/processor.h +1 -0
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	stackleak_erase
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
 	and	x2, x1, #_TIF_WORK_MASK
 	cbnz	x2, work_pending
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ret_fast_syscall_trace:
 	enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +3 -0
index e10bc363f533..904defa36689 100644
@@ -493,3 +493,35 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)

arch/arm64/kernel/process.c void arch_setup_new_exec(void) · line 508

Quoted 10 lines
arch/arm64/kernel/process.c +10 -0
+{
+	unsigned long stack_left;
+	enum stack_type type;
+	unsigned long current_sp = current_stack_pointer;
+	unsigned long low, high;
+
+	type = current_stack_type(current, current_sp, &low, &high);
+	BUG_ON(type == STACK_TYPE_UNKNOWN);
+
+	stack_left = current_sp - low;

... similarly with the info changes, this could be:

	unsigned long current_sp = current_stack_pointer;
	unsigned long stack_left;
	struct stack_info info;

	BUG_ON(!on_accessible_stack(current, current_sp, &info));

	stack_lead = current_sp - info->low;

Otherwise, this looks good to me.

Thanks,
Mark.

Quoted 62 lines
arch/arm64/kernel/process.c +19 -0
+
+	if (size >= stack_left) {
+		/*
+		 * Kernel stack depth overflow is detected, let's report that.
+		 * If CONFIG_VMAP_STACK is enabled, we can safely use BUG().
+		 * If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt
+		 * the neighbour memory. CONFIG_SCHED_STACK_END_CHECK calls
+		 * panic() in a similar situation, so let's do the same if that
+		 * option is on. Otherwise just use BUG() and hope for the best.
+		 */
+#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK)
+		panic("alloca() over the kernel stack boundary\n");
+#else
+		BUG();
+#endif
+	}
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+		$(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -1
index a34e9290a699..25dd2a14560d 100644
@@ -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
include/linux/stackleak.h +1 -0
index b911b973d328..08420ec6b7c3 100644
@@ -5,6 +5,7 @@
 #include <linux/sched.h>
 #include <linux/sched/task_stack.h>
 
+#include <asm/stacktrace.h>
 /*
  * Check that the poison value points to the unused hole in the
  * virtual memory map for your platform.
Signature · 1 line
2.17.1
#35 lore raw

v2 cover Stackleak for arm64

In reply to Alexander Popov

Hi,

This is hopefully the final version of the stackleak plugin for arm64. Acks are appreciated!

Laura Abbott (2):
arm64: Add stack information to on_accessible_stack
arm64: Clear the stack

 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 ++++++
 arch/arm64/include/asm/sdei.h         |  9 ++--
 arch/arm64/include/asm/stacktrace.h   | 73 ++++++++++++++++++++++-----
 arch/arm64/kernel/entry.S             |  7 +++
 arch/arm64/kernel/process.c           | 17 +++++++
 arch/arm64/kernel/ptrace.c            |  2 +-
 arch/arm64/kernel/sdei.c              | 51 ++++++++++++++++---
 arch/arm64/kernel/stacktrace.c        |  2 +-
 arch/arm64/kvm/hyp/Makefile           |  3 +-
 drivers/firmware/efi/libstub/Makefile |  3 +-
 11 files changed, 156 insertions(+), 27 deletions(-)
Signature · 1 line
2.17.1
#36 lore raw

PATCH v2 1/2 arm64: Add stack information to on_accessible_stack

In preparation for enabling the stackleak plugin on arm64, we need a way to get the bounds of the current stack. Extend on_accessible_stack to get this information.

---
v2: Switched to using struct stack_info for argument passing. on_accessible_stack is now the primary API. Split STACK_TYPE_SDEI into STACK_TYPE_SDEI_NORMAL and STACK_TYPE_SDEI_CRITICAL.

---
 arch/arm64/include/asm/sdei.h       |  9 ++--
 arch/arm64/include/asm/stacktrace.h | 73 ++++++++++++++++++++++++-----
 arch/arm64/kernel/ptrace.c          |  2 +-
 arch/arm64/kernel/sdei.c            | 51 ++++++++++++++++----
 arch/arm64/kernel/stacktrace.c      |  2 +-
 5 files changed, 112 insertions(+), 25 deletions(-)
arch/arm64/include/asm/sdei.h +6 -3
index e073e6886685..ffe47d766c25 100644
@@ -40,15 +40,18 @@ asmlinkage unsigned long __sdei_handler(struct pt_regs *regs,
 unsigned long sdei_arch_get_entry_point(int conduit);
 #define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)
 
-bool _on_sdei_stack(unsigned long sp);
-static inline bool on_sdei_stack(unsigned long sp)
+struct stack_info;
+
+bool _on_sdei_stack(unsigned long sp, struct stack_info *info);
+static inline bool on_sdei_stack(unsigned long sp,
+				struct stack_info *info)
 {
 	if (!IS_ENABLED(CONFIG_VMAP_STACK))
 		return false;
 	if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
 		return false;
 	if (in_nmi())
-		return _on_sdei_stack(sp);
+		return _on_sdei_stack(sp, info);
 
 	return false;
 }
arch/arm64/include/asm/stacktrace.h +61 -12

120 lines not on this page · raw message

arch/arm64/kernel/ptrace.c +1 -1
index 5c338ce5a7fa..cf94e1498ba6 100644
@@ -132,7 +132,7 @@ static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
 {
 	return ((addr & ~(THREAD_SIZE - 1))  ==
 		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
-		on_irq_stack(addr);
+		on_irq_stack(addr, NULL);
 }
 
 /**
arch/arm64/kernel/sdei.c +43 -8

66 lines not on this page · raw message

arch/arm64/kernel/stacktrace.c +1 -1
index d5718a060672..4989f7ea1e59 100644
@@ -50,7 +50,7 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
 	if (!tsk)
 		tsk = current;
 
-	if (!on_accessible_stack(tsk, fp))
+	if (!on_accessible_stack(tsk, fp, NULL))
 		return -EINVAL;
 
 	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
Signature · 1 line
2.17.1
reviewer Reviewed-by on v2 1/2
#37 lore raw

Re: [PATCHv2 1/2] arm64: Add stack information to on_accessible_stack

Quoted 10 lines · Laura Abbott

In preparation for enabling the stackleak plugin on arm64, we need a way to get the bounds of the current stack. Extend on_accessible_stack to get this information.

---
v2: Switched to using struct stack_info for argument passing. on_accessible_stack is now the primary API. Split STACK_TYPE_SDEI into STACK_TYPE_SDEI_NORMAL and STACK_TYPE_SDEI_CRITICAL.

[...]

Quoted 22 lines
arch/arm64/include/asm/stacktrace.h +12 -2
-static inline bool on_irq_stack(unsigned long sp)
+static inline bool on_irq_stack(unsigned long sp,
+				struct stack_info *info)
 {
 	unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
 	unsigned long high = low + IRQ_STACK_SIZE;
@@ -47,46 +63,79 @@ static inline bool on_irq_stack(unsigned long sp)
 	if (!low)
 		return false;
 
-	return (low <= sp && sp < high);
+	if (sp < low || sp >= high)
+		return false;
+
+	if (info) {
+		info->low = low;
+		info->high = high;
+		info->type = STACK_TYPE_IRQ;
+	}
+
+	return true;
 }

[...]

Quoted 26 lines
arch/arm64/kernel/sdei.c +25 -1
-bool _on_sdei_stack(unsigned long sp)
+bool on_sdei_normal_stack(unsigned long sp,
+			struct stack_info *info)
+{
+	unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
+	unsigned long high = low + SDEI_STACK_SIZE;
+
+	if (low <= sp && sp < high) {
+		if (info) {
+			info->low = low;
+			info->high = high;
+			info->type = STACK_TYPE_SDEI_NORMAL;
+		}
+		return true;
+	}
+
+	return false;
+}
+
+bool on_sdei_critical_stack(unsigned long sp,
+			struct stack_info *info)
+{
+	unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr);
+	unsigned long high = low + SDEI_STACK_SIZE;
+
+	if (low <= sp && sp < high) {

arch/arm64/kernel/sdei.c static int init_sdei_stacks(void) · line 125

Quoted 10 lines
arch/arm64/kernel/sdei.c +10 -0
+		if (info) {
+			info->low = low;
+			info->high = high;
+			info->type = STACK_TYPE_SDEI_CRITICAL;
+		}
+		return true;
+	}
+
+	return false;
+}

Minor nit, but it would be good to avoid the nested conditionals for these two by bailing out early when the SP is out of bounds, as with the other on_<foo>_stack() functions, e.g.

bool on_sdei_normal_stack(unsigned long sp,
struct stack_info *info)

{
	unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
	unsigned long high = low + SDEI_STACK_SIZE;

	if (sp < low || sp >= high)
		return false;

	if (info) {
		info->low = low;
		info->high = high;
		info->type = STACK_TYPE_SDEI_NORMAL;
	}

	return true;
}

Otherwise, this all looks good to me. With that:

Thanks for working on this!

Mark.

#38 lore raw

PATCH v2 2/2 arm64: Clear the stack

In reply to Laura Abbott

Implementation of stackleak based heavily on the x86 version

---
v2: Convert to adjusted on_acessible_stack APIs. Fixed alloca check to just panic. Dropped the extra include per Kees. I also didn't add the Reviewed-by since the APIs did change and I wanted another pass.

---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 +++++++++++++++
 arch/arm64/kernel/entry.S             |  7 +++++++
 arch/arm64/kernel/process.c           | 17 +++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 6 files changed, 44 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +15 -0
index a73ae1e49200..0061450a793b 100644
@@ -266,5 +266,20 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)	sve_set_current_vl(arg)
 #define SVE_GET_VL()	sve_get_current_vl()
 
+/*
+ * For CONFIG_GCC_PLUGIN_STACKLEAK
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.
+ */
+
+#define current_top_of_stack()							\
+({										\
+	struct stack_info _info;						\
+	BUG_ON(!on_accessible_stack(current, current_stack_pointer, &_info));	\
+	_info.high;								\
+})
+#define on_thread_stack()	(on_task_stack(current, current_stack_pointer, NULL))
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	stackleak_erase
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
 	and	x2, x1, #_TIF_WORK_MASK
 	cbnz	x2, work_pending
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ret_fast_syscall_trace:
 	enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +17 -0
index e10bc363f533..2724e4d31b16 100644
@@ -493,3 +493,20 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)
+{
+	unsigned long stack_left;
+	unsigned long current_sp = current_stack_pointer;
+	struct stack_info info;
+
+	BUG_ON(!on_accessible_stack(current, current_sp, &info));
+
+	stack_left = current_sp - info.low;
+
+	if (size >= stack_left)
+		panic("alloca() over the kernel stack boundary\n");
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+		$(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -1
index a34e9290a699..25dd2a14560d 100644
@@ -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
Signature · 1 line
2.17.1
Reviewed-by on v2 2/2
#39 lore raw

Re: [PATCHv2 2/2] arm64: Clear the stack

Quoted 4 lines · Laura Abbott

Implementation of stackleak based heavily on the x86 version

This is the commit message I wrote when I was using an earlier version, which I think is more descriptive:

arm64: Add support for STACKLEAK gcc plugin

This adds support for the STACKLEAK gcc plugin to arm64 by implementing stackleak_check_alloca(), based heavily on the x86 version, and adding the two helpers used by the stackleak common code: current_top_of_stack() and on_thread_stack(). The stack erasure calls are made at syscall returns. Additionally, this disables the plugin in hypervisor and EFI stub code, which are out of scope for the protection.

Either way:

Thanks for getting this hammered out!

Quoted 4 lines

---
v2: Convert to adjusted on_acessible_stack APIs. Fixed alloca check to just panic. Dropped the extra include per Kees. I also didn't add the Reviewed-by since the APIs did change and I wanted another pass.

Maybe the panic() should get a comment above it to describe why it's there (i.e. summarize the thread where that change was discussed?) Or maybe mention it in the commit log (instead of being only below the --- line?)

-Kees

Quoted 134 lines
---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 +++++++++++++++
 arch/arm64/kernel/entry.S             |  7 +++++++
 arch/arm64/kernel/process.c           | 17 +++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 6 files changed, 44 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +15 -0
index a73ae1e49200..0061450a793b 100644
@@ -266,5 +266,20 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)        sve_set_current_vl(arg)
 #define SVE_GET_VL()   sve_get_current_vl()
 
+/*
+ * For CONFIG_GCC_PLUGIN_STACKLEAK
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.
+ */
+
+#define current_top_of_stack()                                                 \
+({                                                                             \
+       struct stack_info _info;                                                \
+       BUG_ON(!on_accessible_stack(current, current_stack_pointer, &_info));   \
+       _info.high;                                                             \
+})
+#define on_thread_stack()      (on_task_stack(current, current_stack_pointer, NULL))
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk        .req    x28             // current thread_info
 
        .text
 
+       .macro  stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+       bl      stackleak_erase
+#endif
+       .endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
        and     x2, x1, #_TIF_WORK_MASK
        cbnz    x2, work_pending
        enable_step_tsk x1, x2
+       stackleak_erase
        kernel_exit 0
 ret_fast_syscall_trace:
        enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
        cbnz    x2, work_pending
 finish_ret_to_user:
        enable_step_tsk x1, x2
+       stackleak_erase
        kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +17 -0
index e10bc363f533..2724e4d31b16 100644
@@ -493,3 +493,20 @@ void arch_setup_new_exec(void)
 {
        current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)
+{
+       unsigned long stack_left;
+       unsigned long current_sp = current_stack_pointer;
+       struct stack_info info;
+
+       BUG_ON(!on_accessible_stack(current, current_sp, &info));
+
+       stack_left = current_sp - info.low;
+
+       if (size >= stack_left)
+               panic("alloca() over the kernel stack boundary\n");
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+               $(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -2
index a34e9290a699..25dd2a14560d 100644
@@ -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
--

2.17.1

Signature · 2 lines
Kees Cook
Pixel Security
reviewer Reviewed-by on v2 2/2
#40 lore raw

Re: [PATCHv2 2/2] arm64: Clear the stack

In reply to Laura Abbott
Quoted 4 lines · Laura Abbott

Implementation of stackleak based heavily on the x86 version

Thanks for working on this!

Mark.

Quoted 138 lines

---
v2: Convert to adjusted on_acessible_stack APIs. Fixed alloca check to just panic. Dropped the extra include per Kees. I also didn't add the Reviewed-by since the APIs did change and I wanted another pass.

---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 +++++++++++++++
 arch/arm64/kernel/entry.S             |  7 +++++++
 arch/arm64/kernel/process.c           | 17 +++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 6 files changed, 44 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +15 -0
index a73ae1e49200..0061450a793b 100644
@@ -266,5 +266,20 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)	sve_set_current_vl(arg)
 #define SVE_GET_VL()	sve_get_current_vl()
 
+/*
+ * For CONFIG_GCC_PLUGIN_STACKLEAK
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.
+ */
+
+#define current_top_of_stack()							\
+({										\
+	struct stack_info _info;						\
+	BUG_ON(!on_accessible_stack(current, current_stack_pointer, &_info));	\
+	_info.high;								\
+})
+#define on_thread_stack()	(on_task_stack(current, current_stack_pointer, NULL))
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	stackleak_erase
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
 	and	x2, x1, #_TIF_WORK_MASK
 	cbnz	x2, work_pending
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ret_fast_syscall_trace:
 	enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +17 -0
index e10bc363f533..2724e4d31b16 100644
@@ -493,3 +493,20 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)
+{
+	unsigned long stack_left;
+	unsigned long current_sp = current_stack_pointer;
+	struct stack_info info;
+
+	BUG_ON(!on_accessible_stack(current, current_sp, &info));
+
+	stack_left = current_sp - info.low;
+
+	if (size >= stack_left)
+		panic("alloca() over the kernel stack boundary\n");
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+		$(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -1
index a34e9290a699..25dd2a14560d 100644
@@ -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
Signature · 1 line
2.17.1
#41 lore raw

v3 cover Stackleak for arm64

In reply to Alexander Popov

Hi,

This is the version of stackleak for arm64, hopefully ready for queueing

Laura Abbott (2):
arm64: Add stack information to on_accessible_stack
arm64: Add support for STACKLEAK gcc plugin

 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 ++++++
 arch/arm64/include/asm/sdei.h         |  9 ++--
 arch/arm64/include/asm/stacktrace.h   | 73 ++++++++++++++++++++++-----
 arch/arm64/kernel/entry.S             |  7 +++
 arch/arm64/kernel/process.c           | 22 ++++++++
 arch/arm64/kernel/ptrace.c            |  2 +-
 arch/arm64/kernel/sdei.c              | 51 ++++++++++++++++---
 arch/arm64/kernel/stacktrace.c        |  2 +-
 arch/arm64/kvm/hyp/Makefile           |  3 +-
 drivers/firmware/efi/libstub/Makefile |  3 +-
 11 files changed, 161 insertions(+), 27 deletions(-)
Signature · 1 line
2.17.1
#42 lore raw

PATCH v3 1/2 arm64: Add stack information to on_accessible_stack

In preparation for enabling the stackleak plugin on arm64, we need a way to get the bounds of the current stack. Extend on_accessible_stack to get this information.

---
v3: Switched some logic to reduce if nesting

---
 arch/arm64/include/asm/sdei.h       |  9 ++--
 arch/arm64/include/asm/stacktrace.h | 73 ++++++++++++++++++++++++-----
 arch/arm64/kernel/ptrace.c          |  2 +-
 arch/arm64/kernel/sdei.c            | 51 ++++++++++++++++----
 arch/arm64/kernel/stacktrace.c      |  2 +-
 5 files changed, 112 insertions(+), 25 deletions(-)
arch/arm64/include/asm/sdei.h +6 -3
index e073e6886685..ffe47d766c25 100644
@@ -40,15 +40,18 @@ asmlinkage unsigned long __sdei_handler(struct pt_regs *regs,
 unsigned long sdei_arch_get_entry_point(int conduit);
 #define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)
 
-bool _on_sdei_stack(unsigned long sp);
-static inline bool on_sdei_stack(unsigned long sp)
+struct stack_info;
+
+bool _on_sdei_stack(unsigned long sp, struct stack_info *info);
+static inline bool on_sdei_stack(unsigned long sp,
+				struct stack_info *info)
 {
 	if (!IS_ENABLED(CONFIG_VMAP_STACK))
 		return false;
 	if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
 		return false;
 	if (in_nmi())
-		return _on_sdei_stack(sp);
+		return _on_sdei_stack(sp, info);
 
 	return false;
 }
arch/arm64/include/asm/stacktrace.h +61 -12

120 lines not on this page · raw message

arch/arm64/kernel/ptrace.c +1 -1
index 5c338ce5a7fa..cf94e1498ba6 100644
@@ -132,7 +132,7 @@ static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
 {
 	return ((addr & ~(THREAD_SIZE - 1))  ==
 		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
-		on_irq_stack(addr);
+		on_irq_stack(addr, NULL);
 }
 
 /**
arch/arm64/kernel/sdei.c +43 -8

66 lines not on this page · raw message

arch/arm64/kernel/stacktrace.c +1 -1
index d5718a060672..4989f7ea1e59 100644
@@ -50,7 +50,7 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
 	if (!tsk)
 		tsk = current;
 
-	if (!on_accessible_stack(tsk, fp))
+	if (!on_accessible_stack(tsk, fp, NULL))
 		return -EINVAL;
 
 	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
Signature · 1 line
2.17.1
#43 lore raw

PATCH v3 2/2 arm64: Add support for STACKLEAK gcc plugin

In reply to Laura Abbott

This adds support for the STACKLEAK gcc plugin to arm64 by implementing stackleak_check_alloca(), based heavily on the x86 version, and adding the two helpers used by the stackleak common code: current_top_of_stack() and on_thread_stack(). The stack erasure calls are made at syscall returns. Additionally, this disables the plugin in hypervisor and EFI stub code, which are out of scope for the protection.

---
v3: Actual commit text courtesy of Kees. A comment explaining why we panic

---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 +++++++++++++++
 arch/arm64/kernel/entry.S             |  7 +++++++
 arch/arm64/kernel/process.c           | 22 ++++++++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 6 files changed, 49 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +1 -0
index 42c090cf0292..216d36a49ab5 100644
@@ -96,6 +96,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
arch/arm64/include/asm/processor.h +15 -0
index a73ae1e49200..0061450a793b 100644
@@ -266,5 +266,20 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)	sve_set_current_vl(arg)
 #define SVE_GET_VL()	sve_get_current_vl()
 
+/*
+ * For CONFIG_GCC_PLUGIN_STACKLEAK
+ *
+ * These need to be macros because otherwise we get stuck in a nightmare
+ * of header definitions for the use of task_stack_page.
+ */
+
+#define current_top_of_stack()							\
+({										\
+	struct stack_info _info;						\
+	BUG_ON(!on_accessible_stack(current, current_stack_pointer, &_info));	\
+	_info.high;								\
+})
+#define on_thread_stack()	(on_task_stack(current, current_stack_pointer, NULL))
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ASM_PROCESSOR_H */
arch/arm64/kernel/entry.S +7 -0
index 28ad8799406f..67d12016063d 100644
@@ -431,6 +431,11 @@ tsk	.req	x28		// current thread_info
 
 	.text
 
+	.macro	stackleak_erase
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+	bl	stackleak_erase
+#endif
+	.endm
 /*
  * Exception vectors.
  */
@@ -910,6 +915,7 @@ ret_fast_syscall:
 	and	x2, x1, #_TIF_WORK_MASK
 	cbnz	x2, work_pending
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ret_fast_syscall_trace:
 	enable_daif
@@ -936,6 +942,7 @@ ret_to_user:
 	cbnz	x2, work_pending
 finish_ret_to_user:
 	enable_step_tsk x1, x2
+	stackleak_erase
 	kernel_exit 0
 ENDPROC(ret_to_user)
 
arch/arm64/kernel/process.c +22 -0
index e10bc363f533..f0ad00fb6a71 100644
@@ -493,3 +493,25 @@ void arch_setup_new_exec(void)
 {
 	current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 }
+
+#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+void __used stackleak_check_alloca(unsigned long size)
+{
+	unsigned long stack_left;
+	unsigned long current_sp = current_stack_pointer;
+	struct stack_info info;
+
+	BUG_ON(!on_accessible_stack(current, current_sp, &info));
+
+	stack_left = current_sp - info.low;
+
+	/*
+	 * There's a good chance we're almost out of stack space if this
+	 * is true. Using panic() over BUG() is more likely to give
+	 * reliable debugging output.
+	 */
+	if (size >= stack_left)
+		panic("alloca() over the kernel stack boundary\n");
+}
+EXPORT_SYMBOL(stackleak_check_alloca);
+#endif
arch/arm64/kvm/hyp/Makefile +2 -1
index 4313f7475333..2fabc2dc1966 100644
@@ -3,7 +3,8 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \
+		$(DISABLE_STACKLEAK_PLUGIN)
 
 KVM=../../../../virt/kvm
 
drivers/firmware/efi/libstub/Makefile +2 -1
index a34e9290a699..25dd2a14560d 100644
@@ -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
Signature · 1 line
2.17.1
author Acked-by on v3 2/2
#44 lore raw

Re: [PATCHv3 2/2] arm64: Add support for STACKLEAK gcc plugin

Quoted 13 lines · Laura Abbott

This adds support for the STACKLEAK gcc plugin to arm64 by implementing stackleak_check_alloca(), based heavily on the x86 version, and adding the two helpers used by the stackleak common code: current_top_of_stack() and on_thread_stack(). The stack erasure calls are made at syscall returns. Additionally, this disables the plugin in hypervisor and EFI stub code, which are out of scope for the protection.

---
v3: Actual commit text courtesy of Kees. A comment explaining why we panic

Quoted 8 lines
---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 +++++++++++++++
 arch/arm64/kernel/entry.S             |  7 +++++++
 arch/arm64/kernel/process.c           | 22 ++++++++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 6 files changed, 49 insertions(+), 2 deletions(-)

Laura, thanks for your work!

I've reviewed and tested this patch on my LeMaker HiKey board (HiSilicon Kirin 620 SoC). The lkdtm tests for STACKLEAK work fine.

For testing I applied your patches above Kees' for-next/kspp:
https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/log/?h=for-next/kspp

I've had one trouble with building CONFIG_STACKLEAK_RUNTIME_DISABLE on arm64. Kees, could you please fold this into the 7th patch of the series?

---- >8 ----

kernel/stackleak.c +1 -0
index f731c9a..03031f7a 100644
@@ -16,6 +16,7 @@
 
 #ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
 #include <linux/jump_label.h>
+#include <linux/sysctl.h>
 
 static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass);
#45 lore raw

Re: [PATCHv3 2/2] arm64: Add support for STACKLEAK gcc plugin

Quoted 28 lines · Alexander Popov
Quoted 13 lines · Laura Abbott

This adds support for the STACKLEAK gcc plugin to arm64 by implementing stackleak_check_alloca(), based heavily on the x86 version, and adding the two helpers used by the stackleak common code: current_top_of_stack() and on_thread_stack(). The stack erasure calls are made at syscall returns. Additionally, this disables the plugin in hypervisor and EFI stub code, which are out of scope for the protection.

---
v3: Actual commit text courtesy of Kees. A comment explaining why we panic

Quoted 8 lines
---
 arch/arm64/Kconfig                    |  1 +
 arch/arm64/include/asm/processor.h    | 15 +++++++++++++++
 arch/arm64/kernel/entry.S             |  7 +++++++
 arch/arm64/kernel/process.c           | 22 ++++++++++++++++++++++
 arch/arm64/kvm/hyp/Makefile           |  3 ++-
 drivers/firmware/efi/libstub/Makefile |  3 ++-
 6 files changed, 49 insertions(+), 2 deletions(-)

Laura, thanks for your work!

I've reviewed and tested this patch on my LeMaker HiKey board (HiSilicon Kirin 620 SoC). The lkdtm tests for STACKLEAK work fine.

Quoted 7 lines

For testing I applied your patches above Kees' for-next/kspp:
https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/log/?h=for-next/kspp

I've had one trouble with building CONFIG_STACKLEAK_RUNTIME_DISABLE on arm64. Kees, could you please fold this into the 7th patch of the series?

Sure thing!

-Kees

Quoted 15 lines

---- >8 ----

kernel/stackleak.c +1 -0
index f731c9a..03031f7a 100644
@@ -16,6 +16,7 @@
 
 #ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
 #include <linux/jump_label.h>
+#include <linux/sysctl.h>
 
 static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass);
 
Signature · 2 lines
Kees Cook
Pixel Security
#46 lore raw

Re: [PATCHv3 0/2] Stackleak for arm64

In reply to Laura Abbott

Hi Laura,

Quoted 1 line · Laura Abbott

This is the version of stackleak for arm64, hopefully ready for queueing

Thanks. I'll push these into linux-next tomorrow, once I've had a chance to test my conflict resolution in entry.S.

Will

#47 lore raw

Re: [PATCHv3 0/2] Stackleak for arm64

Hi Laura,

Quoted 5 lines · Will Deacon
Quoted 1 line · Laura Abbott

This is the version of stackleak for arm64, hopefully ready for queueing

Thanks. I'll push these into linux-next tomorrow, once I've had a chance to test my conflict resolution in entry.S.

I've run into a couple of issues with this series:

1. I had to install libmpc-dev to get GCC_PLUGINS to appear, otherwise the hostcc check would silently fail. I guess that's a general observation, but it might be nice to print a message about the missing dependencies.

2. It breaks arm64 allmodconfig build. Log below.

Please can you take a look at the build failure? Otherwise, the patches look good to me.

Cheers,

Will

--->8

arch/arm64/kernel/sdei.c: In function ‘on_sdei_normal_stack’:
arch/arm64/kernel/sdei.c:101:7: error: dereferencing pointer to incomplete type ‘struct stack_info’

   info->low = low;
       ^~
arch/arm64/kernel/sdei.c:103:16: error: ‘STACK_TYPE_SDEI_NORMAL’ undeclared (first use in this function); did you mean ‘SCHED_NORMAL’?
   info->type = STACK_TYPE_SDEI_NORMAL;
                ^~~~~~~~~~~~~~~~~~~~~~
                SCHED_NORMAL
arch/arm64/kernel/sdei.c:103:16: note: each undeclared identifier is reported only once for each function it appears in
arch/arm64/kernel/sdei.c: In function ‘on_sdei_critical_stack’:
arch/arm64/kernel/sdei.c:121:16: error: ‘STACK_TYPE_SDEI_CRITICAL’ undeclared (first use in this function)
   info->type = STACK_TYPE_SDEI_CRITICAL;
                ^~~~~~~~~~~~~~~~~~~~~~~~
arch/arm64/kernel/sdei.c: At top level:
arch/arm64/kernel/sdei.c:127:6: error: conflicting types for ‘_on_sdei_stack’
 bool _on_sdei_stack(unsigned long sp,
      ^~~~~~~~~~~~~~
In file included from ./include/linux/arm_sdei.h:14:0,
                 from arch/arm64/kernel/sdei.c:5:
./arch/arm64/include/asm/sdei.h:45:6: note: previous declaration of ‘_on_sdei_stack’ was here
 bool _on_sdei_stack(unsigned long sp, struct stack_info *info);
      ^~~~~~~~~~~~~~
arch/arm64/kernel/sdei.c: In function ‘_on_sdei_stack’:
arch/arm64/kernel/sdei.c:136:33: error: ‘info’ undeclared (first use in this function); did you mean ‘int’?
  if (on_sdei_critical_stack(sp, info))
                                 ^~~~
                                 int
arch/arm64/kernel/sdei.c:131:21: warning: unused variable ‘high’ [-Wunused-variable]
  unsigned long low, high;
                     ^~~~
arch/arm64/kernel/sdei.c:131:16: warning: unused variable ‘low’ [-Wunused-variable]
  unsigned long low, high;
                ^~~

make[1]: *** [arch/arm64/kernel/sdei.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [arch/arm64/kernel] Error 2

#48 lore raw

Re: [PATCHv3 0/2] Stackleak for arm64

Quoted 59 lines · Will Deacon

Hi Laura,

Quoted 5 lines · Will Deacon
Quoted 1 line · Laura Abbott

This is the version of stackleak for arm64, hopefully ready for queueing

Thanks. I'll push these into linux-next tomorrow, once I've had a chance to test my conflict resolution in entry.S.

I've run into a couple of issues with this series:

1. I had to install libmpc-dev to get GCC_PLUGINS to appear, otherwise the hostcc check would silently fail. I guess that's a general observation, but it might be nice to print a message about the missing dependencies.

2. It breaks arm64 allmodconfig build. Log below.

Please can you take a look at the build failure? Otherwise, the patches look good to me.

Cheers,

Will

--->8

arch/arm64/kernel/sdei.c: In function ‘on_sdei_normal_stack’:
arch/arm64/kernel/sdei.c:101:7: error: dereferencing pointer to incomplete type ‘struct stack_info’

    info->low = low;
        ^~
arch/arm64/kernel/sdei.c:103:16: error: ‘STACK_TYPE_SDEI_NORMAL’ undeclared (first use in this function); did you mean ‘SCHED_NORMAL’?
    info->type = STACK_TYPE_SDEI_NORMAL;
                 ^~~~~~~~~~~~~~~~~~~~~~
                 SCHED_NORMAL
arch/arm64/kernel/sdei.c:103:16: note: each undeclared identifier is reported only once for each function it appears in
arch/arm64/kernel/sdei.c: In function ‘on_sdei_critical_stack’:
arch/arm64/kernel/sdei.c:121:16: error: ‘STACK_TYPE_SDEI_CRITICAL’ undeclared (first use in this function)
    info->type = STACK_TYPE_SDEI_CRITICAL;
                 ^~~~~~~~~~~~~~~~~~~~~~~~
arch/arm64/kernel/sdei.c: At top level:
arch/arm64/kernel/sdei.c:127:6: error: conflicting types for ‘_on_sdei_stack’
  bool _on_sdei_stack(unsigned long sp,
       ^~~~~~~~~~~~~~
In file included from ./include/linux/arm_sdei.h:14:0,
                  from arch/arm64/kernel/sdei.c:5:
./arch/arm64/include/asm/sdei.h:45:6: note: previous declaration of ‘_on_sdei_stack’ was here
  bool _on_sdei_stack(unsigned long sp, struct stack_info *info);
       ^~~~~~~~~~~~~~
arch/arm64/kernel/sdei.c: In function ‘_on_sdei_stack’:
arch/arm64/kernel/sdei.c:136:33: error: ‘info’ undeclared (first use in this function); did you mean ‘int’?
   if (on_sdei_critical_stack(sp, info))
                                  ^~~~
                                  int
arch/arm64/kernel/sdei.c:131:21: warning: unused variable ‘high’ [-Wunused-variable]
   unsigned long low, high;
                      ^~~~
arch/arm64/kernel/sdei.c:131:16: warning: unused variable ‘low’ [-Wunused-variable]
   unsigned long low, high;
                 ^~~
Quoted 4 lines

make[1]: *** [arch/arm64/kernel/sdei.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [arch/arm64/kernel] Error 2

Ugh this was a failure that I missed folding in, sorry about that

--- 8< ----

arch/arm64/kernel/sdei.c +2 -4
index 7154fee1cb2b..a94a868f0532 100644
@@ -13,6 +13,7 @@
 #include <asm/mmu.h>
 #include <asm/ptrace.h>
 #include <asm/sections.h>
+#include <asm/stacktrace.h>
 #include <asm/sysreg.h>
 #include <asm/vmap_stack.h>
 
@@ -125,11 +126,8 @@ bool on_sdei_critical_stack(unsigned long sp,
 }
 
 bool _on_sdei_stack(unsigned long sp,
-		unsigned long *stack_low,
-		unsigned long *stack_high)
+		struct stack_info *info)
 {
-	unsigned long low, high;
-
 	if (!IS_ENABLED(CONFIG_VMAP_STACK))
 		return false;
 
#49 lore raw

Re: [PATCHv3 0/2] Stackleak for arm64

Hi Laura,

Quoted 26 lines · Laura Abbott
Quoted 20 lines · Will Deacon

arch/arm64/kernel/sdei.c: At top level:
arch/arm64/kernel/sdei.c:127:6: error: conflicting types for ‘_on_sdei_stack’
bool _on_sdei_stack(unsigned long sp,

      ^~~~~~~~~~~~~~
In file included from ./include/linux/arm_sdei.h:14:0,
                 from arch/arm64/kernel/sdei.c:5:
./arch/arm64/include/asm/sdei.h:45:6: note: previous declaration of ‘_on_sdei_stack’ was here
 bool _on_sdei_stack(unsigned long sp, struct stack_info *info);
      ^~~~~~~~~~~~~~
arch/arm64/kernel/sdei.c: In function ‘_on_sdei_stack’:
arch/arm64/kernel/sdei.c:136:33: error: ‘info’ undeclared (first use in this function); did you mean ‘int’?
  if (on_sdei_critical_stack(sp, info))
                                 ^~~~
                                 int
arch/arm64/kernel/sdei.c:131:21: warning: unused variable ‘high’ [-Wunused-variable]
  unsigned long low, high;
                     ^~~~
arch/arm64/kernel/sdei.c:131:16: warning: unused variable ‘low’ [-Wunused-variable]
  unsigned long low, high;
                ^~~
Quoted 4 lines

make[1]: *** [arch/arm64/kernel/sdei.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [arch/arm64/kernel] Error 2

Quoted 1 line

Ugh this was a failure that I missed folding in, sorry about that

That's ok, thanks for the quick fixup. I'll fold it in and push this out later on.

Cheers,

Will