Commit 2d6bb6ad authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'stackleak-v4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull stackleak gcc plugin from Kees Cook:
 "Please pull this new GCC plugin, stackleak, for v4.20-rc1. This plugin
  was ported from grsecurity by Alexander Popov. It provides efficient
  stack content poisoning at syscall exit. This creates a defense
  against at least two classes of flaws:

   - Uninitialized stack usage. (We continue to work on improving the
     compiler to do this in other ways: e.g. unconditional zero init was
     proposed to GCC and Clang, and more plugin work has started too).

   - Stack content exposure. By greatly reducing the lifetime of valid
     stack contents, exposures via either direct read bugs or unknown
     cache side-channels become much more difficult to exploit. This
     complements the existing buddy and heap poisoning options, but
     provides the coverage for stacks.

  The x86 hooks are included in this series (which have been reviewed by
  Ingo, Dave Hansen, and Thomas Gleixner). The arm64 hooks have already
  been merged through the arm64 tree (written by Laura Abbott and
  reviewed by Mark Rutland and Will Deacon).

  With VLAs having been removed this release, there is no need for
  alloca() protection, so it has been removed from the plugin"

* tag 'stackleak-v4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  arm64: Drop unneeded stackleak_check_alloca()
  stackleak: Allow runtime disabling of kernel stack erasing
  doc: self-protection: Add information about STACKLEAK feature
  fs/proc: Show STACKLEAK metrics in the /proc file system
  lkdtm: Add a test for STACKLEAK
  gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack
  x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls
parents 7c6c54b5 6fcde904
...@@ -302,11 +302,11 @@ sure structure holes are cleared. ...@@ -302,11 +302,11 @@ sure structure holes are cleared.
Memory poisoning Memory poisoning
---------------- ----------------
When releasing memory, it is best to poison the contents (clear stack on When releasing memory, it is best to poison the contents, to avoid reuse
syscall return, wipe heap memory on a free), to avoid reuse attacks that attacks that rely on the old contents of memory. E.g., clear stack on a
rely on the old contents of memory. This frustrates many uninitialized syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a
variable attacks, stack content exposures, heap content exposures, and free. This frustrates many uninitialized variable attacks, stack content
use-after-free attacks. exposures, heap content exposures, and use-after-free attacks.
Destination tracking Destination tracking
-------------------- --------------------
......
...@@ -89,6 +89,7 @@ show up in /proc/sys/kernel: ...@@ -89,6 +89,7 @@ show up in /proc/sys/kernel:
- shmmni - shmmni
- softlockup_all_cpu_backtrace - softlockup_all_cpu_backtrace
- soft_watchdog - soft_watchdog
- stack_erasing
- stop-a [ SPARC only ] - stop-a [ SPARC only ]
- sysrq ==> Documentation/admin-guide/sysrq.rst - sysrq ==> Documentation/admin-guide/sysrq.rst
- sysctl_writes_strict - sysctl_writes_strict
...@@ -987,6 +988,23 @@ detect a hard lockup condition. ...@@ -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: tainted:
Non-zero if the kernel has been tainted. Numeric values, which can be Non-zero if the kernel has been tainted. Numeric values, which can be
......
...@@ -146,3 +146,6 @@ Their order is preserved but their base will be offset early at boot time. ...@@ -146,3 +146,6 @@ Their order is preserved but their base will be offset early at boot time.
Be very careful vs. KASLR when changing anything here. The KASLR address Be very careful vs. KASLR when changing anything here. The KASLR address
range must not overlap with anything except the KASAN shadow area, which is range must not overlap with anything except the KASAN shadow area, which is
correct as KASAN disables KASLR. correct as KASAN disables KASLR.
For both 4- and 5-level layouts, the STACKLEAK_POISON value in the last 2MB
hole: ffffffffffff4111
...@@ -429,6 +429,13 @@ config SECCOMP_FILTER ...@@ -429,6 +429,13 @@ config SECCOMP_FILTER
See Documentation/userspace-api/seccomp_filter.rst for details. See Documentation/userspace-api/seccomp_filter.rst for details.
config HAVE_ARCH_STACKLEAK
bool
help
An architecture should select this if it has the code which
fills the used part of the kernel stack with the STACKLEAK_POISON
value before returning from system calls.
config HAVE_STACKPROTECTOR config HAVE_STACKPROTECTOR
bool bool
help help
......
...@@ -497,25 +497,3 @@ void arch_setup_new_exec(void) ...@@ -497,25 +497,3 @@ void arch_setup_new_exec(void)
{ {
current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0; 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
...@@ -129,6 +129,7 @@ config X86 ...@@ -129,6 +129,7 @@ config X86
select HAVE_ARCH_PREL32_RELOCATIONS select HAVE_ARCH_PREL32_RELOCATIONS
select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_THREAD_STRUCT_WHITELIST
select HAVE_ARCH_STACKLEAK
select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRACEHOOK
select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_ARCH_TRANSPARENT_HUGEPAGE
select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64 select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64
......
...@@ -329,8 +329,22 @@ For 32-bit we have the following conventions - kernel is built with ...@@ -329,8 +329,22 @@ For 32-bit we have the following conventions - kernel is built with
#endif #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 */ #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 * This does 'call enter_from_user_mode' unless we can avoid it based on
* kernel config or using the static jump infrastructure. * kernel config or using the static jump infrastructure.
......
...@@ -46,6 +46,8 @@ ...@@ -46,6 +46,8 @@
#include <asm/frame.h> #include <asm/frame.h>
#include <asm/nospec-branch.h> #include <asm/nospec-branch.h>
#include "calling.h"
.section .entry.text, "ax" .section .entry.text, "ax"
/* /*
...@@ -712,6 +714,7 @@ ENTRY(ret_from_fork) ...@@ -712,6 +714,7 @@ ENTRY(ret_from_fork)
/* When we fork, we trace the syscall return in the child, too. */ /* When we fork, we trace the syscall return in the child, too. */
movl %esp, %eax movl %esp, %eax
call syscall_return_slowpath call syscall_return_slowpath
STACKLEAK_ERASE
jmp restore_all jmp restore_all
/* kernel thread */ /* kernel thread */
...@@ -886,6 +889,8 @@ ENTRY(entry_SYSENTER_32) ...@@ -886,6 +889,8 @@ ENTRY(entry_SYSENTER_32)
ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \ ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \
"jmp .Lsyscall_32_done", X86_FEATURE_XENPV "jmp .Lsyscall_32_done", X86_FEATURE_XENPV
STACKLEAK_ERASE
/* Opportunistic SYSEXIT */ /* Opportunistic SYSEXIT */
TRACE_IRQS_ON /* User mode traces as IRQs on. */ TRACE_IRQS_ON /* User mode traces as IRQs on. */
...@@ -997,6 +1002,8 @@ ENTRY(entry_INT80_32) ...@@ -997,6 +1002,8 @@ ENTRY(entry_INT80_32)
call do_int80_syscall_32 call do_int80_syscall_32
.Lsyscall_32_done: .Lsyscall_32_done:
STACKLEAK_ERASE
restore_all: restore_all:
TRACE_IRQS_IRET TRACE_IRQS_IRET
SWITCH_TO_ENTRY_STACK SWITCH_TO_ENTRY_STACK
......
...@@ -266,6 +266,8 @@ syscall_return_via_sysret: ...@@ -266,6 +266,8 @@ syscall_return_via_sysret:
* We are on the trampoline stack. All regs except RDI are live. * We are on the trampoline stack. All regs except RDI are live.
* We can do future final exit work right here. * We can do future final exit work right here.
*/ */
STACKLEAK_ERASE_NOCLOBBER
SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
popq %rdi popq %rdi
...@@ -625,6 +627,7 @@ GLOBAL(swapgs_restore_regs_and_return_to_usermode) ...@@ -625,6 +627,7 @@ GLOBAL(swapgs_restore_regs_and_return_to_usermode)
* We are on the trampoline stack. All regs except RDI are live. * We are on the trampoline stack. All regs except RDI are live.
* We can do future final exit work right here. * We can do future final exit work right here.
*/ */
STACKLEAK_ERASE_NOCLOBBER
SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
......
...@@ -261,6 +261,11 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe) ...@@ -261,6 +261,11 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe)
/* Opportunistic SYSRET */ /* Opportunistic SYSRET */
sysret32_from_system_call: 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. */ TRACE_IRQS_ON /* User mode traces as IRQs on. */
movq RBX(%rsp), %rbx /* pt_regs->rbx */ movq RBX(%rsp), %rbx /* pt_regs->rbx */
movq RBP(%rsp), %rbp /* pt_regs->rbp */ movq RBP(%rsp), %rbp /* pt_regs->rbp */
......
...@@ -8,7 +8,9 @@ lkdtm-$(CONFIG_LKDTM) += perms.o ...@@ -8,7 +8,9 @@ lkdtm-$(CONFIG_LKDTM) += perms.o
lkdtm-$(CONFIG_LKDTM) += refcount.o lkdtm-$(CONFIG_LKDTM) += refcount.o
lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o
lkdtm-$(CONFIG_LKDTM) += usercopy.o lkdtm-$(CONFIG_LKDTM) += usercopy.o
lkdtm-$(CONFIG_LKDTM) += stackleak.o
KASAN_SANITIZE_stackleak.o := n
KCOV_INSTRUMENT_rodata.o := n KCOV_INSTRUMENT_rodata.o := n
OBJCOPYFLAGS := OBJCOPYFLAGS :=
......
...@@ -184,6 +184,7 @@ static const struct crashtype crashtypes[] = { ...@@ -184,6 +184,7 @@ static const struct crashtype crashtypes[] = {
CRASHTYPE(USERCOPY_STACK_BEYOND), CRASHTYPE(USERCOPY_STACK_BEYOND),
CRASHTYPE(USERCOPY_KERNEL), CRASHTYPE(USERCOPY_KERNEL),
CRASHTYPE(USERCOPY_KERNEL_DS), CRASHTYPE(USERCOPY_KERNEL_DS),
CRASHTYPE(STACKLEAK_ERASING),
}; };
......
...@@ -84,4 +84,7 @@ void lkdtm_USERCOPY_STACK_BEYOND(void); ...@@ -84,4 +84,7 @@ void lkdtm_USERCOPY_STACK_BEYOND(void);
void lkdtm_USERCOPY_KERNEL(void); void lkdtm_USERCOPY_KERNEL(void);
void lkdtm_USERCOPY_KERNEL_DS(void); void lkdtm_USERCOPY_KERNEL_DS(void);
/* lkdtm_stackleak.c */
void lkdtm_STACKLEAK_ERASING(void);
#endif #endif
// SPDX-License-Identifier: GPL-2.0
/*
* This code tests that the current task stack is properly erased (filled
* with STACKLEAK_POISON).
*
* Authors:
* Alexander Popov <alex.popov@linux.com>
* Tycho Andersen <tycho@tycho.ws>
*/
#include "lkdtm.h"
#include <linux/stackleak.h>
void lkdtm_STACKLEAK_ERASING(void)
{
unsigned long *sp, left, found, i;
const unsigned long check_depth =
STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
/*
* For the details about the alignment of the poison values, see
* the comment in stackleak_track_stack().
*/
sp = PTR_ALIGN(&i, sizeof(unsigned long));
left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long);
sp--;
/*
* One 'long int' at the bottom of the thread stack is reserved
* and not poisoned.
*/
if (left > 1) {
left--;
} else {
pr_err("FAIL: not enough stack space for the test\n");
return;
}
pr_info("checking unused part of the thread stack (%lu bytes)...\n",
left * sizeof(unsigned long));
/*
* Search for 'check_depth' poison values in a row (just like
* stackleak_erase() does).
*/
for (i = 0, found = 0; i < left && found <= check_depth; i++) {
if (*(sp - i) == STACKLEAK_POISON)
found++;
else
found = 0;
}
if (found <= check_depth) {
pr_err("FAIL: thread stack is not erased (checked %lu bytes)\n",
i * sizeof(unsigned long));
return;
}
pr_info("first %lu bytes are unpoisoned\n",
(i - found) * sizeof(unsigned long));
/* The rest of thread stack should be erased */
for (; i < left; i++) {
if (*(sp - i) != STACKLEAK_POISON) {
pr_err("FAIL: thread stack is NOT properly erased\n");
return;
}
}
pr_info("OK: the rest of the thread stack is properly erased\n");
return;
}
...@@ -2905,6 +2905,21 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns, ...@@ -2905,6 +2905,21 @@ static int proc_pid_patch_state(struct seq_file *m, struct pid_namespace *ns,
} }
#endif /* CONFIG_LIVEPATCH */ #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 * Thread groups
*/ */
...@@ -3006,6 +3021,9 @@ static const struct pid_entry tgid_base_stuff[] = { ...@@ -3006,6 +3021,9 @@ static const struct pid_entry tgid_base_stuff[] = {
#ifdef CONFIG_LIVEPATCH #ifdef CONFIG_LIVEPATCH
ONE("patch_state", S_IRUSR, proc_pid_patch_state), ONE("patch_state", S_IRUSR, proc_pid_patch_state),
#endif #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) static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
......
...@@ -1200,6 +1200,11 @@ struct task_struct { ...@@ -1200,6 +1200,11 @@ struct task_struct {
void *security; void *security;
#endif #endif
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
unsigned long lowest_stack;
unsigned long prev_lowest_stack;
#endif
/* /*
* New fields for task_struct should be added above here, so that * New fields for task_struct should be added above here, so that
* they are included in the randomized portion of task_struct. * they are included in the randomized portion of task_struct.
......
/* 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
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
#include <asm/stacktrace.h>
static inline void stackleak_task_init(struct task_struct *t)
{
t->lowest_stack = (unsigned long)end_of_stack(t) + sizeof(unsigned long);
# ifdef CONFIG_STACKLEAK_METRICS
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
#endif
...@@ -117,6 +117,10 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o ...@@ -117,6 +117,10 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o
obj-$(CONFIG_ZONE_DEVICE) += memremap.o obj-$(CONFIG_ZONE_DEVICE) += memremap.o
obj-$(CONFIG_RSEQ) += rseq.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 $(obj)/configs.o: $(obj)/config_data.h
targets += config_data.gz targets += config_data.gz
......
...@@ -91,6 +91,7 @@ ...@@ -91,6 +91,7 @@
#include <linux/kcov.h> #include <linux/kcov.h>
#include <linux/livepatch.h> #include <linux/livepatch.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/stackleak.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
...@@ -1926,6 +1927,8 @@ static __latent_entropy struct task_struct *copy_process( ...@@ -1926,6 +1927,8 @@ static __latent_entropy struct task_struct *copy_process(
if (retval) if (retval)
goto bad_fork_cleanup_io; goto bad_fork_cleanup_io;
stackleak_task_init(p);
if (pid != &init_struct_pid) { if (pid != &init_struct_pid) {
pid = alloc_pid(p->nsproxy->pid_ns_for_children); pid = alloc_pid(p->nsproxy->pid_ns_for_children);
if (IS_ERR(pid)) { if (IS_ERR(pid)) {
......
// SPDX-License-Identifier: GPL-2.0
/*
* This code fills the used part of the kernel stack with a poison value
* before returning to userspace. It's part of the STACKLEAK feature
* ported from grsecurity/PaX.
*
* Author: Alexander Popov <alex.popov@linux.com>
*
* STACKLEAK reduces the information which kernel stack leak bugs can
* reveal and blocks some uninitialized stack variable attacks.
*/
#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 */
unsigned long kstack_ptr = current->lowest_stack;
unsigned long boundary = (unsigned long)end_of_stack(current);
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;
/* Search for the poison value in the kernel stack */
while (kstack_ptr > boundary && poison_count <= depth) {
if (*(unsigned long *)kstack_ptr == STACKLEAK_POISON)
poison_count++;
else
poison_count = 0;
kstack_ptr -= sizeof(unsigned long);
}
/*
* One 'long int' at the bottom of the thread stack is reserved and
* should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK=y).
*/
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
* the stack pointer doesn't change when we write poison.
*/
if (on_thread_stack())
boundary = current_stack_pointer;
else
boundary = current_top_of_stack();
while (kstack_ptr < boundary) {
*(unsigned long *)kstack_ptr = STACKLEAK_POISON;
kstack_ptr += sizeof(unsigned long);
}
/* Reset the 'lowest_stack' value for the next syscall */
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);
...@@ -91,7 +91,9 @@ ...@@ -91,7 +91,9 @@
#ifdef CONFIG_CHR_DEV_SG #ifdef CONFIG_CHR_DEV_SG
#include <scsi/sg.h> #include <scsi/sg.h>
#endif #endif
#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
#include <linux/stackleak.h>
#endif
#ifdef CONFIG_LOCKUP_DETECTOR #ifdef CONFIG_LOCKUP_DETECTOR
#include <linux/nmi.h> #include <linux/nmi.h>
#endif #endif
...@@ -1232,6 +1234,17 @@ static struct ctl_table kern_table[] = { ...@@ -1232,6 +1234,17 @@ static struct ctl_table kern_table[] = {
.extra1 = &zero, .extra1 = &zero,
.extra2 = &one, .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 #endif
{ } { }
}; };
......
...@@ -26,6 +26,16 @@ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \ ...@@ -26,6 +26,16 @@ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) \ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) \
+= -fplugin-arg-randomize_layout_plugin-performance-mode += -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
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
+= -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
export DISABLE_STACKLEAK_PLUGIN
# All the plugin CFLAGS are collected here in case a build target needs to # All the plugin CFLAGS are collected here in case a build target needs to
# filter them out of the KBUILD_CFLAGS. # filter them out of the KBUILD_CFLAGS.
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
......
...@@ -139,4 +139,55 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE ...@@ -139,4 +139,55 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE
in structures. This reduces the performance hit of RANDSTRUCT in structures. This reduces the performance hit of RANDSTRUCT
at the cost of weakened randomization. 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.
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 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 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 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 endif
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment