Skip to content

Commit fdabbc8

Browse files
ryanhrobgregkh
authored andcommitted
randomize_kstack: Maintain kstack_offset per task
commit 37beb42 upstream. 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: 39218ff ("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>
1 parent c035564 commit fdabbc8

4 files changed

Lines changed: 21 additions & 12 deletions

File tree

include/linux/randomize_kstack.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
DECLARE_STATIC_KEY_MAYBE(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT,
1111
randomize_kstack_offset);
12-
DECLARE_PER_CPU(u32, kstack_offset);
1312

1413
/*
1514
* Do not use this anywhere else in the kernel. This is used here because
@@ -44,15 +43,14 @@ DECLARE_PER_CPU(u32, kstack_offset);
4443
* add_random_kstack_offset - Increase stack utilization by previously
4544
* chosen random offset
4645
*
47-
* This should be used in the syscall entry path when interrupts and
48-
* preempt are disabled, and after user registers have been stored to
49-
* the stack. For testing the resulting entropy, please see:
50-
* tools/testing/selftests/lkdtm/stack-entropy.sh
46+
* This should be used in the syscall entry path after user registers have been
47+
* stored to the stack. Preemption may be enabled. For testing the resulting
48+
* entropy, please see: tools/testing/selftests/lkdtm/stack-entropy.sh
5149
*/
5250
#define add_random_kstack_offset() do { \
5351
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
5452
&randomize_kstack_offset)) { \
55-
u32 offset = raw_cpu_read(kstack_offset); \
53+
u32 offset = current->kstack_offset; \
5654
u8 *ptr = __kstack_alloca(KSTACK_OFFSET_MAX(offset)); \
5755
/* Keep allocation even after "ptr" loses scope. */ \
5856
asm volatile("" :: "r"(ptr) : "memory"); \
@@ -63,9 +61,9 @@ DECLARE_PER_CPU(u32, kstack_offset);
6361
* choose_random_kstack_offset - Choose the random offset for the next
6462
* add_random_kstack_offset()
6563
*
66-
* This should only be used during syscall exit when interrupts and
67-
* preempt are disabled. This position in the syscall flow is done to
68-
* frustrate attacks from userspace attempting to learn the next offset:
64+
* This should only be used during syscall exit. Preemption may be enabled. This
65+
* position in the syscall flow is done to frustrate attacks from userspace
66+
* attempting to learn the next offset:
6967
* - Maximize the timing uncertainty visible from userspace: if the
7068
* offset is chosen at syscall entry, userspace has much more control
7169
* over the timing between choosing offsets. "How long will we be in
@@ -79,14 +77,20 @@ DECLARE_PER_CPU(u32, kstack_offset);
7977
#define choose_random_kstack_offset(rand) do { \
8078
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
8179
&randomize_kstack_offset)) { \
82-
u32 offset = raw_cpu_read(kstack_offset); \
80+
u32 offset = current->kstack_offset; \
8381
offset = ror32(offset, 5) ^ (rand); \
84-
raw_cpu_write(kstack_offset, offset); \
82+
current->kstack_offset = offset; \
8583
} \
8684
} while (0)
85+
86+
static inline void random_kstack_task_init(struct task_struct *tsk)
87+
{
88+
tsk->kstack_offset = 0;
89+
}
8790
#else /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
8891
#define add_random_kstack_offset() do { } while (0)
8992
#define choose_random_kstack_offset(rand) do { } while (0)
93+
#define random_kstack_task_init(tsk) do { } while (0)
9094
#endif /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
9195

9296
#endif

include/linux/sched.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,10 @@ struct task_struct {
15011501
unsigned long prev_lowest_stack;
15021502
#endif
15031503

1504+
#ifdef CONFIG_RANDOMIZE_KSTACK_OFFSET
1505+
u32 kstack_offset;
1506+
#endif
1507+
15041508
#ifdef CONFIG_X86_MCE
15051509
void __user *mce_vaddr;
15061510
__u64 mce_kflags;

init/main.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,6 @@ static inline void initcall_debug_enable(void)
816816
#ifdef CONFIG_RANDOMIZE_KSTACK_OFFSET
817817
DEFINE_STATIC_KEY_MAYBE_RO(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT,
818818
randomize_kstack_offset);
819-
DEFINE_PER_CPU(u32, kstack_offset);
820819

821820
static int __init early_randomize_kstack_offset(char *buf)
822821
{

kernel/fork.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#include <linux/thread_info.h>
9494
#include <linux/stackleak.h>
9595
#include <linux/kasan.h>
96+
#include <linux/randomize_kstack.h>
9697
#include <linux/scs.h>
9798
#include <linux/io_uring.h>
9899
#include <linux/bpf.h>
@@ -2517,6 +2518,7 @@ __latent_entropy struct task_struct *copy_process(
25172518
if (retval)
25182519
goto bad_fork_cleanup_io;
25192520

2521+
random_kstack_task_init(p);
25202522
stackleak_task_init(p);
25212523

25222524
if (pid != &init_struct_pid) {

0 commit comments

Comments
 (0)