lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87tsvoa7to.ffs@tglx>
Date: Tue, 10 Feb 2026 17:20:51 +0100
From: Thomas Gleixner <tglx@...nel.org>
To: Shinichiro Kawasaki <shinichiro.kawasaki@....com>, Linus Torvalds
 <torvalds@...ux-foundation.org>
Cc: LKML <linux-kernel@...r.kernel.org>, Ihor Solodrai
 <ihor.solodrai@...ux.dev>, Shrikanth Hegde <sshegde@...ux.ibm.com>, Peter
 Zijlstra <peterz@...radead.org>, Mathieu Desnoyers
 <mathieu.desnoyers@...icios.com>, Michael Jeanson <mjeanson@...icios.com>,
 Andrey Ryabinin <ryabinin.a.a@...il.com>, Alexander Potapenko
 <glider@...gle.com>, "kasan-dev@...glegroups.com"
 <kasan-dev@...glegroups.com>
Subject: [PATCH] sched/mmcid: Don't assume CID is CPU owned on mode switch

Shinichiro reported a KASAN UAF, which is actually an out of bounds access
in the MMCID management code.

   CPU0						CPU1
   						T1 runs in userspace
   T0: fork(T4) -> Switch to per CPU CID mode
         fixup() set MM_CID_TRANSIT on T1/CPU1
   T4 exit()
   T3 exit()
   T2 exit()
						T1 exit() switch to per task mode
						 ---> Out of bounds access.

As T1 has not scheduled after T0 set the TRANSIT bit, it exits with the
TRANSIT bit set. sched_mm_cid_remove_user() clears the TRANSIT bit in
the task and drops the CID, but it does not touch the per CPU storage.
That's functionally correct because a CID is only owned by the CPU when
the ONCPU bit is set, which is mutually exclusive with the TRANSIT flag.

Now sched_mm_cid_exit() assumes that the CID is CPU owned because the
prior mode was per CPU. It invokes mm_drop_cid_on_cpu() which clears the
not set ONCPU bit and then invokes clear_bit() with an insanely large
bit number because TRANSIT is set (bit 29).

Prevent that by actually validating that the CID is CPU owned in
mm_drop_cid_on_cpu().

Fixes: 007d84287c74 ("sched/mmcid: Drop per CPU CID immediately when switching to per task mode")
Reported-by: Shinichiro Kawasaki <shinichiro.kawasaki@....com>
Signed-off-by: Thomas Gleixner <tglx@...nel.org>
Tested-by: Shinichiro Kawasaki <shinichiro.kawasaki@....com>
Cc: stable@...r.kernel.org
Closes: https://lore.kernel.org/aYsZrixn9b6s_2zL@shinmob
---

Linus, can you please take that directly?

---
 kernel/sched/core.c  |    7 +++----
 kernel/sched/sched.h |    6 ++++--
 2 files changed, 7 insertions(+), 6 deletions(-)

--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -10729,10 +10729,9 @@ void sched_mm_cid_exit(struct task_struc
 					return;
 				/*
 				 * Mode change. The task has the CID unset
-				 * already. The CPU CID is still valid and
-				 * does not have MM_CID_TRANSIT set as the
-				 * mode change has just taken effect under
-				 * mm::mm_cid::lock. Drop it.
+				 * already and dealt with an eventually set
+				 * TRANSIT bit. If the CID is owned by the CPU
+				 * then drop it.
 				 */
 				mm_drop_cid_on_cpu(mm, this_cpu_ptr(mm->mm_cid.pcpu));
 			}
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -3762,8 +3762,10 @@ static __always_inline void mm_unset_cid
 static __always_inline void mm_drop_cid_on_cpu(struct mm_struct *mm, struct mm_cid_pcpu *pcp)
 {
 	/* Clear the ONCPU bit, but do not set UNSET in the per CPU storage */
-	pcp->cid = cpu_cid_to_cid(pcp->cid);
-	mm_drop_cid(mm, pcp->cid);
+	if (cid_on_cpu(pcp->cid)) {
+		pcp->cid = cpu_cid_to_cid(pcp->cid);
+		mm_drop_cid(mm, pcp->cid);
+	}
 }
 
 static inline unsigned int __mm_get_cid(struct mm_struct *mm, unsigned int max_cids)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ