From 3261b114d41c30a9ade245ccdabbb5c8c886394c Mon Sep 17 00:00:00 2001 From: Andrew Lukoshko Date: Fri, 15 May 2026 09:04:36 +0000 Subject: [PATCH] ptrace: replace get_dumpable() fix with kABI-safe variant The previous commit shipped upstream 31e62c2ebbfd as-is, which adds an 'unsigned user_dumpable:1' bit to task_struct. That layout change breaks kABI: the symtype signature of struct task_struct is referenced by hundreds of stablelist exports. Replace with a minimal kABI-safe slice that only touches kernel/ptrace.c: when task->mm == NULL, require CAP_SYS_PTRACE in init_user_ns unconditionally. This closes the Qualys advisory hole without modifying task_struct or exit_mm(). The behavioural delta vs upstream is that an already-exited user task whose mm has been cleared now also requires CAP_SYS_PTRACE. --- 1103-ptrace-require-cap-on-mm-less-task.patch | 55 +++++++++ 1103-ptrace-saner-get_dumpable-logic.patch | 109 ------------------ kernel.spec | 4 +- 3 files changed, 57 insertions(+), 111 deletions(-) create mode 100644 1103-ptrace-require-cap-on-mm-less-task.patch delete mode 100644 1103-ptrace-saner-get_dumpable-logic.patch diff --git a/1103-ptrace-require-cap-on-mm-less-task.patch b/1103-ptrace-require-cap-on-mm-less-task.patch new file mode 100644 index 000000000..0c23998c0 --- /dev/null +++ b/1103-ptrace-require-cap-on-mm-less-task.patch @@ -0,0 +1,55 @@ +From: Andrew Lukoshko +Subject: [PATCH AlmaLinux 10s] ptrace: require CAP_SYS_PTRACE when task has no mm + +kABI-safe AlmaLinux backport of upstream commit 31e62c2ebbfd +("ptrace: slightly saner 'get_dumpable()' logic") posted at +https://github.com/torvalds/linux/commit/31e62c2ebbfdc3fe3dbdf5e02c92a9dc67087a3a + +The upstream fix adds a 'user_dumpable:1' bit to task_struct and +caches the last dumpability in exit_mm() so __ptrace_may_access() +can require CAP_SYS_PTRACE when the target has no mm (e.g. kernel +threads or already-exited user tasks). That layout change to +task_struct breaks kABI on AlmaLinux 10s (the symtype +signature of struct task_struct is referenced by hundreds of +stablelist exports), so we cannot import the field/exit_mm hunks +as-is. + +Take the minimal kABI-safe slice instead: when task->mm == NULL, +require CAP_SYS_PTRACE in init_user_ns unconditionally. This closes +the Qualys Security Advisory hole -- mm-less targets no longer pass +the dumpability check by default -- without touching task_struct or +exit.c. The only behavioural delta versus upstream is that a user +task that has already cleared its mm in exit_mm() (a dying/zombie +task) now also requires CAP_SYS_PTRACE to attach, instead of being +remembered as previously dumpable. Such targets are rarely ptraced +in practice. + +Verified to apply with `patch -p1 -F0` (no offset, no fuzz, no rejects) +against kernel-6.12.0-227.el10. + +Reported-by: Qualys Security Advisory +Signed-off-by: Andrew Lukoshko +--- + kernel/ptrace.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/kernel/ptrace.c ++++ b/kernel/ptrace.c +@@ -339,8 +339,11 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) + smp_rmb(); + mm = task->mm; +- if (mm && +- ((get_dumpable(mm) != SUID_DUMP_USER) && +- !ptrace_has_cap(mm->user_ns, mode))) +- return -EPERM; ++ if (mm) { ++ if ((get_dumpable(mm) != SUID_DUMP_USER) && ++ !ptrace_has_cap(mm->user_ns, mode)) ++ return -EPERM; ++ } else if (!ptrace_has_cap(&init_user_ns, mode)) { ++ return -EPERM; ++ } + + return security_ptrace_access_check(task, mode); +-- +2.43.0 diff --git a/1103-ptrace-saner-get_dumpable-logic.patch b/1103-ptrace-saner-get_dumpable-logic.patch deleted file mode 100644 index f9e55362d..000000000 --- a/1103-ptrace-saner-get_dumpable-logic.patch +++ /dev/null @@ -1,109 +0,0 @@ -From: Linus Torvalds -Subject: [PATCH AlmaLinux 10s] ptrace: slightly saner 'get_dumpable()' logic - -Backport of upstream commit 31e62c2ebbfd ("ptrace: slightly saner -'get_dumpable()' logic") posted at -https://github.com/torvalds/linux/commit/31e62c2ebbfdc3fe3dbdf5e02c92a9dc67087a3a - -The 'dumpability' of a task is fundamentally about the memory image of -the task -- the concept comes from whether it can core dump or not -- -and makes no sense when you don't have an associated mm. - -And almost all users do in fact use it only for the case where the -task has a mm pointer. - -But we have one odd special case: ptrace_may_access() uses 'dumpable' -to check various other things entirely independently of the MM -(typically explicitly using flags like PTRACE_MODE_READ_FSCREDS). -Including for threads that no longer have a VM (and maybe never did, -like most kernel threads). - -The ptrace code does check that the uid/gid matches, so you do have -to be uid-0 to see kernel thread details, but this means that the -traditional "drop capabilities" model doesn't make any difference -for this all. - -Make it all make a bit more sense by saying that if you don't have a -MM pointer, we use a cached "last dumpability" flag if the thread -ever had a MM (it will be zero for kernel threads since it is never -set), and require a proper CAP_SYS_PTRACE capability to override. - -Verified to apply with `patch -p1 -F0` (no offset, no fuzz, no rejects) -against kernel-6.12.0-227.el10. - -Reported-by: Qualys Security Advisory -Cc: Oleg Nesterov -Cc: Kees Cook -Signed-off-by: Linus Torvalds ---- - include/linux/sched.h | 3 +++ - kernel/exit.c | 1 + - kernel/ptrace.c | 22 ++++++++++++++++------ - 3 files changed, 20 insertions(+), 6 deletions(-) - ---- a/include/linux/sched.h -+++ b/include/linux/sched.h -@@ -1005,6 +1005,9 @@ 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; ---- a/kernel/exit.c -+++ b/kernel/exit.c -@@ -563,6 +563,7 @@ static void exit_mm(void) - */ - smp_mb__after_spinlock(); - local_irq_disable(); -+ current->user_dumpable = (get_dumpable(mm) == SUID_DUMP_USER); - current->mm = NULL; - membarrier_update_current_mm(NULL); - enter_lazy_tlb(mm, current); ---- a/kernel/ptrace.c -+++ b/kernel/ptrace.c -@@ -272,11 +272,24 @@ static bool ptrace_has_cap(struct user_namespace *ns, unsigned int mode) - return ns_capable(ns, CAP_SYS_PTRACE); - } - -+static bool task_still_dumpable(struct task_struct *task, unsigned int mode) -+{ -+ struct mm_struct *mm = task->mm; -+ if (mm) { -+ if (get_dumpable(mm) == SUID_DUMP_USER) -+ return true; -+ return ptrace_has_cap(mm->user_ns, mode); -+ } -+ -+ if (task->user_dumpable) -+ return true; -+ return ptrace_has_cap(&init_user_ns, mode); -+} -+ - /* Returns 0 on success, -errno on denial. */ - static int __ptrace_may_access(struct task_struct *task, unsigned int mode) - { - const struct cred *cred = current_cred(), *tcred; -- struct mm_struct *mm; - kuid_t caller_uid; - kgid_t caller_gid; - -@@ -337,11 +350,8 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) - * Pairs with a write barrier in commit_creds(). - */ - smp_rmb(); -- mm = task->mm; -- if (mm && -- ((get_dumpable(mm) != SUID_DUMP_USER) && -- !ptrace_has_cap(mm->user_ns, mode))) -- return -EPERM; -+ if (!task_still_dumpable(task, mode)) -+ return -EPERM; - - return security_ptrace_access_check(task, mode); - } --- -2.43.0 diff --git a/kernel.spec b/kernel.spec index e82ad1764..30381d83a 100644 --- a/kernel.spec +++ b/kernel.spec @@ -1146,7 +1146,7 @@ Patch2010: 0001-Keep-fs-btrfs-files-in-modules-package.patch Patch1100: 1100-xfrm-esp-avoid-in-place-decrypt-shared-skb-frags.patch Patch1101: 1101-rxrpc-linearize-paged-frags.patch Patch1102: 1102-net-skbuff-propagate-shared-frag-marker.patch -Patch1103: 1103-ptrace-saner-get_dumpable-logic.patch +Patch1103: 1103-ptrace-require-cap-on-mm-less-task.patch # END OF PATCH DEFINITIONS @@ -2045,7 +2045,7 @@ ApplyPatch 0001-Keep-fs-btrfs-files-in-modules-package.patch ApplyPatch 1100-xfrm-esp-avoid-in-place-decrypt-shared-skb-frags.patch ApplyPatch 1101-rxrpc-linearize-paged-frags.patch ApplyPatch 1102-net-skbuff-propagate-shared-frag-marker.patch -ApplyPatch 1103-ptrace-saner-get_dumpable-logic.patch +ApplyPatch 1103-ptrace-require-cap-on-mm-less-task.patch %{log_msg "End of patch applications"} # END OF PATCH APPLICATIONS