Introduce the STACKLEAK feature and a test for it
Subsystems (17)
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
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
@@ -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
PATCH v14 2/6 x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
In reply to Alexander PopovThe 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH v14 3/6 gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack
In reply to Alexander PopovThe 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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH v14 4/6 lkdtm: Add a test for STACKLEAK
In reply to Alexander PopovIntroduce 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
@@ -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
@@ -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
@@ -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
PATCH v14 5/6 fs/proc: Show STACKLEAK metrics in the /proc file system
In reply to Alexander PopovIntroduce 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH v14 6/6 doc: self-protection: Add information about STACKLEAK feature
In reply to Alexander PopovAdd 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
@@ -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
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
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
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
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
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
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_DISABLEconfig option, which would be similar toCONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disablingstackleak_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
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_DISABLEconfig option, which would be similar toCONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disablingstackleak_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:
Ok, now I see, thanks. I'll do that.
Best regards,
Alexander
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_DISABLEconfig option, which would be similar toCONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disablingstackleak_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
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_DISABLEconfig option, which would be similar toCONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disablingstackleak_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
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_DISABLEconfig option, which would be similar toCONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disablingstackleak_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
__initare 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
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_DISABLEconfig option, which would be similar toCONFIG_SECURITY_SELINUX_DISABLE. That option will provide a sysctl switch disablingstackleak_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
Ingo Molnar
Acked-byQuoted 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
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:
Acked-by Ingo Molnar <[email protected]>... 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
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
Acked-by Ingo Molnar <[email protected]>... 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
PATCH v14 7/7 stackleak, sysctl: Allow runtime disabling of kernel stack erasing
In reply to Ingo MolnarIntroduce 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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_DISABLEoption, which provides 'stack_erasing_bypass' sysctl. It can be used in runtime to disable kernel stack erasing for kernels built withCONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain disabled andSTACKLEAK_METRICSwill not be updated until the next boot.Signed-off-by Alexander Popov <[email protected]>[...]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 voidstackleak_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
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_DISABLEoption, which provides 'stack_erasing_bypass' sysctl. It can be used in runtime to disable kernel stack erasing for kernels built withCONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain disabled andSTACKLEAK_METRICSwill not be updated until the next boot.Signed-off-by Alexander Popov <[email protected]>[...]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 voidstackleak_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
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_DISABLEoption, which provides 'stack_erasing_bypass' sysctl. It can be used in runtime to disable kernel stack erasing for kernels built withCONFIG_GCC_PLUGIN_STACKLEAK. Stack erasing will then remain disabled andSTACKLEAK_METRICSwill not be updated until the next boot.Signed-off-by Alexander Popov <[email protected]>[...]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 voidstackleak_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_METRICSshow 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
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_stackwould 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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Re: [PATCH v14 7/7] stackleak: Allow runtime disabling of kernel stack erasing
Quoted 19 lines · Alexander Popov
Introduce
CONFIG_STACKLEAK_RUNTIME_DISABLEoption, which provides 'stack_erasing' sysctl. It can be used in runtime to control kernel stack erasing for kernels built withCONFIG_GCC_PLUGIN_STACKLEAK.Suggested-by Ingo Molnar <[email protected]>Signed-off-by Alexander Popov <[email protected]>--- 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
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 beinclude/linux/stackleak.h +6
-0@@ -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@@ -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
cover Stackleak for arm64
In reply to Alexander PopovHi,
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
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
@@ -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
@@ -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
@@ -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
Mark Rutland
reviewerRe: [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_typewhich is similar to x86'sget_stack_info. Utilize that to reworkon_accessible_stackslightly as well.Signed-off-by Laura Abbott <[email protected]>---
So this did end up looking quite a bit likeget_stack_infobut 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@@ -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@@ -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@@ -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@@ -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.
PATCH 2/2 arm64: Clear the stack
In reply to Laura AbbottImplementation 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Re: [PATCH 2/2] arm64: Clear the stack
Quoted 8 lines · Laura Abbott
Implementation of stackleak based heavily on the x86 version
Signed-off-by Laura Abbott <[email protected]>---
Since last time: Minor style cleanups. Re-wrotecheck_allocato correctly handle all stack types. While doing that, I also realizedcurrent_top_of_stackwas 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@@ -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_HUGEPAGEarch/arm64/include/asm/processor.h +17
-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 +* +* 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@@ -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@@ -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@@ -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/kvmdrivers/firmware/efi/libstub/Makefile +2
-1@@ -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 := ninclude/linux/stackleak.h +1
-1@@ -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
Re: [PATCH 2/2] arm64: Clear the stack
In reply to Laura AbbottHello 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
Signed-off-by Laura Abbott <[email protected]>---
Since last time: Minor style cleanups. Re-wrotecheck_allocato correctly handle all stack types. While doing that, I also realizedcurrent_top_of_stackwas 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@@ -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_HUGEPAGEarch/arm64/include/asm/processor.h +0
-0
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@@ -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@@ -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@@ -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/kvmdrivers/firmware/efi/libstub/Makefile +2
-1@@ -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 := ninclude/linux/stackleak.h +1
-0@@ -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.
Mark Rutland
reviewerRe: [PATCH 2/2] arm64: Clear the stack
In reply to Laura AbbottQuoted 43 lines · Laura Abbott
Implementation of stackleak based heavily on the x86 version
Signed-off-by Laura Abbott <[email protected]>---
Since last time: Minor style cleanups. Re-wrotecheck_allocato correctly handle all stack types. While doing that, I also realizedcurrent_top_of_stackwas 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@@ -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_HUGEPAGEarch/arm64/include/asm/processor.h +5
-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 +* +* 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@@ -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@@ -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@@ -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/kvmdrivers/firmware/efi/libstub/Makefile +2
-1@@ -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 := ninclude/linux/stackleak.h +1
-0@@ -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
v2 cover Stackleak for arm64
In reply to Alexander PopovHi,
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
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
@@ -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
@@ -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
@@ -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
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_stackto get this information.Signed-off-by Laura Abbott <[email protected]>---
v2: Switched to using structstack_infofor argument passing.on_accessible_stackis now the primary API. SplitSTACK_TYPE_SDEIintoSTACK_TYPE_SDEI_NORMALandSTACK_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.
PATCH v2 2/2 arm64: Clear the stack
In reply to Laura AbbottImplementation 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
Re: [PATCHv2 2/2] arm64: Clear the stack
Quoted 4 lines · Laura Abbott
Implementation of stackleak based heavily on the x86 version
Signed-off-by Laura Abbott <[email protected]>
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 adjustedon_acessible_stackAPIs. 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@@ -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_HUGEPAGEarch/arm64/include/asm/processor.h +15
-0@@ -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@@ -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@@ -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@@ -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/kvmdrivers/firmware/efi/libstub/Makefile +2
-2@@ -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
Re: [PATCHv2 2/2] arm64: Clear the stack
In reply to Laura AbbottQuoted 4 lines · Laura Abbott
Implementation of stackleak based heavily on the x86 version
Signed-off-by Laura Abbott <[email protected]>
Thanks for working on this!
Mark.
Quoted 138 lines
---
v2: Convert to adjustedon_acessible_stackAPIs. 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@@ -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_HUGEPAGEarch/arm64/include/asm/processor.h +15
-0@@ -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@@ -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@@ -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@@ -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/kvmdrivers/firmware/efi/libstub/Makefile +2
-1@@ -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 := nSignature · 1 line
2.17.1
v3 cover Stackleak for arm64
In reply to Alexander PopovHi,
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
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
@@ -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
@@ -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
@@ -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
PATCH v3 2/2 arm64: Add support for STACKLEAK gcc plugin
In reply to Laura AbbottThis 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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()andon_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.Reviewed-by Mark Rutland <[email protected]>Reviewed-by Kees Cook <[email protected]>Signed-off-by Laura Abbott <[email protected]>---
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
@@ -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);
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()andon_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.Reviewed-by Mark Rutland <[email protected]>Reviewed-by Kees Cook <[email protected]>Signed-off-by Laura Abbott <[email protected]>---
v3: Actual commit text courtesy of Kees. A comment explaining why we panicQuoted 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
Acked-by Alexander Popov <[email protected]>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/ksppI've had one trouble with building
CONFIG_STACKLEAK_RUNTIME_DISABLEon 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@@ -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
Re: [PATCHv3 0/2] Stackleak for arm64
In reply to Laura AbbottHi 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
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
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_PLUGINSto 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
@@ -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;
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