hardening updates for v5.18-rc1

- 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)
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmI4kXMWHGtlZXNjb29r
 QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJhBoD/wJFr0s13Cvsbibuk7PLAPJlQe9
 QBMolrrS9+JNoqdIMiILrmthCPnDBkBNrU/YvfkIyGQOO2RGxrtZVzLhyHKCDg6u
 iIkNG9S5D12ucEdqqLWdZxyBZcQuR6Rf//lGvtx8ps+jYy8fDwRekurJIb3kWl5u
 qB0O0PFd+RjGgvtm+Fh8h0FiBMxbKfPXI+s7W2rCfcwe+w5Z24YD1eoCHmnQJYcu
 Mnuk7cHsx2TFms4UqUK1Z/0EBpCKNEEX4s0z/nrfu8dRTPvLqLgbGpcmXTkik9PN
 BucIxgdRqqYbTyGvhsDhpEUVfmFcQzdPmuMnnnUc8BiXy9EqGqSfjMEzutuf+RS7
 0i4LWoDW2LYMUixqDLAMdLpwdC2Ca7hP62kE4vNVqW3jBty+jhPBVO6ddhHO14nd
 q6m+CQz0SVTIyrLI4N+TNg/EIj2DpBpAhs49QWDOL/ZqP0ewYk8Ef8pXKgJo2jJC
 aAs+18pdpoVCEs1fztzjuWZT77iTmziYhb2BOMnT4yBcAdifi7eW6l0pYsgfxoJ/
 WC/MmTWt08/IHBk09d8GbFdoP8byDUgzmzUUoskJJH2JA7475xM6qhI2J627Lpth
 baEv3UT8JWBBX+koU2wxhxKgscIvbNjJjpEGNt2YuBBeQ4lrlijsFzQjmu62gZDL
 LG0XOVV97/1V9uJ2CA==
 =yaWZ
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2022-03-21 19:32:04 -07:00
commit 2142b7f0c6
21 changed files with 139 additions and 31 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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) */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) \

View File

@ -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,

View File

@ -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)))

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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. */

View File

@ -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

View File

@ -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 (!strncmp(TREE_STRING_POINTER(section), ".devinit.text", 13))
if (STRING_EQUAL(section, ".devinit.text"))
return false;
if (!strncmp(TREE_STRING_POINTER(section), ".cpuinit.text", 13))
if (STRING_EQUAL(section, ".cpuinit.text"))
return false;
if (!strncmp(TREE_STRING_POINTER(section), ".meminit.text", 13))
if (STRING_EQUAL(section, ".meminit.text"))
return false;
if (STRING_EQUAL(section, ".noinstr.text"))
return false;
if (STRING_EQUAL(section, ".entry.text"))
return false;
}

View File

@ -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