Commit 964c9dff authored by Alexander Popov's avatar Alexander Popov Committed by Kees Cook
Browse files

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.

Suggested-by: default avatarIngo Molnar <mingo@kernel.org>
Signed-off-by: default avatarAlexander Popov <alex.popov@linux.com>
Tested-by: default avatarLaura Abbott <labbott@redhat.com>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
parent ed535a2d
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -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
+6 −0
Original line number Diff line number Diff line
@@ -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
+38 −0
Original line number Diff line number Diff line
@@ -12,6 +12,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 */
@@ -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;
+14 −1
Original line number Diff line number Diff line
@@ -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
@@ -1232,6 +1234,17 @@ static struct ctl_table kern_table[] = {
		.extra1		= &zero,
		.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
	{ }
};
+8 −0
Original line number Diff line number Diff line
@@ -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