stackleak: Allow runtime disabling of kernel stack erasing
authorAlexander Popov <alex.popov@linux.com>
Thu, 16 Aug 2018 22:17:03 +0000 (01:17 +0300)
committerKees Cook <keescook@chromium.org>
Tue, 4 Sep 2018 17:35:48 +0000 (10:35 -0700)
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.

Suggested-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Alexander Popov <alex.popov@linux.com>
Tested-by: Laura Abbott <labbott@redhat.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Documentation/sysctl/kernel.txt
include/linux/stackleak.h
kernel/stackleak.c
kernel/sysctl.c
scripts/gcc-plugins/Kconfig

index 37a6795..1b87752 100644 (file)
@@ -89,6 +89,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
@@ -987,6 +988,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
index b911b97..3d5c327 100644 (file)
@@ -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
index f662395..e428929 100644 (file)
 
 #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 */
@@ -20,6 +55,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;
+
        /* Check that 'lowest_stack' value is sane */
        if (unlikely(kstack_ptr - boundary >= THREAD_SIZE))
                kstack_ptr = boundary;
index cc02050..3ae223f 100644 (file)
@@ -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
@@ -1233,6 +1235,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
        { }
 };
 
index b0a015e..0d5c799 100644 (file)
@@ -182,4 +182,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