From: Christian Brauner <brauner@kernel.org>
To: Jann Horn <jannh@google.com>,
Linus Torvalds <torvalds@linuxfoundation.org>,
Oleg Nesterov <oleg@redhat.com>
Cc: "David Hildenbrand (Arm)" <david@kernel.org>,
Andrew Morton <akpm@linux-foundation.org>,
Qualys Security Advisory <qsa@qualys.com>,
Kees Cook <kees@kernel.org>, Minchan Kim <minchan@kernel.org>,
linux-mm@kvack.org, Suren Baghdasaryan <surenb@google.com>,
Lorenzo Stoakes <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>,
Mike Rapoport <rppt@kernel.org>, Michal Hocko <mhocko@suse.com>,
"Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH RFC v2 2/5] exec: introduce struct task_exec_state and relocate dumpable
Date: Wed, 20 May 2026 16:42:55 +0200 [thread overview]
Message-ID: <20260520-work-task_exec_state-v2-2-9ea88ceb09e6@kernel.org> (raw)
In-Reply-To: <20260520-work-task_exec_state-v2-0-9ea88ceb09e6@kernel.org>
Introduce struct task_exec_state, a per-task RCU-protected structure
that holds the dumpable mode and stays attached to the task for its
full lifetime.
task_exec_state_rcu() is the canonical reader: asserts RCU or
task_lock is held, WARNs on a NULL state, returns the
rcu_dereference()'d pointer.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
include/linux/sched.h | 3 ++
include/linux/sched/exec_state.h | 31 ++++++++++++
kernel/Makefile | 2 +-
kernel/exec_state.c | 105 +++++++++++++++++++++++++++++++++++++++
4 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ee06cba5c6f5..d895c3ff2154 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -962,6 +962,9 @@ struct task_struct {
struct mm_struct *mm;
struct mm_struct *active_mm;
+ /* Exec-time state outliving exit_mm(); see <linux/sched/exec_state.h>. */
+ struct task_exec_state __rcu *exec_state;
+
int exit_state;
int exit_code;
int exit_signal;
diff --git a/include/linux/sched/exec_state.h b/include/linux/sched/exec_state.h
new file mode 100644
index 000000000000..7a267efc34d3
--- /dev/null
+++ b/include/linux/sched/exec_state.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SCHED_EXEC_STATE_H
+#define _LINUX_SCHED_EXEC_STATE_H
+
+#include <linux/init.h>
+#include <linux/rcupdate.h>
+#include <linux/refcount.h>
+#include <linux/sched/coredump.h>
+#include <linux/user_namespace.h>
+
+struct task_exec_state {
+ refcount_t count;
+ enum task_dumpable dumpable;
+ struct user_namespace *user_ns;
+ struct rcu_head rcu;
+};
+
+struct task_exec_state *alloc_task_exec_state(void);
+void put_task_exec_state(struct task_exec_state *es);
+struct task_exec_state *task_exec_state_rcu(const struct task_struct *tsk);
+struct task_exec_state *task_exec_state_replace(struct task_struct *tsk,
+ struct task_exec_state *exec_state);
+void task_exec_state_set_dumpable(enum task_dumpable value);
+enum task_dumpable task_exec_state_get_dumpable(struct task_struct *task);
+void copy_exec_state(struct task_struct *tsk);
+void __init exec_state_init(void);
+
+DEFINE_FREE(put_task_exec_state, struct task_exec_state *,
+ if (_T) put_task_exec_state(_T))
+
+#endif /* _LINUX_SCHED_EXEC_STATE_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index 6785982013dc..1e1a31673577 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -3,7 +3,7 @@
# Makefile for the linux kernel.
#
-obj-y = fork.o exec_domain.o panic.o \
+obj-y = fork.o exec_domain.o exec_state.o panic.o \
cpu.o exit.o softirq.o resource.o \
sysctl.o capability.o ptrace.o user.o \
signal.o sys.o umh.o workqueue.o pid.o task_work.o \
diff --git a/kernel/exec_state.c b/kernel/exec_state.c
new file mode 100644
index 000000000000..85178b1d2c57
--- /dev/null
+++ b/kernel/exec_state.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/init.h>
+#include <linux/rcupdate.h>
+#include <linux/refcount.h>
+#include <linux/sched.h>
+#include <linux/sched/coredump.h>
+#include <linux/sched/exec_state.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+
+static struct kmem_cache *task_exec_state_cachep;
+
+static void __free_task_exec_state(struct rcu_head *rcu)
+{
+ struct task_exec_state *es = container_of(rcu, struct task_exec_state, rcu);
+
+ kmem_cache_free(task_exec_state_cachep, es);
+}
+
+void put_task_exec_state(struct task_exec_state *es)
+{
+ if (es && refcount_dec_and_test(&es->count))
+ call_rcu(&es->rcu, __free_task_exec_state);
+}
+
+struct task_exec_state *alloc_task_exec_state(void)
+{
+ struct task_exec_state *es;
+
+ es = kmem_cache_alloc(task_exec_state_cachep, GFP_KERNEL);
+ if (!es)
+ return NULL;
+ refcount_set(&es->count, 1);
+ es->dumpable = TASK_DUMPABLE_OFF;
+ return es;
+}
+
+struct task_exec_state *task_exec_state_rcu(const struct task_struct *tsk)
+{
+ RCU_LOCKDEP_WARN(!rcu_read_lock_held() && !lockdep_is_held(&tsk->alloc_lock),
+ "task_exec_state_rcu() requires RCU or task_lock");
+ WARN_ON_ONCE(!tsk->exec_state);
+ return rcu_dereference(tsk->exec_state);
+}
+
+struct task_exec_state *task_exec_state_replace(struct task_struct *tsk,
+ struct task_exec_state *exec_state)
+{
+ /*
+ * Updates must hold both locks so callers needing a consistent
+ * snapshot of mm + dumpability are covered.
+ */
+ lockdep_assert_held(&tsk->alloc_lock);
+ lockdep_assert_held_write(&tsk->signal->exec_update_lock);
+
+ return rcu_replace_pointer(tsk->exec_state, exec_state, true);
+}
+
+/*
+ * exec_state is anchored to the execve() that established the current
+ * privilege domain. All clone() variants refcount-share it; only a
+ * subsequent execve() in the child swaps in a fresh one.
+ */
+void copy_exec_state(struct task_struct *tsk)
+{
+ struct task_exec_state *es = current->exec_state;
+
+ refcount_inc(&es->count);
+ rcu_assign_pointer(tsk->exec_state, es);
+}
+
+/*
+ * Store TASK_DUMPABLE_* on current->exec_state. All callers
+ * (commit_creds, begin_new_exec, prctl(PR_SET_DUMPABLE)) act on the
+ * running task, which guarantees ->exec_state is allocated and cannot
+ * be replaced under us.
+ */
+void task_exec_state_set_dumpable(enum task_dumpable value)
+{
+ struct task_exec_state *es;
+
+ if (WARN_ON(value > TASK_DUMPABLE_ROOT))
+ value = TASK_DUMPABLE_OFF;
+
+ es = rcu_dereference_protected(current->exec_state, true);
+ WRITE_ONCE(es->dumpable, value);
+}
+
+enum task_dumpable task_exec_state_get_dumpable(struct task_struct *task)
+{
+ struct task_exec_state *es;
+
+ guard(rcu)();
+ es = rcu_dereference(task->exec_state);
+ return READ_ONCE(es->dumpable);
+}
+
+void __init exec_state_init(void)
+{
+ task_exec_state_cachep = kmem_cache_create("task_exec_state",
+ sizeof(struct task_exec_state), 0,
+ SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT,
+ NULL);
+}
--
2.47.3
next prev parent reply other threads:[~2026-05-20 14:43 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-20 14:42 [PATCH RFC v2 0/5] ptrace: keep mm metadata accessible past exit_mm() Christian Brauner
2026-05-20 14:42 ` [PATCH RFC v2 1/5] sched/coredump: introduce enum task_dumpable Christian Brauner
2026-05-20 16:27 ` Jann Horn
2026-05-20 14:42 ` Christian Brauner [this message]
2026-05-20 15:14 ` [PATCH RFC v2 2/5] exec: introduce struct task_exec_state and relocate dumpable Linus Torvalds
2026-05-20 15:24 ` Christian Brauner
2026-05-20 16:27 ` Jann Horn
2026-05-20 19:47 ` Christian Brauner
2026-05-20 14:42 ` [PATCH RFC v2 3/5] ptrace: add ptracer_access_allowed() Christian Brauner
2026-05-20 16:28 ` Jann Horn
2026-05-20 14:42 ` [PATCH RFC v2 4/5] exec_state: relocate dumpable information Christian Brauner
2026-05-20 19:21 ` Jann Horn
2026-05-20 19:47 ` Christian Brauner
2026-05-20 14:42 ` [PATCH RFC v2 5/5] cred: switch dumpability lowering to task_exec_state Christian Brauner
2026-05-20 18:44 ` Jann Horn
2026-05-20 15:08 ` [PATCH RFC v2 0/5] ptrace: keep mm metadata accessible past exit_mm() Christian Brauner
2026-05-20 16:27 ` Jann Horn
2026-05-20 16:52 ` Linus Torvalds
2026-05-20 16:55 ` Linus Torvalds
2026-05-20 18:09 ` Jann Horn
2026-05-20 18:12 ` Linus Torvalds
2026-05-20 19:46 ` Christian Brauner
2026-05-20 17:29 ` Jann Horn
2026-05-20 18:11 ` Linus Torvalds
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260520-work-task_exec_state-v2-2-9ea88ceb09e6@kernel.org \
--to=brauner@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=jannh@google.com \
--cc=kees@kernel.org \
--cc=liam@infradead.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=minchan@kernel.org \
--cc=oleg@redhat.com \
--cc=qsa@qualys.com \
--cc=rppt@kernel.org \
--cc=surenb@google.com \
--cc=torvalds@linuxfoundation.org \
--cc=vbabka@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.