mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-07-08 18:13:59 +02:00
561dacb457
commit37beb42560upstream. kstack_offset was previously maintained per-cpu, but this caused a couple of issues. So let's instead make it per-task. Issue 1: add_random_kstack_offset() and choose_random_kstack_offset() expected and required to be called with interrupts and preemption disabled so that it could manipulate per-cpu state. But arm64, loongarch and risc-v are calling them with interrupts and preemption enabled. I don't _think_ this causes any functional issues, but it's certainly unexpected and could lead to manipulating the wrong cpu's state, which could cause a minor performance degradation due to bouncing the cache lines. By maintaining the state per-task those functions can safely be called in preemptible context. Issue 2: add_random_kstack_offset() is called before executing the syscall and expands the stack using a previously chosen random offset. choose_random_kstack_offset() is called after executing the syscall and chooses and stores a new random offset for the next syscall. With per-cpu storage for this offset, an attacker could force cpu migration during the execution of the syscall and prevent the offset from being updated for the original cpu such that it is predictable for the next syscall on that cpu. By maintaining the state per-task, this problem goes away because the per-task random offset is updated after the syscall regardless of which cpu it is executing on. Fixes:39218ff4c6("stack: Optionally randomize kernel stack offset each syscall") Closes: https://lore.kernel.org/all/dd8c37bc-795f-4c7a-9086-69e584d8ab24@arm.com/ Cc: stable@vger.kernel.org Acked-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Ryan Roberts <ryan.roberts@arm.com> Link: https://patch.msgid.link/20260303150840.3789438-2-ryan.roberts@arm.com Signed-off-by: Kees Cook <kees@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
103 lines
4.0 KiB
C
103 lines
4.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
#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>
|
|
|
|
DECLARE_STATIC_KEY_MAYBE(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT,
|
|
randomize_kstack_offset);
|
|
|
|
/*
|
|
* Do not use this anywhere else in the kernel. This is used here because
|
|
* it provides an arch-agnostic way to grow the stack with correct
|
|
* 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.
|
|
*/
|
|
#if __has_builtin(__builtin_alloca_uninitialized)
|
|
#define __kstack_alloca __builtin_alloca_uninitialized
|
|
#else
|
|
#define __kstack_alloca __builtin_alloca
|
|
#endif
|
|
|
|
/*
|
|
* Use, at most, 6 bits of entropy (on 64-bit; 8 on 32-bit). This cap is
|
|
* to keep the "VLA" from being unbounded (see above). Additionally clear
|
|
* the bottom 4 bits (on 64-bit systems, 2 for 32-bit), since stack
|
|
* alignment will always be at least word size. This makes the compiler
|
|
* code gen better when it is applying the actual per-arch alignment to
|
|
* the final offset. The resulting randomness is reasonable without overly
|
|
* constraining usable stack space.
|
|
*/
|
|
#ifdef CONFIG_64BIT
|
|
#define KSTACK_OFFSET_MAX(x) ((x) & 0b1111110000)
|
|
#else
|
|
#define KSTACK_OFFSET_MAX(x) ((x) & 0b1111111100)
|
|
#endif
|
|
|
|
/**
|
|
* add_random_kstack_offset - Increase stack utilization by previously
|
|
* chosen random offset
|
|
*
|
|
* This should be used in the syscall entry path after user registers have been
|
|
* stored to the stack. Preemption may be enabled. For testing the resulting
|
|
* entropy, please see: tools/testing/selftests/lkdtm/stack-entropy.sh
|
|
*/
|
|
#define add_random_kstack_offset() do { \
|
|
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
|
|
&randomize_kstack_offset)) { \
|
|
u32 offset = current->kstack_offset; \
|
|
u8 *ptr = __kstack_alloca(KSTACK_OFFSET_MAX(offset)); \
|
|
/* Keep allocation even after "ptr" loses scope. */ \
|
|
asm volatile("" :: "r"(ptr) : "memory"); \
|
|
} \
|
|
} while (0)
|
|
|
|
/**
|
|
* choose_random_kstack_offset - Choose the random offset for the next
|
|
* add_random_kstack_offset()
|
|
*
|
|
* This should only be used during syscall exit. Preemption may be enabled. This
|
|
* position in the syscall flow is done to frustrate attacks from userspace
|
|
* attempting to learn the next offset:
|
|
* - Maximize the timing uncertainty visible from userspace: if the
|
|
* offset is chosen at syscall entry, userspace has much more control
|
|
* over the timing between choosing offsets. "How long will we be in
|
|
* kernel mode?" tends to be more difficult to predict than "how long
|
|
* will we be in user mode?"
|
|
* - Reduce the lifetime of the new offset sitting in memory during
|
|
* kernel mode execution. Exposure of "thread-local" memory content
|
|
* (e.g. current, percpu, etc) tends to be easier than arbitrary
|
|
* location memory exposure.
|
|
*/
|
|
#define choose_random_kstack_offset(rand) do { \
|
|
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
|
|
&randomize_kstack_offset)) { \
|
|
u32 offset = current->kstack_offset; \
|
|
offset = ror32(offset, 5) ^ (rand); \
|
|
current->kstack_offset = offset; \
|
|
} \
|
|
} while (0)
|
|
|
|
static inline void random_kstack_task_init(struct task_struct *tsk)
|
|
{
|
|
tsk->kstack_offset = 0;
|
|
}
|
|
#else /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
|
|
#define add_random_kstack_offset() do { } while (0)
|
|
#define choose_random_kstack_offset(rand) do { } while (0)
|
|
#define random_kstack_task_init(tsk) do { } while (0)
|
|
#endif /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
|
|
|
|
#endif
|