Skip to content

Commit c8c4bfb

Browse files
roxanan1996PlaidCat
authored andcommitted
sched: fix exit_mm vs membarrier (v4)
bugfix CVE-2026-46333 commit-author Mathieu Desnoyers <mathieu.desnoyers@efficios.com> commit 5bc7850 upstream-diff Red Hat fixed this in the base code without pulling in this patch. This is technically a pre-conditional patch for the CVE applied afterward. The content is still valid but i'm changing the tag to bug fix so not to confuse anyone that we have additional CVE fixes that Red Hat has already addressed. exit_mm should issue memory barriers after user-space memory accesses, before clearing current->mm, to order user-space memory accesses performed prior to exit_mm before clearing tsk->mm, which has the effect of skipping the membarrier private expedited IPIs. exit_mm should also update the runqueue's membarrier_state so membarrier global expedited IPIs are not sent when they are not needed. The membarrier system call can be issued concurrently with do_exit if we have thread groups created with CLONE_VM but not CLONE_THREAD. Here is the scenario I have in mind: Two thread groups are created, A and B. Thread group B is created by issuing clone from group A with flag CLONE_VM set, but not CLONE_THREAD. Let's assume we have a single thread within each thread group (Thread A and Thread B). The AFAIU we can have: Userspace variables: int x = 0, y = 0; CPU 0 CPU 1 Thread A Thread B (in thread group A) (in thread group B) x = 1 barrier() y = 1 exit() exit_mm() current->mm = NULL; r1 = load y membarrier() skips CPU 0 (no IPI) because its current mm is NULL r2 = load x BUG_ON(r1 == 1 && r2 == 0) Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lkml.kernel.org/r/20201020134715.13909-2-mathieu.desnoyers@efficios.com (cherry picked from commit 5bc7850) Signed-off-by: Roxana Nicolescu <rnicolescu@ciq.com>
1 parent ea737e8 commit c8c4bfb

3 files changed

Lines changed: 32 additions & 1 deletion

File tree

include/linux/sched/mm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ static inline void membarrier_mm_sync_core_before_usermode(struct mm_struct *mm)
379379

380380
extern void membarrier_exec_mmap(struct mm_struct *mm);
381381

382+
extern void membarrier_update_current_mm(struct mm_struct *next_mm);
383+
382384
#else
383385
#ifdef CONFIG_ARCH_HAS_MEMBARRIER_CALLBACKS
384386
static inline void membarrier_arch_switch_mm(struct mm_struct *prev,
@@ -393,6 +395,9 @@ static inline void membarrier_exec_mmap(struct mm_struct *mm)
393395
static inline void membarrier_mm_sync_core_before_usermode(struct mm_struct *mm)
394396
{
395397
}
398+
static inline void membarrier_update_current_mm(struct mm_struct *next_mm)
399+
{
400+
}
396401
#endif
397402

398403
#ifdef CONFIG_IOMMU_SVA

kernel/exit.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,11 +482,25 @@ static void exit_mm(void)
482482
BUG_ON(mm != current->active_mm);
483483
/* more a memory barrier than a real lock */
484484
task_lock(current);
485+
/*
486+
* When a thread stops operating on an address space, the loop
487+
* in membarrier_private_expedited() may not observe that
488+
* tsk->mm, and the loop in membarrier_global_expedited() may
489+
* not observe a MEMBARRIER_STATE_GLOBAL_EXPEDITED
490+
* rq->membarrier_state, so those would not issue an IPI.
491+
* Membarrier requires a memory barrier after accessing
492+
* user-space memory, before clearing tsk->mm or the
493+
* rq->membarrier_state.
494+
*/
495+
smp_mb__after_spinlock();
496+
local_irq_disable();
485497
current->user_dumpable = (get_dumpable(mm) == SUID_DUMP_USER);
486498
current->mm = NULL;
487-
mmap_read_unlock(mm);
499+
membarrier_update_current_mm(NULL);
488500
enter_lazy_tlb(mm, current);
501+
local_irq_enable();
489502
task_unlock(current);
503+
mmap_read_unlock(mm);
490504
mm_update_next_owner(mm);
491505
mmput(mm);
492506
if (test_thread_flag(TIF_MEMDIE))

kernel/sched/membarrier.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ void membarrier_exec_mmap(struct mm_struct *mm)
7474
this_cpu_write(runqueues.membarrier_state, 0);
7575
}
7676

77+
void membarrier_update_current_mm(struct mm_struct *next_mm)
78+
{
79+
struct rq *rq = this_rq();
80+
int membarrier_state = 0;
81+
82+
if (next_mm)
83+
membarrier_state = atomic_read(&next_mm->membarrier_state);
84+
if (READ_ONCE(rq->membarrier_state) == membarrier_state)
85+
return;
86+
WRITE_ONCE(rq->membarrier_state, membarrier_state);
87+
}
88+
7789
static int membarrier_global_expedited(void)
7890
{
7991
int cpu;

0 commit comments

Comments
 (0)