Commit f4e9e0e6 authored by Liam R. Howlett's avatar Liam R. Howlett Committed by Andrew Morton

mm/mempolicy: fix use-after-free of VMA iterator

set_mempolicy_home_node() iterates over a list of VMAs and calls
mbind_range() on each VMA, which also iterates over the singular list of
the VMA passed in and potentially splits the VMA.  Since the VMA iterator
is not passed through, set_mempolicy_home_node() may now point to a stale
node in the VMA tree.  This can result in a UAF as reported by syzbot.

Avoid the stale maple tree node by passing the VMA iterator through to the
underlying call to split_vma().

mbind_range() is also overly complicated, since there are two calling
functions and one already handles iterating over the VMAs.  Simplify
mbind_range() to only handle merging and splitting of the VMAs.

Align the new loop in do_mbind() and existing loop in
set_mempolicy_home_node() to use the reduced mbind_range() function.  This
allows for a single location of the range calculation and avoids
constantly looking up the previous VMA (since this is a loop over the
VMAs).

Link: https://lore.kernel.org/linux-mm/000000000000c93feb05f87e24ad@google.com/
Fixes: 66850be5 ("mm/mempolicy: use vma iterator & maple state instead of vma linked list")
Signed-off-by: default avatarLiam R. Howlett <Liam.Howlett@oracle.com>
Reported-by: syzbot+a7c1ec5b1d71ceaa5186@syzkaller.appspotmail.com
  Link: https://lkml.kernel.org/r/20230410152205.2294819-1-Liam.Howlett@oracle.com
Tested-by: syzbot+a7c1ec5b1d71ceaa5186@syzkaller.appspotmail.com
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 4737edbb
...@@ -790,61 +790,50 @@ static int vma_replace_policy(struct vm_area_struct *vma, ...@@ -790,61 +790,50 @@ static int vma_replace_policy(struct vm_area_struct *vma,
return err; return err;
} }
/* Step 2: apply policy to a range and do splits. */ /* Split or merge the VMA (if required) and apply the new policy */
static int mbind_range(struct mm_struct *mm, unsigned long start, static int mbind_range(struct vma_iterator *vmi, struct vm_area_struct *vma,
unsigned long end, struct mempolicy *new_pol) struct vm_area_struct **prev, unsigned long start,
unsigned long end, struct mempolicy *new_pol)
{ {
VMA_ITERATOR(vmi, mm, start); struct vm_area_struct *merged;
struct vm_area_struct *prev; unsigned long vmstart, vmend;
struct vm_area_struct *vma;
int err = 0;
pgoff_t pgoff; pgoff_t pgoff;
int err;
prev = vma_prev(&vmi); vmend = min(end, vma->vm_end);
vma = vma_find(&vmi, end); if (start > vma->vm_start) {
if (WARN_ON(!vma)) *prev = vma;
vmstart = start;
} else {
vmstart = vma->vm_start;
}
if (mpol_equal(vma_policy(vma), new_pol))
return 0; return 0;
if (start > vma->vm_start) pgoff = vma->vm_pgoff + ((vmstart - vma->vm_start) >> PAGE_SHIFT);
prev = vma; merged = vma_merge(vmi, vma->vm_mm, *prev, vmstart, vmend, vma->vm_flags,
vma->anon_vma, vma->vm_file, pgoff, new_pol,
do { vma->vm_userfaultfd_ctx, anon_vma_name(vma));
unsigned long vmstart = max(start, vma->vm_start); if (merged) {
unsigned long vmend = min(end, vma->vm_end); *prev = merged;
return vma_replace_policy(merged, new_pol);
if (mpol_equal(vma_policy(vma), new_pol)) }
goto next;
if (vma->vm_start != vmstart) {
pgoff = vma->vm_pgoff + err = split_vma(vmi, vma, vmstart, 1);
((vmstart - vma->vm_start) >> PAGE_SHIFT);
prev = vma_merge(&vmi, mm, prev, vmstart, vmend, vma->vm_flags,
vma->anon_vma, vma->vm_file, pgoff,
new_pol, vma->vm_userfaultfd_ctx,
anon_vma_name(vma));
if (prev) {
vma = prev;
goto replace;
}
if (vma->vm_start != vmstart) {
err = split_vma(&vmi, vma, vmstart, 1);
if (err)
goto out;
}
if (vma->vm_end != vmend) {
err = split_vma(&vmi, vma, vmend, 0);
if (err)
goto out;
}
replace:
err = vma_replace_policy(vma, new_pol);
if (err) if (err)
goto out; return err;
next: }
prev = vma;
} for_each_vma_range(vmi, vma, end);
out: if (vma->vm_end != vmend) {
return err; err = split_vma(vmi, vma, vmend, 0);
if (err)
return err;
}
*prev = vma;
return vma_replace_policy(vma, new_pol);
} }
/* Set the process memory policy */ /* Set the process memory policy */
...@@ -1259,6 +1248,8 @@ static long do_mbind(unsigned long start, unsigned long len, ...@@ -1259,6 +1248,8 @@ static long do_mbind(unsigned long start, unsigned long len,
nodemask_t *nmask, unsigned long flags) nodemask_t *nmask, unsigned long flags)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct vm_area_struct *vma, *prev;
struct vma_iterator vmi;
struct mempolicy *new; struct mempolicy *new;
unsigned long end; unsigned long end;
int err; int err;
...@@ -1328,7 +1319,13 @@ static long do_mbind(unsigned long start, unsigned long len, ...@@ -1328,7 +1319,13 @@ static long do_mbind(unsigned long start, unsigned long len,
goto up_out; goto up_out;
} }
err = mbind_range(mm, start, end, new); vma_iter_init(&vmi, mm, start);
prev = vma_prev(&vmi);
for_each_vma_range(vmi, vma, end) {
err = mbind_range(&vmi, vma, &prev, start, end, new);
if (err)
break;
}
if (!err) { if (!err) {
int nr_failed = 0; int nr_failed = 0;
...@@ -1489,10 +1486,8 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le ...@@ -1489,10 +1486,8 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le
unsigned long, home_node, unsigned long, flags) unsigned long, home_node, unsigned long, flags)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct vm_area_struct *vma; struct vm_area_struct *vma, *prev;
struct mempolicy *new, *old; struct mempolicy *new, *old;
unsigned long vmstart;
unsigned long vmend;
unsigned long end; unsigned long end;
int err = -ENOENT; int err = -ENOENT;
VMA_ITERATOR(vmi, mm, start); VMA_ITERATOR(vmi, mm, start);
...@@ -1521,6 +1516,7 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le ...@@ -1521,6 +1516,7 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le
if (end == start) if (end == start)
return 0; return 0;
mmap_write_lock(mm); mmap_write_lock(mm);
prev = vma_prev(&vmi);
for_each_vma_range(vmi, vma, end) { for_each_vma_range(vmi, vma, end) {
/* /*
* If any vma in the range got policy other than MPOL_BIND * If any vma in the range got policy other than MPOL_BIND
...@@ -1541,9 +1537,7 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le ...@@ -1541,9 +1537,7 @@ SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, le
} }
new->home_node = home_node; new->home_node = home_node;
vmstart = max(start, vma->vm_start); err = mbind_range(&vmi, vma, &prev, start, end, new);
vmend = min(end, vma->vm_end);
err = mbind_range(mm, vmstart, vmend, new);
mpol_put(new); mpol_put(new);
if (err) if (err)
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment