Skip to content

Commit 64591e8

Browse files
surenbaghdasaryantorvalds
authored andcommitted
mm: protect free_pgtables with mmap_lock write lock in exit_mmap
oom-reaper and process_mrelease system call should protect against races with exit_mmap which can destroy page tables while they walk the VMA tree. oom-reaper protects from that race by setting MMF_OOM_VICTIM and by relying on exit_mmap to set MMF_OOM_SKIP before taking and releasing mmap_write_lock. process_mrelease has to elevate mm->mm_users to prevent such race. Both oom-reaper and process_mrelease hold mmap_read_lock when walking the VMA tree. The locking rules and mechanisms could be simpler if exit_mmap takes mmap_write_lock while executing destructive operations such as free_pgtables. Change exit_mmap to hold the mmap_write_lock when calling unlock_range, free_pgtables and remove_vma. Note also that because oom-reaper checks VM_LOCKED flag, unlock_range() should not be allowed to race with it. Before this patch, remove_vma used to be called with no locks held, however with fput being executed asynchronously and vm_ops->close not being allowed to hold mmap_lock (it is called from __split_vma with mmap_sem held for write), changing that should be fine. In most cases this lock should be uncontended. Previously, Kirill reported ~4% regression caused by a similar change [1]. We reran the same test and although the individual results are quite noisy, the percentiles show lower regression with 1.6% being the worst case [2]. The change allows oom-reaper and process_mrelease to execute safely under mmap_read_lock without worries that exit_mmap might destroy page tables from under them. [1] https://lore.kernel.org/all/[email protected]/ [2] https://lore.kernel.org/all/CAJuCfpGC9-c9P40x7oy=jy5SphMcd0o0G_6U1-+JAziGKG6dGA@mail.gmail.com/ Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Suren Baghdasaryan <[email protected]> Acked-by: Michal Hocko <[email protected]> Cc: David Rientjes <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Roman Gushchin <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Minchan Kim <[email protected]> Cc: Kirill A. Shutemov <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Christian Brauner <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Jann Horn <[email protected]> Cc: Shakeel Butt <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Christian Brauner <[email protected]> Cc: Florian Weimer <[email protected]> Cc: Jan Engelhardt <[email protected]> Cc: Tim Murray <[email protected]> Cc: Jason Gunthorpe <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 36090de commit 64591e8

File tree

1 file changed

+8
-8
lines changed

1 file changed

+8
-8
lines changed

mm/mmap.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3149,25 +3149,27 @@ void exit_mmap(struct mm_struct *mm)
31493149
* to mmu_notifier_release(mm) ensures mmu notifier callbacks in
31503150
* __oom_reap_task_mm() will not block.
31513151
*
3152-
* This needs to be done before calling munlock_vma_pages_all(),
3152+
* This needs to be done before calling unlock_range(),
31533153
* which clears VM_LOCKED, otherwise the oom reaper cannot
31543154
* reliably test it.
31553155
*/
31563156
(void)__oom_reap_task_mm(mm);
31573157

31583158
set_bit(MMF_OOM_SKIP, &mm->flags);
3159-
mmap_write_lock(mm);
3160-
mmap_write_unlock(mm);
31613159
}
31623160

3161+
mmap_write_lock(mm);
31633162
if (mm->locked_vm)
31643163
unlock_range(mm->mmap, ULONG_MAX);
31653164

31663165
arch_exit_mmap(mm);
31673166

31683167
vma = mm->mmap;
3169-
if (!vma) /* Can happen if dup_mmap() received an OOM */
3168+
if (!vma) {
3169+
/* Can happen if dup_mmap() received an OOM */
3170+
mmap_write_unlock(mm);
31703171
return;
3172+
}
31713173

31723174
lru_add_drain();
31733175
flush_cache_mm(mm);
@@ -3178,16 +3180,14 @@ void exit_mmap(struct mm_struct *mm)
31783180
free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, USER_PGTABLES_CEILING);
31793181
tlb_finish_mmu(&tlb);
31803182

3181-
/*
3182-
* Walk the list again, actually closing and freeing it,
3183-
* with preemption enabled, without holding any MM locks.
3184-
*/
3183+
/* Walk the list again, actually closing and freeing it. */
31853184
while (vma) {
31863185
if (vma->vm_flags & VM_ACCOUNT)
31873186
nr_accounted += vma_pages(vma);
31883187
vma = remove_vma(vma);
31893188
cond_resched();
31903189
}
3190+
mmap_write_unlock(mm);
31913191
vm_unacct_memory(nr_accounted);
31923192
}
31933193

0 commit comments

Comments
 (0)