commit_creds() has historically called set_dumpable(suid_dumpable) on every effective uid/gid/cap change, paired with an smp_wmb()/smp_rmb() fence against __ptrace_may_access() reading the credentials. Switch the call to task_exec_state_set_dumpable() so the dumpability lowering targets the new per-task exec_state rather than mm->flags. Drop the open-coded "if (task->mm)" guard - exec_state is always allocated for any observable task - and drop the explicit smp_wmb()/smp_rmb() pair: the new model relies on RCU acquire/release on the cred pointer. WRITE_ONCE() on es->dumpable inside task_exec_state_set_dumpable() happens-before rcu_assign_pointer() of the new cred in commit_creds(), so a reader that observes the new cred via rcu_dereference(task->real_cred) in __ptrace_may_access() is guaranteed to observe the new dumpable via READ_ONCE(es->dumpable). The same-uid ptrace shedding and /proc visibility behavior that long-running daemons launched as root (sshd, dbus-daemon, polkitd, NetworkManager, postfix workers, ...) rely on when they setresuid() to a service uid is preserved. No userspace audit cycle is required. Behavioral change: dumpability propagates across the fork subtree ================================================================= exec_state is refcount-shared across every clone() variant - thread, fork(), vfork(), io_uring worker - so this write is observed by every task still sharing the same exec_state. Pre-series, set_dumpable() targeted mm->flags, which was per-mm: shared by CLONE_VM threads but private to fork()-without-CLONE_VM children. Under the new model a privilege drop in any task in the subtree lowers dumpability for the entire subtree, including non-CLONE_VM siblings. This matches the model the series codifies: the entire fork subtree of one execve shares one exec_state, and dumpability is a property of that domain. Signed-off-by: Christian Brauner (Amutable) Signed-off-by: Christian Brauner --- kernel/cred.c | 25 ++++++++++++------------- kernel/ptrace.c | 10 ---------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/kernel/cred.c b/kernel/cred.c index 51c35ac94787..335d8da1c43b 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -378,25 +378,24 @@ int commit_creds(struct cred *new) get_cred(new); /* we will require a ref for the subj creds too */ - /* dumpability changes */ + /* + * Lower dumpability on euid/egid/fsuid/fsgid/capability changes. + * Long-running daemons launched as root (sshd, dbus-daemon, + * polkitd, NetworkManager, postfix workers, ...) rely on this to + * shed /proc visibility and same-uid ptrace exposure of + * root-acquired secrets when they setresuid() to a service uid. + * + * exec_state is shared across the whole fork subtree of the + * establishing execve(), so this write is observed by every task + * still sharing the same exec_state. + */ if (!uid_eq(old->euid, new->euid) || !gid_eq(old->egid, new->egid) || !uid_eq(old->fsuid, new->fsuid) || !gid_eq(old->fsgid, new->fsgid) || !cred_cap_issubset(old, new)) { - if (task->mm) - task_exec_state_set_dumpable(suid_dumpable); + task_exec_state_set_dumpable(suid_dumpable); task->pdeath_signal = 0; - /* - * If a task drops privileges and becomes nondumpable, - * the dumpability change must become visible before - * the credential change; otherwise, a __ptrace_may_access() - * racing with this change may be able to attach to a task it - * shouldn't be able to attach to (as if the task had dropped - * privileges without becoming nondumpable). - * Pairs with a read barrier in __ptrace_may_access(). - */ - smp_wmb(); } /* alter the thread keyring */ diff --git a/kernel/ptrace.c b/kernel/ptrace.c index a4932ef716c6..c340a741e76a 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -356,16 +356,6 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) return -EPERM; ok: rcu_read_unlock(); - /* - * If a task drops privileges and becomes nondumpable (through a syscall - * like setresuid()) while we are trying to access it, we must ensure - * that the dumpability is read after the credentials; otherwise, - * we may be able to attach to a task that we shouldn't be able to - * attach to (as if the task had dropped privileges without becoming - * nondumpable). - * Pairs with a write barrier in commit_creds(). - */ - smp_rmb(); if (!task_still_dumpable(task, mode)) return -EPERM; -- 2.47.3