mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 02:21:52 +09:00
UPSTREAM: mm/mempolicy: Take VMA lock before replacing policy
mbind() calls down into vma_replace_policy() without taking the per-VMA locks, replaces the VMA's vma->vm_policy pointer, and frees the old policy. That's bad; a concurrent page fault might still be using the old policy (in vma_alloc_folio()), resulting in use-after-free. Normally this will manifest as a use-after-free read first, but it can result in memory corruption, including because vma_alloc_folio() can call mpol_cond_put() on the freed policy, which conditionally changes the policy's refcount member. This bug is specific to CONFIG_NUMA, but it does also affect non-NUMA systems as long as the kernel was built with CONFIG_NUMA. Signed-off-by: Jann Horn <jannh@google.com> Reviewed-by: Suren Baghdasaryan <surenb@google.com> Fixes:5e31275cc9("mm: add per-VMA lock and helper functions to control it") Cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Bug: 293665307 (cherry picked from commit6c21e066f9) Change-Id: I2e3a4ee8bad97457ee3e127694f0609e7a240a2f Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
committed by
Suren Baghdasaryan
parent
890b1aabb1
commit
f5c707dc65
@@ -384,8 +384,10 @@ void mpol_rebind_mm(struct mm_struct *mm, nodemask_t *new)
|
||||
VMA_ITERATOR(vmi, mm, 0);
|
||||
|
||||
mmap_write_lock(mm);
|
||||
for_each_vma(vmi, vma)
|
||||
for_each_vma(vmi, vma) {
|
||||
vma_start_write(vma);
|
||||
mpol_rebind_policy(vma->vm_policy, new);
|
||||
}
|
||||
mmap_write_unlock(mm);
|
||||
}
|
||||
|
||||
@@ -759,6 +761,8 @@ static int vma_replace_policy(struct vm_area_struct *vma,
|
||||
struct mempolicy *old;
|
||||
struct mempolicy *new;
|
||||
|
||||
vma_assert_write_locked(vma);
|
||||
|
||||
pr_debug("vma %lx-%lx/%lx vm_ops %p vm_file %p set_policy %p\n",
|
||||
vma->vm_start, vma->vm_end, vma->vm_pgoff,
|
||||
vma->vm_ops, vma->vm_file,
|
||||
@@ -1259,6 +1263,8 @@ static long do_mbind(unsigned long start, unsigned long len,
|
||||
nodemask_t *nmask, unsigned long flags)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
struct vma_iterator vmi;
|
||||
struct mempolicy *new;
|
||||
unsigned long end;
|
||||
int err;
|
||||
@@ -1320,6 +1326,14 @@ static long do_mbind(unsigned long start, unsigned long len,
|
||||
if (err)
|
||||
goto mpol_out;
|
||||
|
||||
/*
|
||||
* Lock the VMAs before scanning for pages to migrate, to ensure we don't
|
||||
* miss a concurrently inserted page.
|
||||
*/
|
||||
vma_iter_init(&vmi, mm, start);
|
||||
for_each_vma_range(vmi, vma, end)
|
||||
vma_start_write(vma);
|
||||
|
||||
ret = queue_pages_range(mm, start, end, nmask,
|
||||
flags | MPOL_MF_INVERT, &pagelist);
|
||||
|
||||
@@ -1546,6 +1560,7 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le
|
||||
break;
|
||||
}
|
||||
|
||||
vma_start_write(vma);
|
||||
new->home_node = home_node;
|
||||
err = mbind_range(mm, vmstart, vmend, new);
|
||||
mpol_put(new);
|
||||
|
||||
Reference in New Issue
Block a user