Commit 2142b7f0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hardening-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull kernel hardening updates from Kees Cook:

 - Add arm64 Shadow Call Stack support for GCC 12 (Dan Li)

 - Avoid memset with stack offset randomization under Clang (Marco
   Elver)

 - Clean up stackleak plugin to play nice with .noinstr (Kees Cook)

 - Check stack depth for greater usercopy hardening coverage (Kees Cook)

* tag 'hardening-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  arm64: Add gcc Shadow Call Stack support
  m68k: Implement "current_stack_pointer"
  xtensa: Implement "current_stack_pointer"
  usercopy: Check valid lifetime via stack depth
  stack: Constrain and fix stack offset randomization with Clang builds
  stack: Introduce CONFIG_RANDOMIZE_KSTACK_OFFSET
  gcc-plugins/stackleak: Ignore .noinstr.text and .entry.text
  gcc-plugins/stackleak: Exactly match strings instead of prefixes
  gcc-plugins/stackleak: Provide verbose mode
parents fd2d7a4a afcf5441
......@@ -599,21 +599,22 @@ config STACKPROTECTOR_STRONG
config ARCH_SUPPORTS_SHADOW_CALL_STACK
bool
help
An architecture should select this if it supports Clang's Shadow
Call Stack and implements runtime support for shadow stack
An architecture should select this if it supports the compiler's
Shadow Call Stack and implements runtime support for shadow stack
switching.
config SHADOW_CALL_STACK
bool "Clang Shadow Call Stack"
depends on CC_IS_CLANG && ARCH_SUPPORTS_SHADOW_CALL_STACK
bool "Shadow Call Stack"
depends on ARCH_SUPPORTS_SHADOW_CALL_STACK
depends on DYNAMIC_FTRACE_WITH_REGS || !FUNCTION_GRAPH_TRACER
help
This option enables Clang's Shadow Call Stack, which uses a
shadow stack to protect function return addresses from being
overwritten by an attacker. More information can be found in
Clang's documentation:
This option enables the compiler's Shadow Call Stack, which
uses a shadow stack to protect function return addresses from
being overwritten by an attacker. More information can be found
in the compiler's documentation:
https://clang.llvm.org/docs/ShadowCallStack.html
- Clang: https://clang.llvm.org/docs/ShadowCallStack.html
- GCC: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#Instrumentation-Options
Note that security guarantees in the kernel differ from the
ones documented for user space. The kernel must store addresses
......@@ -1159,16 +1160,30 @@ config HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET
to the compiler, so it will attempt to add canary checks regardless
of the static branch state.
config RANDOMIZE_KSTACK_OFFSET_DEFAULT
bool "Randomize kernel stack offset on syscall entry"
config RANDOMIZE_KSTACK_OFFSET
bool "Support for randomizing kernel stack offset on syscall entry" if EXPERT
default y
depends on HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET
depends on INIT_STACK_NONE || !CC_IS_CLANG || CLANG_VERSION >= 140000
help
The kernel stack offset can be randomized (after pt_regs) by
roughly 5 bits of entropy, frustrating memory corruption
attacks that depend on stack address determinism or
cross-syscall address exposures. This feature is controlled
by kernel boot param "randomize_kstack_offset=on/off", and this
config chooses the default boot state.
cross-syscall address exposures.
The feature is controlled via the "randomize_kstack_offset=on/off"
kernel boot param, and if turned off has zero overhead due to its use
of static branches (see JUMP_LABEL).
If unsure, say Y.
config RANDOMIZE_KSTACK_OFFSET_DEFAULT
bool "Default state of kernel stack offset randomization"
depends on RANDOMIZE_KSTACK_OFFSET
help
Kernel stack offset randomization is controlled by kernel boot param
"randomize_kstack_offset=on/off", and this config chooses the default
boot state.
config ARCH_OPTIONAL_KERNEL_RWX
def_bool n
......
......@@ -5,6 +5,7 @@ config ARM
select ARCH_32BIT_OFF_T
select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE if HAVE_KRETPROBES && FRAME_POINTER && !ARM_UNWIND
select ARCH_HAS_BINFMT_FLAT
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VIRTUAL if MMU
select ARCH_HAS_DMA_WRITE_COMBINE if !ARM_DMA_MEM_BUFFERABLE
select ARCH_HAS_ELF_RANDOMIZE
......
......@@ -19,6 +19,7 @@ config ARM64
select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
select ARCH_HAS_CACHE_LINE_SIZE
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VIRTUAL
select ARCH_HAS_DEBUG_VM_PGTABLE
select ARCH_HAS_DMA_PREP_COHERENT
......@@ -1257,7 +1258,7 @@ config HW_PERF_EVENTS
def_bool y
depends on ARM_PMU
# Supported by clang >= 7.0
# Supported by clang >= 7.0 or GCC >= 12.0.0
config CC_HAVE_SHADOW_CALL_STACK
def_bool $(cc-option, -fsanitize=shadow-call-stack -ffixed-x18)
......
......@@ -4,6 +4,7 @@ config M68K
default y
select ARCH_32BIT_OFF_T
select ARCH_HAS_BINFMT_FLAT
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DMA_PREP_COHERENT if HAS_DMA && MMU && !COLDFIRE
select ARCH_HAS_SYNC_DMA_FOR_DEVICE if HAS_DMA
select ARCH_HAVE_NMI_SAFE_CMPXCHG if RMW_INSNS
......
......@@ -24,6 +24,8 @@ static inline struct task_struct *get_current(void)
#define current get_current()
#endif /* CONFNIG_MMU */
#endif /* CONFIG_MMU */
register unsigned long current_stack_pointer __asm__("sp");
#endif /* !(_M68K_CURRENT_H) */
......@@ -108,6 +108,7 @@ config PPC
select ARCH_ENABLE_MEMORY_HOTPLUG
select ARCH_ENABLE_MEMORY_HOTREMOVE
select ARCH_HAS_COPY_MC if PPC64
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VIRTUAL
select ARCH_HAS_DEBUG_VM_PGTABLE
select ARCH_HAS_DEBUG_WX if STRICT_KERNEL_RWX
......
......@@ -60,6 +60,7 @@ config S390
select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM
select ARCH_ENABLE_MEMORY_HOTREMOVE
select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VM_PGTABLE
select ARCH_HAS_DEBUG_WX
select ARCH_HAS_DEVMEM_IS_ALLOWED
......
......@@ -7,6 +7,7 @@ config SUPERH
select ARCH_HAVE_CUSTOM_GPIO_H
select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A)
select ARCH_HAS_BINFMT_FLAT if !MMU
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_GIGANTIC_PAGE
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_HAS_PTE_SPECIAL
......
......@@ -69,6 +69,7 @@ config X86
select ARCH_ENABLE_THP_MIGRATION if X86_64 && TRANSPARENT_HUGEPAGE
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
select ARCH_HAS_CACHE_LINE_SIZE
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VIRTUAL
select ARCH_HAS_DEBUG_VM_PGTABLE if !X86_PAE
select ARCH_HAS_DEVMEM_IS_ALLOWED
......
......@@ -3,6 +3,7 @@ config XTENSA
def_bool y
select ARCH_32BIT_OFF_T
select ARCH_HAS_BINFMT_FLAT if !MMU
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DMA_PREP_COHERENT if MMU
select ARCH_HAS_SYNC_DMA_FOR_CPU if MMU
select ARCH_HAS_SYNC_DMA_FOR_DEVICE if MMU
......
......@@ -26,6 +26,8 @@ static inline struct task_struct *get_current(void)
#define current get_current()
register unsigned long current_stack_pointer __asm__("a1");
#else
#define GET_CURRENT(reg,sp) \
......
......@@ -19,14 +19,14 @@ struct stackframe {
static __always_inline unsigned long *stack_pointer(struct task_struct *task)
{
unsigned long *sp;
unsigned long sp;
if (!task || task == current)
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
sp = current_stack_pointer;
else
sp = (unsigned long *)task->thread.sp;
sp = task->thread.sp;
return sp;
return (unsigned long *)sp;
}
void walk_stackframe(unsigned long *sp,
......
......@@ -36,9 +36,8 @@ asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs)
#ifdef CONFIG_DEBUG_STACKOVERFLOW
/* Debugging check for stack overflow: is there less than 1KB free? */
{
unsigned long sp;
unsigned long sp = current_stack_pointer;
__asm__ __volatile__ ("mov %0, a1\n" : "=a" (sp));
sp &= THREAD_SIZE - 1;
if (unlikely(sp < (sizeof(thread_info) + 1024)))
......
......@@ -97,6 +97,10 @@
#define KASAN_ABI_VERSION 4
#endif
#ifdef CONFIG_SHADOW_CALL_STACK
#define __noscs __attribute__((__no_sanitize__("shadow-call-stack")))
#endif
#if __has_attribute(__no_sanitize_address__)
#define __no_sanitize_address __attribute__((no_sanitize_address))
#else
......
......@@ -2,6 +2,7 @@
#ifndef _LINUX_RANDOMIZE_KSTACK_H
#define _LINUX_RANDOMIZE_KSTACK_H
#ifdef CONFIG_RANDOMIZE_KSTACK_OFFSET
#include <linux/kernel.h>
#include <linux/jump_label.h>
#include <linux/percpu-defs.h>
......@@ -16,8 +17,20 @@ DECLARE_PER_CPU(u32, kstack_offset);
* alignment. Also, since this use is being explicitly masked to a max of
* 10 bits, stack-clash style attacks are unlikely. For more details see
* "VLAs" in Documentation/process/deprecated.rst
*
* The normal __builtin_alloca() is initialized with INIT_STACK_ALL (currently
* only with Clang and not GCC). Initializing the unused area on each syscall
* entry is expensive, and generating an implicit call to memset() may also be
* problematic (such as in noinstr functions). Therefore, if the compiler
* supports it (which it should if it initializes allocas), always use the
* "uninitialized" variant of the builtin.
*/
void *__builtin_alloca(size_t size);
#if __has_builtin(__builtin_alloca_uninitialized)
#define __kstack_alloca __builtin_alloca_uninitialized
#else
#define __kstack_alloca __builtin_alloca
#endif
/*
* Use, at most, 10 bits of entropy. We explicitly cap this to keep the
* "VLA" from being unbounded (see above). 10 bits leaves enough room for
......@@ -36,7 +49,7 @@ void *__builtin_alloca(size_t size);
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
&randomize_kstack_offset)) { \
u32 offset = raw_cpu_read(kstack_offset); \
u8 *ptr = __builtin_alloca(KSTACK_OFFSET_MAX(offset)); \
u8 *ptr = __kstack_alloca(KSTACK_OFFSET_MAX(offset)); \
/* Keep allocation even after "ptr" loses scope. */ \
asm volatile("" :: "r"(ptr) : "memory"); \
} \
......@@ -50,5 +63,9 @@ void *__builtin_alloca(size_t size);
raw_cpu_write(kstack_offset, offset); \
} \
} while (0)
#else /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
#define add_random_kstack_offset() do { } while (0)
#define choose_random_kstack_offset(rand) do { } while (0)
#endif /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
#endif
......@@ -853,7 +853,7 @@ static void __init mm_init(void)
pti_init();
}
#ifdef CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET
#ifdef CONFIG_RANDOMIZE_KSTACK_OFFSET
DEFINE_STATIC_KEY_MAYBE_RO(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT,
randomize_kstack_offset);
DEFINE_PER_CPU(u32, kstack_offset);
......
......@@ -744,6 +744,15 @@ config IDLE_PAGE_TRACKING
config ARCH_HAS_CACHE_LINE_SIZE
bool
config ARCH_HAS_CURRENT_STACK_POINTER
bool
help
In support of HARDENED_USERCOPY performing stack variable lifetime
checking, an architecture-agnostic way to find the stack pointer
is needed. Once an architecture defines an unsigned long global
register alias named "current_stack_pointer", this config can be
selected.
config ARCH_HAS_PTE_DEVMAP
bool
......
......@@ -29,7 +29,7 @@
* Returns:
* NOT_STACK: not at all on the stack
* GOOD_FRAME: fully within a valid stack frame
* GOOD_STACK: fully on the stack (when can't do frame-checking)
* GOOD_STACK: within the current stack (when can't frame-check exactly)
* BAD_STACK: error condition (invalid stack position or bad stack frame)
*/
static noinline int check_stack_object(const void *obj, unsigned long len)
......@@ -55,6 +55,17 @@ static noinline int check_stack_object(const void *obj, unsigned long len)
if (ret)
return ret;
/* Finally, check stack depth if possible. */
#ifdef CONFIG_ARCH_HAS_CURRENT_STACK_POINTER
if (IS_ENABLED(CONFIG_STACK_GROWSUP)) {
if ((void *)current_stack_pointer < obj + len)
return BAD_STACK;
} else {
if (obj < (void *)current_stack_pointer)
return BAD_STACK;
}
#endif
return GOOD_STACK;
}
......@@ -280,7 +291,15 @@ void __check_object_size(const void *ptr, unsigned long n, bool to_user)
*/
return;
default:
usercopy_abort("process stack", NULL, to_user, 0, n);
usercopy_abort("process stack", NULL, to_user,
#ifdef CONFIG_ARCH_HAS_CURRENT_STACK_POINTER
IS_ENABLED(CONFIG_STACK_GROWSUP) ?
ptr - (void *)current_stack_pointer :
(void *)current_stack_pointer - ptr,
#else
0,
#endif
n);
}
/* Check for bad heap object. */
......
......@@ -37,6 +37,8 @@ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
+= -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
+= -fplugin-arg-stackleak_plugin-arch=$(SRCARCH)
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) \
+= -fplugin-arg-stackleak_plugin-verbose
ifdef CONFIG_GCC_PLUGIN_STACKLEAK
DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable
endif
......
......@@ -429,6 +429,23 @@ static unsigned int stackleak_cleanup_execute(void)
return 0;
}
/*
* STRING_CST may or may not be NUL terminated:
* https://gcc.gnu.org/onlinedocs/gccint/Constant-expressions.html
*/
static inline bool string_equal(tree node, const char *string, int length)
{
if (TREE_STRING_LENGTH(node) < length)
return false;
if (TREE_STRING_LENGTH(node) > length + 1)
return false;
if (TREE_STRING_LENGTH(node) == length + 1 &&
TREE_STRING_POINTER(node)[length] != '\0')
return false;
return !memcmp(TREE_STRING_POINTER(node), string, length);
}
#define STRING_EQUAL(node, str) string_equal(node, str, strlen(str))
static bool stackleak_gate(void)
{
tree section;
......@@ -438,13 +455,17 @@ static bool stackleak_gate(void)
if (section && TREE_VALUE(section)) {
section = TREE_VALUE(TREE_VALUE(section));
if (!strncmp(TREE_STRING_POINTER(section), ".init.text", 10))
if (STRING_EQUAL(section, ".init.text"))
return false;
if (STRING_EQUAL(section, ".devinit.text"))
return false;
if (STRING_EQUAL(section, ".cpuinit.text"))
return false;
if (!strncmp(TREE_STRING_POINTER(section), ".devinit.text", 13))
if (STRING_EQUAL(section, ".meminit.text"))
return false;
if (!strncmp(TREE_STRING_POINTER(section), ".cpuinit.text", 13))
if (STRING_EQUAL(section, ".noinstr.text"))
return false;
if (!strncmp(TREE_STRING_POINTER(section), ".meminit.text", 13))
if (STRING_EQUAL(section, ".entry.text"))
return false;
}
......
......@@ -174,6 +174,16 @@ config GCC_PLUGIN_STACKLEAK
* https://grsecurity.net/
* https://pax.grsecurity.net/
config GCC_PLUGIN_STACKLEAK_VERBOSE
bool "Report stack depth analysis instrumentation" if EXPERT
depends on GCC_PLUGIN_STACKLEAK
depends on !COMPILE_TEST # too noisy
help
This option will cause a warning to be printed each time the
stackleak plugin finds a function it thinks needs to be
instrumented. This is useful for comparing coverage between
builds.
config STACKLEAK_TRACK_MIN_SIZE
int "Minimum stack frame size of functions tracked by STACKLEAK"
default 100
......
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