Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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@wdc.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Cc: stable@vger.kernel.org
Closes: https://lore.kernel.org/aYsZrixn9b6s_2zL@shinmob
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Thomas Gleixner and committed by
Linus Torvalds
1e83ccd5 939faf71

+7 -6
+3 -4
kernel/sched/core.c
··· 10788 10788 return; 10789 10789 /* 10790 10790 * 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. 10795 10794 */ 10796 10795 mm_drop_cid_on_cpu(mm, this_cpu_ptr(mm->mm_cid.pcpu)); 10797 10796 }
+4 -2
kernel/sched/sched.h
··· 3813 3813 static __always_inline void mm_drop_cid_on_cpu(struct mm_struct *mm, struct mm_cid_pcpu *pcp) 3814 3814 { 3815 3815 /* 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 + } 3818 3820 } 3819 3821 3820 3822 static inline unsigned int __mm_get_cid(struct mm_struct *mm, unsigned int max_cids)