[PATCH RFC v3 37/90] x86: give a task without an mm no user state

From: Josef Bacik

Date: Thu Nov 05 2026 - 10:14:17 EST


A later patch creates tasks that have no mm and whose first action is to
exec. Such a task has no use for the user state of its parent, and none of
that state should be left in it. A kernel thread can keep the state
because it never returns to user mode, but these tasks do.

In copy_thread(), clear the segment bases and selectors, the I/O bitmap
and the shadow stack state for a task without an mm. The registers are
already cleared, as for every task that starts in a kernel function.

Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
arch/x86/include/asm/shstk.h | 2 ++
arch/x86/kernel/process.c | 24 +++++++++++++++++++++---
arch/x86/kernel/shstk.c | 14 ++++++++++++++
3 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/shstk.h b/arch/x86/include/asm/shstk.h
index fc7dcec58fd4..fdf43119c2bb 100644
--- a/arch/x86/include/asm/shstk.h
+++ b/arch/x86/include/asm/shstk.h
@@ -16,6 +16,7 @@ struct thread_shstk {

long shstk_prctl(struct task_struct *task, int option, unsigned long arg2);
void reset_thread_features(void);
+void shstk_clear_thread(struct task_struct *p);
unsigned long shstk_alloc_thread_stack(struct task_struct *p, u64 clone_flags,
unsigned long stack_size);
void shstk_free(struct task_struct *p);
@@ -29,6 +30,7 @@ int shstk_push(u64 val);
static inline long shstk_prctl(struct task_struct *task, int option,
unsigned long arg2) { return -EINVAL; }
static inline void reset_thread_features(void) {}
+static inline void shstk_clear_thread(struct task_struct *p) {}
static inline unsigned long shstk_alloc_thread_stack(struct task_struct *p,
u64 clone_flags,
unsigned long stack_size) { return 0; }
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 346c438ac880..d2be7a9647ce 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -219,9 +219,23 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
* is disabled, new_ssp will remain 0, and fpu_clone() will know not to
* update it.
*/
- new_ssp = shstk_alloc_thread_stack(p, clone_flags, args->stack_size);
- if (IS_ERR_VALUE(new_ssp))
- return PTR_ERR((void *)new_ssp);
+ if (unlikely(args->fn && !p->mm)) {
+ /*
+ * It has no address space to have a shadow stack in, and what
+ * it will run it has yet to exec. Nothing of the user state of
+ * whoever created it is any of its business.
+ */
+ new_ssp = 0;
+ shstk_clear_thread(p);
+#ifdef CONFIG_X86_64
+ p->thread.fsindex = p->thread.gsindex = 0;
+ p->thread.fsbase = p->thread.gsbase = 0;
+#endif
+ } else {
+ new_ssp = shstk_alloc_thread_stack(p, clone_flags, args->stack_size);
+ if (IS_ERR_VALUE(new_ssp))
+ return PTR_ERR((void *)new_ssp);
+ }

fpu_clone(p, clone_flags, args->fn, new_ssp);

@@ -258,6 +272,10 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
*/
childregs->sp = 0;
childregs->ip = 0;
+ if (!p->mm) {
+ memset(childregs, 0, sizeof(struct pt_regs));
+ p->thread.pkru = pkru_get_init_value();
+ }
kthread_frame_init(frame, args->fn, args->fn_arg);
return 0;
}
diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c
index eb690ba90180..41d77efd7952 100644
--- a/arch/x86/kernel/shstk.c
+++ b/arch/x86/kernel/shstk.c
@@ -183,6 +183,20 @@ void reset_thread_features(void)
current->thread.features_locked = 0;
}

+/**
+ * shstk_clear_thread - a new task takes no shadow stack state from its parent
+ * @p: a task that is being created
+ *
+ * For a task without an mm, which has no address space to have a shadow stack
+ * in. What reset_thread_features() does for the caller at exec.
+ */
+void shstk_clear_thread(struct task_struct *p)
+{
+ memset(&p->thread.shstk, 0, sizeof(struct thread_shstk));
+ p->thread.features = 0;
+ p->thread.features_locked = 0;
+}
+
unsigned long shstk_alloc_thread_stack(struct task_struct *tsk, u64 clone_flags,
unsigned long stack_size)
{

--
2.55.0