1b092062cSChristian Brauner (Amutable) // SPDX-License-Identifier: GPL-2.0 2b092062cSChristian Brauner (Amutable) /* Copyright (c) 2026 Christian Brauner <brauner@kernel.org> */ 3b092062cSChristian Brauner (Amutable) #include <linux/init.h> 4b092062cSChristian Brauner (Amutable) #include <linux/rcupdate.h> 5b092062cSChristian Brauner (Amutable) #include <linux/refcount.h> 6b092062cSChristian Brauner (Amutable) #include <linux/sched.h> 7b092062cSChristian Brauner (Amutable) #include <linux/sched/coredump.h> 8b092062cSChristian Brauner (Amutable) #include <linux/sched/exec_state.h> 9b092062cSChristian Brauner (Amutable) #include <linux/sched/signal.h> 10b092062cSChristian Brauner (Amutable) #include <linux/slab.h> 11b092062cSChristian Brauner (Amutable) #include <linux/user_namespace.h> 12b092062cSChristian Brauner (Amutable) 13b092062cSChristian Brauner (Amutable) static struct kmem_cache *task_exec_state_cachep; 14b092062cSChristian Brauner (Amutable) 15b092062cSChristian Brauner (Amutable) static void __free_task_exec_state(struct rcu_head *rcu) 16b092062cSChristian Brauner (Amutable) { 17b092062cSChristian Brauner (Amutable) struct task_exec_state *exec_state = container_of(rcu, struct task_exec_state, rcu); 18b092062cSChristian Brauner (Amutable) 19b092062cSChristian Brauner (Amutable) put_user_ns(exec_state->user_ns); 20b092062cSChristian Brauner (Amutable) kmem_cache_free(task_exec_state_cachep, exec_state); 21b092062cSChristian Brauner (Amutable) } 22b092062cSChristian Brauner (Amutable) 23b092062cSChristian Brauner (Amutable) void put_task_exec_state(struct task_exec_state *exec_state) 24b092062cSChristian Brauner (Amutable) { 25b092062cSChristian Brauner (Amutable) if (exec_state && refcount_dec_and_test(&exec_state->count)) 26b092062cSChristian Brauner (Amutable) call_rcu(&exec_state->rcu, __free_task_exec_state); 27b092062cSChristian Brauner (Amutable) } 28b092062cSChristian Brauner (Amutable) 29b092062cSChristian Brauner (Amutable) struct task_exec_state *alloc_task_exec_state(struct user_namespace *user_ns) 30b092062cSChristian Brauner (Amutable) { 31b092062cSChristian Brauner (Amutable) struct task_exec_state *exec_state; 32b092062cSChristian Brauner (Amutable) 33b092062cSChristian Brauner (Amutable) exec_state = kmem_cache_alloc(task_exec_state_cachep, GFP_KERNEL); 34b092062cSChristian Brauner (Amutable) if (!exec_state) 35b092062cSChristian Brauner (Amutable) return NULL; 36b092062cSChristian Brauner (Amutable) refcount_set(&exec_state->count, 1); 37b092062cSChristian Brauner (Amutable) exec_state->dumpable = TASK_DUMPABLE_OFF; 38b092062cSChristian Brauner (Amutable) exec_state->user_ns = get_user_ns(user_ns); 39b092062cSChristian Brauner (Amutable) return exec_state; 40b092062cSChristian Brauner (Amutable) } 41b092062cSChristian Brauner (Amutable) 42b092062cSChristian Brauner (Amutable) struct task_exec_state *task_exec_state_rcu(const struct task_struct *tsk) 43b092062cSChristian Brauner (Amutable) { 44b092062cSChristian Brauner (Amutable) struct task_exec_state *exec_state; 45b092062cSChristian Brauner (Amutable) 46b092062cSChristian Brauner (Amutable) exec_state = rcu_dereference_check(tsk->exec_state, 47b092062cSChristian Brauner (Amutable) lockdep_is_held(&tsk->alloc_lock)); 48b092062cSChristian Brauner (Amutable) WARN_ON_ONCE(!exec_state); 49b092062cSChristian Brauner (Amutable) return exec_state; 50b092062cSChristian Brauner (Amutable) } 51b092062cSChristian Brauner (Amutable) 52b092062cSChristian Brauner (Amutable) struct task_exec_state *task_exec_state_replace(struct task_struct *tsk, 53b092062cSChristian Brauner (Amutable) struct task_exec_state *exec_state) 54b092062cSChristian Brauner (Amutable) { 55b092062cSChristian Brauner (Amutable) /* 56b092062cSChristian Brauner (Amutable) * Updates must hold both locks so callers needing a consistent 57b092062cSChristian Brauner (Amutable) * snapshot of mm + dumpability are covered. 58b092062cSChristian Brauner (Amutable) */ 59b092062cSChristian Brauner (Amutable) lockdep_assert_held(&tsk->alloc_lock); 60b092062cSChristian Brauner (Amutable) lockdep_assert_held_write(&tsk->signal->exec_update_lock); 61b092062cSChristian Brauner (Amutable) 62b092062cSChristian Brauner (Amutable) return rcu_replace_pointer(tsk->exec_state, exec_state, true); 63b092062cSChristian Brauner (Amutable) } 64b092062cSChristian Brauner (Amutable) 65b092062cSChristian Brauner (Amutable) /* 66b092062cSChristian Brauner (Amutable) * The non-CLONE_VM clone path: allocate a fresh exec_state and 67b092062cSChristian Brauner (Amutable) * inherit the parent's dumpable mode and user_ns reference. CLONE_VM 68b092062cSChristian Brauner (Amutable) * siblings refcount-share via copy_exec_state() in fork.c; only this 69b092062cSChristian Brauner (Amutable) * path and execve() ever allocate. 70b092062cSChristian Brauner (Amutable) */ 71b092062cSChristian Brauner (Amutable) int task_exec_state_copy(struct task_struct *tsk) 72b092062cSChristian Brauner (Amutable) { 73b092062cSChristian Brauner (Amutable) struct task_exec_state *src, *dst; 74b092062cSChristian Brauner (Amutable) 75b092062cSChristian Brauner (Amutable) src = rcu_dereference_protected(current->exec_state, true); 76b092062cSChristian Brauner (Amutable) dst = alloc_task_exec_state(src->user_ns); 77b092062cSChristian Brauner (Amutable) if (!dst) 78b092062cSChristian Brauner (Amutable) return -ENOMEM; 79b092062cSChristian Brauner (Amutable) dst->dumpable = READ_ONCE(src->dumpable); 80b092062cSChristian Brauner (Amutable) rcu_assign_pointer(tsk->exec_state, dst); 81b092062cSChristian Brauner (Amutable) return 0; 82b092062cSChristian Brauner (Amutable) } 83b092062cSChristian Brauner (Amutable) 84b092062cSChristian Brauner (Amutable) /* 85b092062cSChristian Brauner (Amutable) * Store TASK_DUMPABLE_* on current->exec_state. All callers 86b092062cSChristian Brauner (Amutable) * (commit_creds, begin_new_exec, prctl(PR_SET_DUMPABLE)) act on the 87b092062cSChristian Brauner (Amutable) * running task, which guarantees ->exec_state is allocated and cannot 88b092062cSChristian Brauner (Amutable) * be replaced under us. 89b092062cSChristian Brauner (Amutable) */ 90b092062cSChristian Brauner (Amutable) void task_exec_state_set_dumpable(enum task_dumpable value) 91b092062cSChristian Brauner (Amutable) { 92b092062cSChristian Brauner (Amutable) struct task_exec_state *exec_state; 93b092062cSChristian Brauner (Amutable) 94b092062cSChristian Brauner (Amutable) if (WARN_ON_ONCE(value > TASK_DUMPABLE_ROOT)) 95b092062cSChristian Brauner (Amutable) value = TASK_DUMPABLE_OFF; 96b092062cSChristian Brauner (Amutable) 97b092062cSChristian Brauner (Amutable) exec_state = rcu_dereference_protected(current->exec_state, true); 98*6b1c66c9SChristian Brauner (Amutable) /* mm-less tasks share init_task's exec_state; never mutate it */ 99*6b1c66c9SChristian Brauner (Amutable) if (WARN_ON_ONCE(exec_state == &init_task_exec_state)) 100*6b1c66c9SChristian Brauner (Amutable) return; 101b092062cSChristian Brauner (Amutable) WRITE_ONCE(exec_state->dumpable, value); 102b092062cSChristian Brauner (Amutable) } 103b092062cSChristian Brauner (Amutable) 104b092062cSChristian Brauner (Amutable) enum task_dumpable task_exec_state_get_dumpable(struct task_struct *task) 105b092062cSChristian Brauner (Amutable) { 106b092062cSChristian Brauner (Amutable) struct task_exec_state *exec_state; 107b092062cSChristian Brauner (Amutable) 108b092062cSChristian Brauner (Amutable) guard(rcu)(); 109b092062cSChristian Brauner (Amutable) exec_state = rcu_dereference(task->exec_state); 110b092062cSChristian Brauner (Amutable) return READ_ONCE(exec_state->dumpable); 111b092062cSChristian Brauner (Amutable) } 112b092062cSChristian Brauner (Amutable) 113b092062cSChristian Brauner (Amutable) void __init exec_state_init(void) 114b092062cSChristian Brauner (Amutable) { 115b092062cSChristian Brauner (Amutable) task_exec_state_cachep = kmem_cache_create("task_exec_state", 116b092062cSChristian Brauner (Amutable) sizeof(struct task_exec_state), 0, 117b092062cSChristian Brauner (Amutable) SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT, 118b092062cSChristian Brauner (Amutable) NULL); 119b092062cSChristian Brauner (Amutable) } 120