stack: Introduce CONFIG_RANDOMIZE_KSTACK_OFFSET
PATCH v2 1/2 stack: Introduce CONFIG_RANDOMIZE_KSTACK_OFFSET
The randomize_kstack_offset feature is unconditionally compiled in when the architecture supports it.
To add constraints on compiler versions, we require a dedicated Kconfig variable. Therefore, introduce RANDOMIZE_KSTACK_OFFSET.
Furthermore, this option is now also configurable by EXPERT kernels:
while the feature is supposed to have zero performance overhead when disabled, due to its use of static branches, there are few cases where giving a distribution the option to disable the feature entirely makes sense. For example, in very resource constrained environments, which would never enable the feature to begin with, in which case the additional kernel code size increase would be redundant.
--- arch/Kconfig | 23 ++++++++++++++++++----- include/linux/randomize_kstack.h | 5 +++++ init/main.c | 2 +- 3 files changed, 24 insertions(+), 6 deletions(-)
arch/Kconfig +18 -5
@@ -1159,16 +1159,29 @@ 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 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
include/linux/randomize_kstack.h +5 -0
@@ -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>
@@ -50,5 +51,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
init/main.c +1 -1
@@ -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);
Signature · 1 line
2.35.0.rc2.247.g8bbb082509-goog
PATCH v2 2/2 stack: Constrain and fix stack offset randomization with Clang builds
All supported versions of Clang perform auto-init of __builtin_alloca() when stack auto-init is on (CONFIG_INIT_STACK_ALL_{ZERO,PATTERN}).
add_random_kstack_offset() uses __builtin_alloca() to add a stack offset. This means, when CONFIG_INIT_STACK_ALL_{ZERO,PATTERN} is enabled, add_random_kstack_offset() will auto-init that unused portion of the stack used to add an offset.
There are several problems with this:
1. These offsets can be as large as 1023 bytes. Performing memset() on them isn't exactly cheap, and this is done on every syscall entry.
2. Architectures adding add_random_kstack_offset() to syscall entry implemented in C require them to be 'noinstr' (e.g. see x86 and s390). The potential problem here is that a call to memset may occur, which is not noinstr.
A x86_64 defconfig kernel with Clang 11 and CONFIG_VMLINUX_VALIDATION shows:
| vmlinux.o: warning: objtool: do_syscall_64()+0x9d: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: do_int80_syscall_32()+0xab: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: __do_fast_syscall_32()+0xe2: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: fixup_bad_iret()+0x2f: call to memset() leaves .noinstr.text section
Clang 14 (unreleased) will introduce a way to skip alloca initialization via __builtin_alloca_uninitialized() (https://reviews.llvm.org/D115440).
Constrain RANDOMIZE_KSTACK_OFFSET to only be enabled if no stack auto-init is enabled, the compiler is GCC, or Clang is version 14+. Use __builtin_alloca_uninitialized() if the compiler provides it, as is done by Clang 14.
---
v2:
* Update comment to point out which compilers initialize allocas.
--- arch/Kconfig | 1 + include/linux/randomize_kstack.h | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
arch/Kconfig +1 -0
@@ -1163,6 +1163,7 @@ 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
include/linux/randomize_kstack.h +14 -2
@@ -17,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
@@ -37,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. */ \ asmvolatile ("" ::"r" (ptr) :"memory" ); \ } \
Signature · 1 line
2.35.0.rc2.247.g8bbb082509-goog
Re: [PATCH v2 2/2] stack: Constrain and fix stack offset randomization with Clang builds
Quoted 29 lines · Marco Elver
All supported versions of Clang perform auto-init of
__builtin_alloca()when stack auto-init is on (CONFIG_INIT_STACK_ALL_{ZERO,PATTERN}).
add_random_kstack_offset()uses__builtin_alloca()to add a stack offset. This means, when CONFIG_INIT_STACK_ALL_{ZERO,PATTERN} is enabled,add_random_kstack_offset()will auto-init that unused portion of the stack used to add an offset.There are several problems with this:
1. These offsets can be as large as 1023 bytes. Performing
memset()on them isn't exactly cheap, and this is done on every syscall entry.2. Architectures adding
add_random_kstack_offset()to syscall entry implemented in C require them to be 'noinstr' (e.g. see x86 and s390). The potential problem here is that a call to memset may occur, which is not noinstr.A
x86_64defconfig kernel with Clang 11 andCONFIG_VMLINUX_VALIDATIONshows:| vmlinux.o: warning: objtool: do_syscall_64()+0x9d: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: do_int80_syscall_32()+0xab: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: __do_fast_syscall_32()+0xe2: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: fixup_bad_iret()+0x2f: call to memset() leaves .noinstr.text sectionClang 14 (unreleased) will introduce a way to skip alloca initialization via
__builtin_alloca_uninitialized()(https://reviews.llvm.org/D115440).
Quoted 8 lines
Constrain
RANDOMIZE_KSTACK_OFFSETto only be enabled if no stack auto-init is enabled, the compiler is GCC, or Clang is version 14+. Use__builtin_alloca_uninitialized()if the compiler provides it, as is done by Clang 14.Fixes 39218ff4c625 ("stack: Optionally randomize kernel stack offset each syscall")Signed-off-by Marco Elver <[email protected]>
Thanks for the tweaks; this looks good to me now.
Signature · 1 line
Kees Cook
Re: [PATCH v2 2/2] stack: Constrain and fix stack offset randomization with Clang builds
Quoted 35 lines · Kees Cook
Quoted 34 lines · Marco Elver
All supported versions of Clang perform auto-init of
__builtin_alloca()when stack auto-init is on (CONFIG_INIT_STACK_ALL_{ZERO,PATTERN}).
add_random_kstack_offset()uses__builtin_alloca()to add a stack offset. This means, when CONFIG_INIT_STACK_ALL_{ZERO,PATTERN} is enabled,add_random_kstack_offset()will auto-init that unused portion of the stack used to add an offset.There are several problems with this:
1. These offsets can be as large as 1023 bytes. Performing
memset()on them isn't exactly cheap, and this is done on every syscall entry.2. Architectures adding
add_random_kstack_offset()to syscall entry implemented in C require them to be 'noinstr' (e.g. see x86 and s390). The potential problem here is that a call to memset may occur, which is not noinstr.A
x86_64defconfig kernel with Clang 11 andCONFIG_VMLINUX_VALIDATIONshows:| vmlinux.o: warning: objtool: do_syscall_64()+0x9d: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: do_int80_syscall_32()+0xab: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: __do_fast_syscall_32()+0xe2: call to memset() leaves .noinstr.text section | vmlinux.o: warning: objtool: fixup_bad_iret()+0x2f: call to memset() leaves .noinstr.text sectionClang 14 (unreleased) will introduce a way to skip alloca initialization via
__builtin_alloca_uninitialized()(https://reviews.llvm.org/D115440).Constrain
RANDOMIZE_KSTACK_OFFSETto only be enabled if no stack auto-init is enabled, the compiler is GCC, or Clang is version 14+. Use__builtin_alloca_uninitialized()if the compiler provides it, as is done by Clang 14.
Quoted 7 lines
Quoted 3 lines
Fixes 39218ff4c625 ("stack: Optionally randomize kernel stack offset each syscall")Signed-off-by Marco Elver <[email protected]>Thanks for the tweaks; this looks good to me now.
Acked-by Kees Cook <[email protected]>
Kees, which tree do randomize_kstack changes go through these days? I've seen previous patches went through -tip via Thomas.
Thanks,
-- Marco
Quoted 14 lines · Marco Elver
The
randomize_kstack_offsetfeature is unconditionally compiled in when the architecture supports it.To add constraints on compiler versions, we require a dedicated
Kconfigvariable. Therefore, introduceRANDOMIZE_KSTACK_OFFSET.Furthermore, this option is now also configurable by EXPERT kernels:
while the feature is supposed to have zero performance overhead when disabled, due to its use of static branches, there are few cases where giving a distribution the option to disable the feature entirely makes sense. For example, in very resource constrained environments, which would never enable the feature to begin with, in which case the additional kernel code size increase would be redundant.
Quoted 1 line
Signed-off-by Marco Elver <[email protected]>
Signature · 1 line
Kees Cook
Quoted 14 lines · Marco Elver
The
randomize_kstack_offsetfeature is unconditionally compiled in when the architecture supports it.To add constraints on compiler versions, we require a dedicated
Kconfigvariable. Therefore, introduceRANDOMIZE_KSTACK_OFFSET.Furthermore, this option is now also configurable by EXPERT kernels:
while the feature is supposed to have zero performance overhead when disabled, due to its use of static branches, there are few cases where giving a distribution the option to disable the feature entirely makes sense. For example, in very resource constrained environments, which would never enable the feature to begin with, in which case the additional kernel code size increase would be redundant.
Quoted 2 lines
Signed-off-by Marco Elver <[email protected]>Reviewed-by Nathan Chancellor <[email protected]>
For both patches:
Kees Cook
AppliedQuoted 14 lines · Marco Elver
The
randomize_kstack_offsetfeature is unconditionally compiled in when the architecture supports it.To add constraints on compiler versions, we require a dedicated
Kconfigvariable. Therefore, introduceRANDOMIZE_KSTACK_OFFSET.Furthermore, this option is now also configurable by EXPERT kernels:
while the feature is supposed to have zero performance overhead when disabled, due to its use of static branches, there are few cases where giving a distribution the option to disable the feature entirely makes sense. For example, in very resource constrained environments, which would never enable the feature to begin with, in which case the additional kernel code size increase would be redundant.
Quoted 1 line
[...]
Applied to for-next/hardening, thanks!
[1/2] stack: Introduce CONFIG_RANDOMIZE_KSTACK_OFFSET
https://git.kernel.org/kees/c/8cb37a5974a4 [2/2] stack: Constrain and fix stack offset randomization with Clang builds https://git.kernel.org/kees/c/efa90c11f62e
Signature · 1 line
Kees Cook