Skip to content

Commit 1e83ccd

Browse files
Thomas Gleixnertorvalds
authored andcommitted
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: 007d842 ("sched/mmcid: Drop per CPU CID immediately when switching to per task mode") Reported-by: Shinichiro Kawasaki <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Shinichiro Kawasaki <[email protected]> Cc: [email protected] Closes: https://lore.kernel.org/aYsZrixn9b6s_2zL@shinmob Reviewed-by: Mathieu Desnoyers <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 939faf7 commit 1e83ccd

2 files changed

Lines changed: 7 additions & 6 deletions

File tree

kernel/sched/core.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10788,10 +10788,9 @@ void sched_mm_cid_exit(struct task_struct *t)
1078810788
return;
1078910789
/*
1079010790
* Mode change. The task has the CID unset
10791-
* already. The CPU CID is still valid and
10792-
* does not have MM_CID_TRANSIT set as the
10793-
* mode change has just taken effect under
10794-
* mm::mm_cid::lock. Drop it.
10791+
* already and dealt with an eventually set
10792+
* TRANSIT bit. If the CID is owned by the CPU
10793+
* then drop it.
1079510794
*/
1079610795
mm_drop_cid_on_cpu(mm, this_cpu_ptr(mm->mm_cid.pcpu));
1079710796
}

kernel/sched/sched.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,8 +3813,10 @@ static __always_inline void mm_unset_cid_on_task(struct task_struct *t)
38133813
static __always_inline void mm_drop_cid_on_cpu(struct mm_struct *mm, struct mm_cid_pcpu *pcp)
38143814
{
38153815
/* Clear the ONCPU bit, but do not set UNSET in the per CPU storage */
3816-
pcp->cid = cpu_cid_to_cid(pcp->cid);
3817-
mm_drop_cid(mm, pcp->cid);
3816+
if (cid_on_cpu(pcp->cid)) {
3817+
pcp->cid = cpu_cid_to_cid(pcp->cid);
3818+
mm_drop_cid(mm, pcp->cid);
3819+
}
38183820
}
38193821

38203822
static inline unsigned int __mm_get_cid(struct mm_struct *mm, unsigned int max_cids)

0 commit comments

Comments
 (0)