exec_state: relocate dumpable information

The dumpable flag captured at execve() is consulted by
__ptrace_may_access() and several /proc owner / visibility checks.
It lives on mm_struct today, which exit_mm() clears from the task
long before the task itself is reaped.

exec_state is anchored to the execve() that established the current
privilege domain.  CLONE_VM siblings refcount-share the parent's
exec_state via copy_exec_state(); non-CLONE_VM clones allocate a
fresh exec_state inheriting the parent's dumpable mode and user_ns
reference via task_exec_state_copy().  execve() allocates a fresh
instance (via alloc_task_exec_state() in begin_new_exec()) and
installs it under task_lock + exec_update_lock with
task_exec_state_replace().  init_task uses a static instance.

The dumpable mode now lives on task->exec_state->dumpable.
task->mm->flags no longer carries dumpability; MMF_DUMPABLE_MASK is
removed, but MMF_DUMPABLE_BITS is reserved so MMF_DUMP_FILTER_* bit
positions remain stable for the /proc/<pid>/coredump_filter ABI. The
task->user_dumpable cache bit and its assignment in exit_mm() are
removed; readers go through get_dumpable(task) directly.

coredump_params gains a snapshot field cprm.dumpable, populated from
get_dumpable(current) at vfs_coredump() entry, replacing the previous
__get_dumpable(cprm->mm_flags) consumers in fs/coredump.c and
fs/pidfs.c.

The user namespace recorded at execve() is consulted by
__ptrace_may_access() and by /proc/PID/* owner derivation. Move the
captured user_ns onto task_exec_state, which stays attached to the task
past exit_mm() and across exit_files().

bprm grows a user_ns field staged in bprm_mm_init() with the caller's
user_ns, narrowed by would_dump() to the closest privileged ancestor,
and consumed by exec_mmap() via alloc_task_exec_state(bprm->user_ns).
free_bprm() releases the staging reference.

mm_struct loses ->user_ns entirely.  Initializers in init-mm, efi_mm,
and the implicit one in mm_init()/dup_mm()/mm_alloc() are removed;
__mmdrop() drops the matching put_user_ns(). The kthread_use_mm()
WARN_ON_ONCE(!mm->user_ns) is no longer meaningful and goes too.

Reviewed-by: Jann Horn <jannh@google.com>
Link: https://patch.msgid.link/20260520-work-task_exec_state-v3-4-69f895bc1385@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner (Amutable)
2026-05-26 11:02:01 +02:00
committed by Christian Brauner
parent 92f829f6b2
commit 6b1c66c9cc
21 changed files with 116 additions and 143 deletions
+2 -4
View File
@@ -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) != TASK_DUMPABLE_OWNER) &&
!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;
}
-1
View File
@@ -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
+7 -13
View File
@@ -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) == TASK_DUMPABLE_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(),
};
+20 -19
View File
@@ -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];
@@ -834,12 +838,17 @@ EXPORT_SYMBOL(read_code);
* On success, this function returns with exec_update_lock
* held for writing.
*/
static int exec_mmap(struct mm_struct *mm)
static int exec_mmap(struct mm_struct *mm, struct user_namespace *user_ns)
{
struct task_exec_state *exec_state __free(put_task_exec_state) = NULL;
struct task_struct *tsk;
struct mm_struct *old_mm, *active_mm;
int ret;
exec_state = alloc_task_exec_state(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 +879,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
@@ -1145,7 +1155,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->mm, bprm->user_ns);
if (retval)
goto out;
@@ -1210,9 +1220,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, TASK_DUMPABLE_OWNER);
task_exec_state_set_dumpable(TASK_DUMPABLE_OWNER);
perf_event_exec();
@@ -1261,7 +1271,7 @@ int begin_new_exec(struct linux_binprm * bprm)
* wait until new credentials are committed
* by commit_creds() above
*/
if (get_dumpable(me->mm) != TASK_DUMPABLE_OWNER)
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 +1308,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);
}
}
@@ -1375,6 +1385,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 */
@@ -1905,17 +1917,6 @@ void set_binfmt(struct linux_binfmt *new)
}
EXPORT_SYMBOL(set_binfmt);
/*
* set_dumpable stores three-value TASK_DUMPABLE_* into mm->flags.
*/
void set_dumpable(struct mm_struct *mm, int value)
{
if (WARN_ON((unsigned)value > TASK_DUMPABLE_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};
+6 -11
View File
@@ -338,9 +338,9 @@ 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)) {
switch (dumpable) {
case TASK_DUMPABLE_OWNER:
return PIDFD_COREDUMP_USER;
case TASK_DUMPABLE_ROOT:
@@ -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);
+14 -21
View File
@@ -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) != TASK_DUMPABLE_OWNER) {
struct user_namespace *user_ns = mm->user_ns;
if (mode != (S_IFDIR | S_IRUGO | S_IXUGO)) {
struct task_exec_state *exec_state;
uid = make_kuid(user_ns, 0);
if (!uid_valid(uid))
uid = GLOBAL_ROOT_UID;
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(user_ns, 0);
if (!gid_valid(gid))
gid = GLOBAL_ROOT_GID;
}
} else {
uid = GLOBAL_ROOT_UID;
gid = GLOBAL_ROOT_GID;
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) >>
+2
View File
@@ -25,6 +25,8 @@ struct linux_binprm {
struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm;
/* 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? */
+4
View File
@@ -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;
+4 -5
View File
@@ -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 TASK_DUMPABLE_* 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)
+1 -3
View File
@@ -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;
@@ -1004,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;
+2 -34
View File
@@ -2,8 +2,6 @@
#ifndef _LINUX_SCHED_COREDUMP_H
#define _LINUX_SCHED_COREDUMP_H
#include <linux/mm_types.h>
/*
* Task dumpability mode. Gates core dump production and ptrace_attach()
* authorization. The numeric values are stable ABI (suid_dumpable
@@ -15,37 +13,7 @@ enum task_dumpable {
TASK_DUMPABLE_ROOT = 2, /* dump as root; ptrace needs CAP_SYS_PTRACE */
};
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 TASK_DUMPABLE_OWNER rather than treating it as a boolean
* value.
*/
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);
}
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 */
+2 -2
View File
@@ -16,13 +16,13 @@ struct task_exec_state {
struct rcu_head rcu;
};
extern struct task_exec_state init_task_exec_state;
struct task_exec_state *alloc_task_exec_state(struct user_namespace *user_ns);
void put_task_exec_state(struct task_exec_state *exec_state);
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);
int task_exec_state_copy(struct task_struct *tsk);
void __init exec_state_init(void);
+10
View File
@@ -7,6 +7,8 @@
#include <linux/sched/rt.h>
#include <linux/sched/task.h>
#include <linux/sched/ext.h>
#include <linux/sched/exec_state.h>
#include <linux/user_namespace.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
@@ -56,6 +58,13 @@ static struct sighand_struct init_sighand = {
.signalfd_wqh = __WAIT_QUEUE_HEAD_INITIALIZER(init_sighand.signalfd_wqh),
};
/* init to 2 - one for init_task, one to ensure it is never freed */
struct task_exec_state init_task_exec_state = {
.count = REFCOUNT_INIT(2),
.dumpable = TASK_DUMPABLE_OWNER,
.user_ns = &init_user_ns,
};
#ifdef CONFIG_SHADOW_CALL_STACK
unsigned long init_shadow_call_stack[SCS_SIZE / sizeof(long)] = {
[(SCS_SIZE / sizeof(long)) - 1] = SCS_END_MAGIC
@@ -113,6 +122,7 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
.nr_cpus_allowed= NR_CPUS,
.mm = NULL,
.active_mm = &init_mm,
.exec_state = &init_task_exec_state,
.restart_block = {
.fn = do_no_restart_syscall,
},
+2 -1
View File
@@ -384,8 +384,9 @@ int commit_creds(struct cred *new)
!uid_eq(old->fsuid, new->fsuid) ||
!gid_eq(old->fsgid, new->fsgid) ||
!cred_cap_issubset(old, new)) {
/* mm-less tasks share init_task's exec_state */
if (task->mm)
set_dumpable(task->mm, suid_dumpable);
task_exec_state_set_dumpable(suid_dumpable);
task->pdeath_signal = 0;
/*
* If a task drops privileges and becomes nondumpable,
+3
View File
@@ -95,6 +95,9 @@ void task_exec_state_set_dumpable(enum task_dumpable value)
value = TASK_DUMPABLE_OFF;
exec_state = rcu_dereference_protected(current->exec_state, true);
/* mm-less tasks share init_task's exec_state; never mutate it */
if (WARN_ON_ONCE(exec_state == &init_task_exec_state))
return;
WRITE_ONCE(exec_state->dumpable, value);
}
-1
View File
@@ -571,7 +571,6 @@ static void exit_mm(void)
*/
smp_mb__after_spinlock();
local_irq_disable();
current->user_dumpable = (get_dumpable(mm) == TASK_DUMPABLE_OWNER);
current->mm = NULL;
membarrier_update_current_mm(NULL);
enter_lazy_tlb(mm, current);
+27 -6
View File
@@ -23,6 +23,7 @@
#include <linux/sched/task_stack.h>
#include <linux/sched/cputime.h>
#include <linux/sched/ext.h>
#include <linux/sched/exec_state.h>
#include <linux/seq_file.h>
#include <linux/rtmutex.h>
#include <linux/init.h>
@@ -555,6 +556,7 @@ void free_task(struct task_struct *tsk)
if (tsk->flags & PF_KTHREAD)
free_kthread_struct(tsk);
bpf_task_storage_free(tsk);
put_task_exec_state(rcu_access_pointer(tsk->exec_state));
free_task_struct(tsk);
}
EXPORT_SYMBOL(free_task);
@@ -731,7 +733,6 @@ void __mmdrop(struct mm_struct *mm)
destroy_context(mm);
mmu_notifier_subscriptions_destroy(mm);
check_mm(mm);
put_user_ns(mm->user_ns);
mm_pasid_drop(mm);
mm_destroy_cid(mm);
percpu_counter_destroy_many(mm->rss_stat, NR_MM_COUNTERS);
@@ -946,6 +947,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
tsk->seccomp.filter = NULL;
#endif
RCU_INIT_POINTER(tsk->exec_state, NULL);
setup_thread_stack(tsk, orig);
clear_user_return_notifier(tsk);
clear_tsk_need_resched(tsk);
@@ -1072,8 +1075,7 @@ static void mmap_init_lock(struct mm_struct *mm)
#endif
}
static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
struct user_namespace *user_ns)
static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
{
mt_init_flags(&mm->mm_mt, MM_MT_FLAGS);
mt_set_external_lock(&mm->mm_mt, &mm->mmap_lock);
@@ -1132,7 +1134,6 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
NR_MM_COUNTERS))
goto fail_pcpu;
mm->user_ns = get_user_ns(user_ns);
lru_gen_init_mm(mm);
return mm;
@@ -1163,7 +1164,7 @@ struct mm_struct *mm_alloc(void)
return NULL;
memset(mm, 0, sizeof(*mm));
return mm_init(mm, current, current_user_ns());
return mm_init(mm, current);
}
EXPORT_SYMBOL_IF_KUNIT(mm_alloc);
@@ -1527,7 +1528,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk,
memcpy(mm, oldmm, sizeof(*mm));
if (!mm_init(mm, tsk, mm->user_ns))
if (!mm_init(mm, tsk))
goto fail_nomem;
uprobe_start_dup_mmap();
@@ -1593,6 +1594,22 @@ static int copy_mm(u64 clone_flags, struct task_struct *tsk)
return 0;
}
static int copy_exec_state(u64 clone_flags, struct task_struct *tsk)
{
struct task_exec_state *exec_state;
/* CLONE_VM siblings refcount-share the parent's exec_state. */
if (clone_flags & CLONE_VM) {
exec_state = rcu_dereference_protected(current->exec_state, true);
refcount_inc(&exec_state->count);
rcu_assign_pointer(tsk->exec_state, exec_state);
return 0;
}
/* Everyone else inherits a fresh copy. */
return task_exec_state_copy(tsk);
}
static int copy_fs(u64 clone_flags, struct task_struct *tsk)
{
struct fs_struct *fs = current->fs;
@@ -2090,6 +2107,9 @@ __latent_entropy struct task_struct *copy_process(
p = dup_task_struct(current, node);
if (!p)
goto fork_out;
retval = copy_exec_state(clone_flags, p);
if (retval)
goto bad_fork_free;
p->flags &= ~PF_KTHREAD;
if (args->kthread)
p->flags |= PF_KTHREAD;
@@ -3098,6 +3118,7 @@ void __init proc_caches_init(void)
sizeof(struct signal_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
NULL);
exec_state_init();
files_cachep = kmem_cache_create("files_cache",
sizeof(struct files_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT,
-1
View File
@@ -1619,7 +1619,6 @@ void kthread_use_mm(struct mm_struct *mm)
WARN_ON_ONCE(!(tsk->flags & PF_KTHREAD));
WARN_ON_ONCE(tsk->mm);
WARN_ON_ONCE(!mm->user_ns);
/*
* It is possible for mm to be the same as tsk->active_mm, but
+8 -18
View File
@@ -70,21 +70,14 @@ int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
void *buf, int len, unsigned int gup_flags)
{
struct mm_struct *mm;
int ret;
int ret = 0;
mm = get_task_mm(tsk);
if (!mm)
return 0;
if (!tsk->ptrace ||
(current != tsk->parent) ||
((get_dumpable(mm) != TASK_DUMPABLE_OWNER) &&
!ptracer_capable(tsk, mm->user_ns))) {
mmput(mm);
return 0;
}
ret = access_remote_vm(mm, addr, buf, len, gup_flags);
if (ptracer_access_allowed(tsk))
ret = access_remote_vm(mm, addr, buf, len, gup_flags);
mmput(mm);
return ret;
@@ -299,16 +292,13 @@ static bool ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
static bool task_still_dumpable(struct task_struct *task, unsigned int mode)
{
struct mm_struct *mm = task->mm;
if (mm) {
if (get_dumpable(mm) == TASK_DUMPABLE_OWNER)
return true;
return ptrace_has_cap(mm->user_ns, mode);
}
const struct task_exec_state *exec_state;
if (task->user_dumpable)
guard(rcu)();
exec_state = task_exec_state_rcu(task);
if (READ_ONCE(exec_state->dumpable) == TASK_DUMPABLE_OWNER)
return true;
return ptrace_has_cap(&init_user_ns, mode);
return ptrace_has_cap(exec_state->user_ns, mode);
}
/* Returns 0 on success, -errno on denial. */
+2 -2
View File
@@ -2565,14 +2565,14 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
error = put_user(me->pdeath_signal, (int __user *)arg2);
break;
case PR_GET_DUMPABLE:
error = get_dumpable(me->mm);
error = task_exec_state_get_dumpable(me);
break;
case PR_SET_DUMPABLE:
if (arg2 != TASK_DUMPABLE_OFF && arg2 != TASK_DUMPABLE_OWNER) {
error = -EINVAL;
break;
}
set_dumpable(me->mm, arg2);
task_exec_state_set_dumpable(arg2);
break;
case PR_SET_UNALIGN:
-1
View File
@@ -43,7 +43,6 @@ struct mm_struct init_mm = {
.vma_writer_wait = __RCUWAIT_INITIALIZER(init_mm.vma_writer_wait),
.mm_lock_seq = SEQCNT_ZERO(init_mm.mm_lock_seq),
#endif
.user_ns = &init_user_ns,
#ifdef CONFIG_SCHED_MM_CID
.mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(init_mm.mm_cid.lock),
#endif