projects/linux·commit 9c9e6bd4

Merge tag 'kernel-7.2-rc1.task_exec_state' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

9c9e6bd4 · Linus Torvalds <[email protected]> · 2026-06-15 03:00:58 +05:30

Pull task_exec_state updates from Christian Brauner:
 "This introduces a new per-task task_exec_state structure and relocates
  the dumpable mode and the user namespace captured at execve() from
  mm_struct onto it. It stays attached to the task for its full
  lifetime.

  __ptrace_may_access() and several /proc owner and visibility checks
  need to consult two pieces of state for any observable task, including
  zombies that have already gone through exit_mm(): the dumpable mode
  and the user namespace captured at execve(). Both live on mm_struct
  today, which exit_mm() clears from the task long before the task is
  reaped. A reader that races with do_exit() observes task->mm == NULL
  and either fails the check or falls back to init_user_ns - which
  denies legitimate access to non-dumpable zombies that were running in
  a nested user namespace.

  mm_struct loses ->user_ns and the dumpability bits in ->flags.
  MMF_DUMPABLE_BITS is reserved so the MMF_DUMP_FILTER_* layout exposed
  via /proc/<pid>/coredump_filter stays stable. task->user_dumpable and
  its exit_mm() snapshot are removed.

  task_exec_state is the privilege domain established by an execve().
  Within a thread group it is shared via refcount; across thread groups
  each task has its own:

   - CLONE_VM siblings (thread-group members, io_uring workers)
     refcount-share the parent's exec_state.

   - Non-CLONE_VM clones (fork(), vfork() without CLONE_VM) allocate a
     fresh exec_state inheriting the parent's dumpable mode and user_ns.

   - execve() in the child allocates a fresh instance and installs it
     under task_lock + exec_update_lock via task_exec_state_replace().

   - Credential changes (setresuid, capset, ...) and
     prctl(PR_SET_DUMPABLE) update dumpability on the current task's
     exec_state, i.e., on the thread group's shared instance.

  On top of this exec_mmap() no longer tears down the old mm while
  holding exec_update_lock for writing and cred_guard_mutex. Neither
  lock is needed for that: exec_update_lock only exists to make the mm
  swap atomic with the later commit_creds() and all its readers operate
  on the new mm; none looks at the detached old mm.

  The cost was real: __mmput() runs exit_mmap() over the entire old
  address space and can block in exit_aio() waiting for in-flight AIO,
  so execve() of a large process blocked ptrace_attach() and every
  exec_update_lock reader for the duration of the teardown.

  The old mm is now stashed in bprm->old_mm and released from
  setup_new_exec() after both locks are dropped, with a backstop in
  free_bprm() for the error paths"

* tag 'kernel-7.2-rc1.task_exec_state' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  exec: free the old mm outside the exec locks
  exec_state: relocate dumpable information
  ptrace: add ptracer_access_allowed()
  exec: introduce struct task_exec_state
  sched/coredump: introduce enum task_dumpable
Commit
9c9e6bd4cca02f2d183eb260451fb6018f9ee67e
Parents
5d15ab71 · 38205ecb

Merge commit. Changes below are compared with the first parent: 5d15ab71.

Changes

23 files changed · +329 −155

Only the first 600 diff lines are shown. Download the complete patch.

arch/arm64/kernel/mte.c

View file
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index 6874b16..1a9aad6 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/prctl.h>
+#include <linux/ptrace.h>
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/string.h>
@@ -537,16 +538,13 @@ static int access_remote_tags(struct task_struct *tsk, unsigned long addr,
if (!mm)
return -EPERM;
- if (!tsk->ptrace || (current != tsk->parent) ||
- ((get_dumpable(mm) != SUID_DUMP_USER) &&
- !ptracer_capable(tsk, mm->user_ns))) {
+ if (!ptracer_access_allowed(tsk)) {
mmput(mm);
return -EPERM;
}
ret = __access_remote_tags(mm, addr, kiov, gup_flags);
mmput(mm);
-
return ret;
}

drivers/firmware/efi/efi.c

View file
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 318d1cc..0327a39 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -73,7 +73,6 @@ struct mm_struct efi_mm = {
MMAP_LOCK_INITIALIZER(efi_mm)
.page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
- .user_ns = &init_user_ns,
#ifdef CONFIG_SCHED_MM_CID
.mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(efi_mm.mm_cid.lock),
#endif

fs/coredump.c

View file
diff --git a/fs/coredump.c b/fs/coredump.c
index bb6fdb1..e943569 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -395,8 +395,7 @@ static bool coredump_parse(struct core_name *cn, struct coredump_params *cprm,
cred->gid));
break;
case 'd':
- err = cn_printf(cn, "%d",
- __get_dumpable(cprm->mm_flags));
+ err = cn_printf(cn, "%d", cprm->dumpable);
break;
/* signal that caused the coredump */
case 's':
@@ -869,11 +868,11 @@ static inline void coredump_sock_shutdown(struct file *file) { }
static inline bool coredump_socket(struct core_name *cn, struct coredump_params *cprm) { return false; }
#endif
-/* cprm->mm_flags contains a stable snapshot of dumpability flags. */
+/* cprm->dumpable is the snapshot of task dumpability at dump start. */
static inline bool coredump_force_suid_safe(const struct coredump_params *cprm)
{
/* Require nonrelative corefile path and be extra careful. */
- return __get_dumpable(cprm->mm_flags) == SUID_DUMP_ROOT;
+ return cprm->dumpable == TASK_DUMPABLE_ROOT;
}
static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,
@@ -1085,7 +1084,7 @@ static inline bool coredump_skip(const struct coredump_params *cprm,
return true;
if (!binfmt->core_dump)
return true;
- if (!__get_dumpable(cprm->mm_flags))
+ if (cprm->dumpable == TASK_DUMPABLE_OFF)
return true;
return false;
}
@@ -1170,14 +1169,9 @@ void vfs_coredump(const kernel_siginfo_t *siginfo)
struct coredump_params cprm = {
.siginfo = siginfo,
.limit = rlimit(RLIMIT_CORE),
- /*
- * We must use the same mm->flags while dumping core to avoid
- * inconsistency of bit flags, since this flag is not protected
- * by any locks.
- *
- * Note that we only care about MMF_DUMP* flags.
- */
- .mm_flags = __mm_flags_get_dumpable(mm),
+ /* Snapshot MMF_DUMP_FILTER_* (unlocked) and dumpable for the dump. */
+ .mm_flags = __mm_flags_get_word(mm),
+ .dumpable = task_exec_state_get_dumpable(current),
.vma_meta = NULL,
.cpu = raw_smp_processor_id(),
};
@@ -1419,7 +1413,7 @@ EXPORT_SYMBOL(dump_align);
void validate_coredump_safety(void)
{
- if (suid_dumpable == SUID_DUMP_ROOT &&
+ if (suid_dumpable == TASK_DUMPABLE_ROOT &&
core_pattern[0] != '/' && core_pattern[0] != '|' && core_pattern[0] != '@') {
coredump_report_failure("Unsafe core_pattern used with fs.suid_dumpable=2: "

fs/exec.c

View file
diff --git a/fs/exec.c b/fs/exec.c
index ba12b4c..824b46c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -35,6 +35,7 @@
#include <linux/init.h>
#include <linux/sched/mm.h>
#include <linux/sched/coredump.h>
+#include <linux/sched/exec_state.h>
#include <linux/sched/signal.h>
#include <linux/sched/numa_balancing.h>
#include <linux/sched/task.h>
@@ -263,6 +264,9 @@ static int bprm_mm_init(struct linux_binprm *bprm)
if (!mm)
goto err;
+ /* Staged for would_dump() narrowing; consumed by begin_new_exec(). */
+ bprm->user_ns = get_user_ns(current_user_ns());
+
/* Save current stack limit for all calculations made during exec. */
task_lock(current->group_leader);
bprm->rlim_stack = current->signal->rlim[RLIMIT_STACK];
@@ -832,14 +836,21 @@ EXPORT_SYMBOL(read_code);
/*
* Maps the mm_struct mm into the current task struct.
* On success, this function returns with exec_update_lock
- * held for writing.
+ * held for writing. The replaced address space is stashed in
+ * bprm->old_mm for setup_new_exec() to release outside the lock.
*/
-static int exec_mmap(struct mm_struct *mm)
+static int exec_mmap(struct linux_binprm *bprm)
{
+ struct task_exec_state *exec_state __free(put_task_exec_state) = NULL;
+ struct mm_struct *mm = bprm->mm;
struct task_struct *tsk;
struct mm_struct *old_mm, *active_mm;
int ret;
+ exec_state = alloc_task_exec_state(bprm->user_ns);
+ if (!exec_state)
+ return -ENOMEM;
+
/* Notify parent that we're no longer interested in the old VM */
tsk = current;
old_mm = current->mm;
@@ -870,6 +881,7 @@ static int exec_mmap(struct mm_struct *mm)
tsk->active_mm = mm;
tsk->mm = mm;
mm_init_cid(mm, tsk);
+ exec_state = task_exec_state_replace(tsk, exec_state);
/*
* This prevents preemption while active_mm is being loaded and
* it and mm are being updated, which could cause problems for
@@ -888,15 +900,22 @@ static int exec_mmap(struct mm_struct *mm)
if (old_mm) {
mmap_read_unlock(old_mm);
BUG_ON(active_mm != old_mm);
- setmax_mm_hiwater_rss(&tsk->signal->maxrss, old_mm);
- mm_update_next_owner(old_mm);
- mmput(old_mm);
+ /* Defer teardown to setup_new_exec(), outside the exec locks. */
+ bprm->old_mm = old_mm;
return 0;
}
mmdrop_lazy_tlb(active_mm);
return 0;
}
+/* Release the address space replaced by exec, outside the exec locks. */
+static void exec_mm_put_old(struct mm_struct *old_mm)
+{
+ setmax_mm_hiwater_rss(&current->signal->maxrss, old_mm);
+ mm_update_next_owner(old_mm);
+ mmput(old_mm);
+}
+
static int de_thread(struct task_struct *tsk)
{
struct signal_struct *sig = tsk->signal;
@@ -1145,7 +1164,7 @@ int begin_new_exec(struct linux_binprm * bprm)
* Release all of the old mmap stuff
*/
acct_arg_size(bprm, 0);
- retval = exec_mmap(bprm->mm);
+ retval = exec_mmap(bprm);
if (retval)
goto out;
@@ -1210,9 +1229,9 @@ int begin_new_exec(struct linux_binprm * bprm)
if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP ||
!(uid_eq(current_euid(), current_uid()) &&
gid_eq(current_egid(), current_gid())))
- set_dumpable(current->mm, suid_dumpable);
+ task_exec_state_set_dumpable(suid_dumpable);
else
- set_dumpable(current->mm, SUID_DUMP_USER);
+ task_exec_state_set_dumpable(TASK_DUMPABLE_OWNER);
perf_event_exec();
@@ -1261,7 +1280,7 @@ int begin_new_exec(struct linux_binprm * bprm)
* wait until new credentials are committed
* by commit_creds() above
*/
- if (get_dumpable(me->mm) != SUID_DUMP_USER)
+ if (task_exec_state_get_dumpable(me) != TASK_DUMPABLE_OWNER)
perf_event_exit_task(me);
/*
* cred_guard_mutex must be held at least to this point to prevent
@@ -1298,14 +1317,14 @@ void would_dump(struct linux_binprm *bprm, struct file *file)
struct user_namespace *old, *user_ns;
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
- /* Ensure mm->user_ns contains the executable */
- user_ns = old = bprm->mm->user_ns;
+ /* Ensure bprm->user_ns contains the executable. */
+ user_ns = old = bprm->user_ns;
while ((user_ns != &init_user_ns) &&
!privileged_wrt_inode_uidgid(user_ns, idmap, inode))
user_ns = user_ns->parent;
if (old != user_ns) {
- bprm->mm->user_ns = get_user_ns(user_ns);
+ bprm->user_ns = get_user_ns(user_ns);
put_user_ns(old);
}
}
@@ -1328,6 +1347,12 @@ void setup_new_exec(struct linux_binprm * bprm)
me->mm->task_size = TASK_SIZE;
up_write(&me->signal->exec_update_lock);
mutex_unlock(&me->signal->cred_guard_mutex);
+
+ /* The exec locks are dropped: release the old address space now. */
+ if (bprm->old_mm) {
+ exec_mm_put_old(bprm->old_mm);
+ bprm->old_mm = NULL;
+ }
}
EXPORT_SYMBOL(setup_new_exec);
@@ -1375,6 +1400,8 @@ static void free_bprm(struct linux_binprm *bprm)
acct_arg_size(bprm, 0);
mmput(bprm->mm);
}
+ if (bprm->user_ns)
+ put_user_ns(bprm->user_ns);
free_arg_pages(bprm);
if (bprm->cred) {
/* in case exec fails before de_thread() succeeds */
@@ -1382,6 +1409,9 @@ static void free_bprm(struct linux_binprm *bprm)
mutex_unlock(&current->signal->cred_guard_mutex);
abort_creds(bprm->cred);
}
+ /* exec swapped the mm but failed before setup_new_exec() freed it */
+ if (bprm->old_mm)
+ exec_mm_put_old(bprm->old_mm);
do_close_execat(bprm->file);
if (bprm->executable)
fput(bprm->executable);
@@ -1905,17 +1935,6 @@ void set_binfmt(struct linux_binfmt *new)
}
EXPORT_SYMBOL(set_binfmt);
-/*
- * set_dumpable stores three-value SUID_DUMP_* into mm->flags.
- */
-void set_dumpable(struct mm_struct *mm, int value)
-{
- if (WARN_ON((unsigned)value > SUID_DUMP_ROOT))
- return;
-
- __mm_flags_set_mask_dumpable(mm, value);
-}
-
static inline struct user_arg_ptr native_arg(const char __user *const __user *p)
{
return (struct user_arg_ptr){.ptr.native = p};

fs/pidfs.c

View file
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 1cce4f3..b2ff950 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -338,14 +338,14 @@ static inline bool pid_in_current_pidns(const struct pid *pid)
return false;
}
-static __u32 pidfs_coredump_mask(unsigned long mm_flags)
+static __u32 pidfs_coredump_mask(enum task_dumpable dumpable)
{
- switch (__get_dumpable(mm_flags)) {
- case SUID_DUMP_USER:
+ switch (dumpable) {
+ case TASK_DUMPABLE_OWNER:
return PIDFD_COREDUMP_USER;
- case SUID_DUMP_ROOT:
+ case TASK_DUMPABLE_ROOT:
return PIDFD_COREDUMP_ROOT;
- case SUID_DUMP_DISABLE:
+ case TASK_DUMPABLE_OFF:
return PIDFD_COREDUMP_SKIP;
default:
WARN_ON_ONCE(true);
@@ -433,14 +433,9 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
return -ESRCH;
if ((mask & PIDFD_INFO_COREDUMP) && !kinfo.coredump_mask) {
- guard(task_lock)(task);
- if (task->mm) {
- unsigned long flags = __mm_flags_get_dumpable(task->mm);
-
- kinfo.coredump_mask = pidfs_coredump_mask(flags);
- kinfo.mask |= PIDFD_INFO_COREDUMP;
- /* No coredump actually took place, so no coredump signal. */
- }
+ kinfo.coredump_mask = pidfs_coredump_mask(task_exec_state_get_dumpable(task));
+ kinfo.mask |= PIDFD_INFO_COREDUMP;
+ /* No coredump actually took place, so no coredump signal. */
}
/* Unconditionally return identifiers and credentials, the rest only on request */
@@ -779,7 +774,7 @@ void pidfs_coredump(const struct coredump_params *cprm)
VFS_WARN_ON_ONCE(attr == PIDFS_PID_DEAD);
/* Note how we were coredumped and that we coredumped. */
- attr->coredump_mask = pidfs_coredump_mask(cprm->mm_flags) |
+ attr->coredump_mask = pidfs_coredump_mask(cprm->dumpable) |
PIDFD_COREDUMPED;
/* If coredumping is set to skip we should never end up here. */
VFS_WARN_ON_ONCE(attr->coredump_mask & PIDFD_COREDUMP_SKIP);

fs/proc/base.c

View file
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d9acfa8..65f5613 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -91,6 +91,7 @@
#include <linux/sched/mm.h>
#include <linux/sched/coredump.h>
#include <linux/sched/debug.h>
+#include <linux/sched/exec_state.h>
#include <linux/sched/stat.h>
#include <linux/posix-timers.h>
#include <linux/time_namespace.h>
@@ -1893,7 +1894,6 @@ void task_dump_owner(struct task_struct *task, umode_t mode,
cred = __task_cred(task);
uid = cred->euid;
gid = cred->egid;
- rcu_read_unlock();
/*
* Before the /proc/pid/status file was created the only way to read
@@ -1903,29 +1903,22 @@ void task_dump_owner(struct task_struct *task, umode_t mode,
* made this apply to all per process world readable and executable
* directories.
*/
- if (mode != (S_IFDIR|S_IRUGO|S_IXUGO)) {
- struct mm_struct *mm;
- task_lock(task);
- mm = task->mm;
- /* Make non-dumpable tasks owned by some root */
- if (mm) {
- if (get_dumpable(mm) != SUID_DUMP_USER) {
- struct user_namespace *user_ns = mm->user_ns;
-
- uid = make_kuid(user_ns, 0);
- if (!uid_valid(uid))
- uid = GLOBAL_ROOT_UID;
-
- gid = make_kgid(user_ns, 0);
- if (!gid_valid(gid))
- gid = GLOBAL_ROOT_GID;
- }
- } else {
- uid = GLOBAL_ROOT_UID;
- gid = GLOBAL_ROOT_GID;
+ if (mode != (S_IFDIR | S_IRUGO | S_IXUGO)) {
+ struct task_exec_state *exec_state;
+
+ exec_state = task_exec_state_rcu(task);
+ if (READ_ONCE(exec_state->dumpable) != TASK_DUMPABLE_OWNER) {
+ uid = make_kuid(exec_state->user_ns, 0);
+ if (!uid_valid(uid))
+ uid = GLOBAL_ROOT_UID;
+
+ gid = make_kgid(exec_state->user_ns, 0);
+ if (!gid_valid(gid))
+ gid = GLOBAL_ROOT_GID;
}
- task_unlock(task);
}
+ rcu_read_unlock();
+
*ruid = uid;
*rgid = gid;
}
@@ -2965,7 +2958,7 @@ static ssize_t proc_coredump_filter_read(struct file *file, char __user *buf,
ret = 0;
mm = get_task_mm(task);
if (mm) {
- unsigned long flags = __mm_flags_get_dumpable(mm);
+ unsigned long flags = __mm_flags_get_word(mm);
len = snprintf(buffer, sizeof(buffer), "%08lx\n",
((flags & MMF_DUMP_FILTER_MASK) >>

include/linux/binfmts.h

View file
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 65abd5a..2c77e38 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -25,6 +25,9 @@ struct linux_binprm {
struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm;
+ struct mm_struct *old_mm; /* replaced address space, freed by setup_new_exec() */
+ /* user_ns published to task->exec_state at execve, narrowed by would_dump(). */
+ struct user_namespace *user_ns;
unsigned long p; /* current top of mem */
unsigned int
/* Should an execfd be passed to userspace? */

include/linux/coredump.h

View file
diff --git a/include/linux/coredump.h b/include/linux/coredump.h
index 68861da..7b38ee2 100644
--- a/include/linux/coredump.h
+++ b/include/linux/coredump.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/fs.h>
+#include <linux/sched/coredump.h>
#include <asm/siginfo.h>
#ifdef CONFIG_COREDUMP
@@ -20,7 +21,10 @@ struct coredump_params {
const kernel_siginfo_t *siginfo;
struct file *file;
unsigned long limit;
+ /* MMF_DUMP_FILTER_* bits, snapshot of mm->flags at dump start. */
unsigned long mm_flags;
+ /* Snapshot of dumpable at dump start. */
+ enum task_dumpable dumpable;
int cpu;
loff_t written;
loff_t pos;

include/linux/mm_types.h

View file
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index a308e2c..9588ce3 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -1342,7 +1342,6 @@ struct mm_struct {
*/
struct task_struct __rcu *owner;
#endif
- struct user_namespace *user_ns;
/* store ref to file /proc/<pid>/exe symlink points to */
struct file __rcu *exe_file;
@@ -1907,11 +1906,11 @@ enum {
/* mm flags */
/*
- * The first two bits represent core dump modes for set-user-ID,
- * the modes are SUID_DUMP_* defined in linux/sched/coredump.h
+ * Bits 0 and 1 were dumpability; that moved to task->exec_state. Reserve
+ * the bits so MMF_DUMP_FILTER_* positions stay stable for the
+ * /proc/<pid>/coredump_filter ABI.
*/
#define MMF_DUMPABLE_BITS 2
-#define MMF_DUMPABLE_MASK (BIT(MMF_DUMPABLE_BITS) - 1)
/* coredump filter bits */
#define MMF_DUMP_ANON_PRIVATE 2
#define MMF_DUMP_ANON_SHARED 3
@@ -1972,7 +1971,7 @@ enum {
#define MMF_TOPDOWN 31 /* mm searches top down by default */
#define MMF_TOPDOWN_MASK BIT(MMF_TOPDOWN)
-#define MMF_INIT_LEGACY_MASK (MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\
+#define MMF_INIT_LEGACY_MASK (MMF_DUMP_FILTER_MASK |\
MMF_DISABLE_THP_MASK | MMF_HAS_MDWE_MASK |\
MMF_VM_MERGE_ANY_MASK | MMF_TOPDOWN_MASK)

include/linux/ptrace.h

View file
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 90507d4..ef314f7 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -17,6 +17,7 @@ struct syscall_info {
struct seccomp_data data;
};
+bool ptracer_access_allowed(struct task_struct *tsk);
extern int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
void *buf, int len, unsigned int gup_flags);

include/linux/sched.h

View file
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ee06cba..258cb07 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -85,6 +85,7 @@ struct seq_file;
struct sighand_struct;
struct signal_struct;
struct task_delay_info;
+struct task_exec_state;
struct task_group;
struct task_struct;
struct timespec64;
@@ -962,6 +963,8 @@ struct task_struct {
struct mm_struct *mm;
struct mm_struct *active_mm;
+ struct task_exec_state __rcu *exec_state;
+
int exit_state;
int exit_code;
int exit_signal;
@@ -1002,9 +1005,6 @@ struct task_struct {
unsigned sched_rt_mutex:1;
#endif
- /* Save user-dumpable when mm goes away */
- unsigned user_dumpable:1;
-
/* Bit to tell TOMOYO we're in execve(): */
unsigned in_execve:1;
unsigned in_iowait:1;

include/linux/sched/coredump.h

View file
diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index 624fda1..20957cc 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -2,43 +2,18 @@
#ifndef _LINUX_SCHED_COREDUMP_H
#define _LINUX_SCHED_COREDUMP_H
-#include <linux/mm_types.h>
-
-#define SUID_DUMP_DISABLE 0 /* No setuid dumping */
-#define SUID_DUMP_USER 1 /* Dump as user of process */
-#define SUID_DUMP_ROOT 2 /* Dump as root */
-
-static inline unsigned long __mm_flags_get_dumpable(const struct mm_struct *mm)
-{
- /*
- * By convention, dumpable bits are contained in first 32 bits of the
- * bitmap, so we can simply access this first unsigned long directly.
- */
- return __mm_flags_get_word(mm);
-}
-
-static inline void __mm_flags_set_mask_dumpable(struct mm_struct *mm, int value)
-{
- __mm_flags_set_mask_bits_word(mm, MMF_DUMPABLE_MASK, value);
-}
-
-extern void set_dumpable(struct mm_struct *mm, int value);
/*
- * This returns the actual value of the suid_dumpable flag. For things
- * that are using this for checking for privilege transitions, it must
- * test against SUID_DUMP_USER rather than treating it as a boolean
- * value.
+ * Task dumpability mode. Gates core dump production and ptrace_attach()
+ * authorization. The numeric values are stable ABI (suid_dumpable
+ * sysctl, prctl(PR_SET_DUMPABLE)); do not renumber.
*/
-static inline int __get_dumpable(unsigned long mm_flags)
-{
- return mm_flags & MMF_DUMPABLE_MASK;
-}
-
-static inline int get_dumpable(struct mm_struct *mm)
-{
- unsigned long flags = __mm_flags_get_dumpable(mm);
-
- return __get_dumpable(flags);
-}
+enum task_dumpable {
+ TASK_DUMPABLE_OFF = 0, /* no dump; ptrace needs CAP_SYS_PTRACE */
+ TASK_DUMPABLE_OWNER = 1, /* default; dump and ptrace by uid match */
+ TASK_DUMPABLE_ROOT = 2, /* dump as root; ptrace needs CAP_SYS_PTRACE */
+};
+
+void task_exec_state_set_dumpable(enum task_dumpable value);
+enum task_dumpable task_exec_state_get_dumpable(struct task_struct *task);
#endif /* _LINUX_SCHED_COREDUMP_H */

include/linux/sched/exec_state.h

View file
diff --git a/include/linux/sched/exec_state.h b/include/linux/sched/exec_state.h
new file mode 100644
index 0000000..9b61782
--- /dev/null
+++ b/include/linux/sched/exec_state.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Christian Brauner <[email protected]> */
+#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>
+

This diff is incomplete. Download the complete patch.

init/init_task.c

View file

This diff is incomplete. Download the complete patch.

kernel/Makefile

View file

This diff is incomplete. Download the complete patch.

kernel/cred.c

View file

This diff is incomplete. Download the complete patch.

kernel/exec_state.c

View file

This diff is incomplete. Download the complete patch.

kernel/exit.c

View file

This diff is incomplete. Download the complete patch.

kernel/fork.c

View file

This diff is incomplete. Download the complete patch.

kernel/kthread.c

View file

This diff is incomplete. Download the complete patch.

kernel/ptrace.c

View file

This diff is incomplete. Download the complete patch.

kernel/sys.c

View file

This diff is incomplete. Download the complete patch.

mm/init-mm.c

View file

This diff is incomplete. Download the complete patch.