Skip to content

Commit

Permalink
vma_adjust: fix the copying of anon_vma chains
Browse files Browse the repository at this point in the history
When we move the boundaries between two vma's due to things like
mprotect, we need to make sure that the anon_vma of the pages that got
moved from one vma to another gets properly copied around.  And that was
not always the case, in this rather hard-to-follow code sequence.

Clarify the code, and fix it so that it copies the anon_vma from the
right source.

Reviewed-by: Rik van Riel <[email protected]>
Acked-by: Johannes Weiner <[email protected]>
Tested-by: Borislav Petkov <[email protected]> [ "Yeah, not so much this one either" ]
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
torvalds committed Apr 13, 2010
1 parent d0e9fe1 commit 287d97a
Showing 1 changed file with 8 additions and 16 deletions.
24 changes: 8 additions & 16 deletions mm/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,27 +507,28 @@ int vma_adjust(struct vm_area_struct *vma, unsigned long start,
struct address_space *mapping = NULL;
struct prio_tree_root *root = NULL;
struct file *file = vma->vm_file;
struct anon_vma *anon_vma = NULL;
long adjust_next = 0;
int remove_next = 0;

if (next && !insert) {
struct vm_area_struct *exporter = NULL;

if (end >= next->vm_end) {
/*
* vma expands, overlapping all the next, and
* perhaps the one after too (mprotect case 6).
*/
again: remove_next = 1 + (end > next->vm_end);
end = next->vm_end;
anon_vma = next->anon_vma;
exporter = next;
importer = vma;
} else if (end > next->vm_start) {
/*
* vma expands, overlapping part of the next:
* mprotect case 5 shifting the boundary up.
*/
adjust_next = (end - next->vm_start) >> PAGE_SHIFT;
anon_vma = next->anon_vma;
exporter = next;
importer = vma;
} else if (end < vma->vm_end) {
/*
Expand All @@ -536,28 +537,19 @@ again: remove_next = 1 + (end > next->vm_end);
* mprotect case 4 shifting the boundary down.
*/
adjust_next = - ((vma->vm_end - end) >> PAGE_SHIFT);
anon_vma = next->anon_vma;
exporter = vma;
importer = next;
}
}

/*
* When changing only vma->vm_end, we don't really need anon_vma lock.
*/
if (vma->anon_vma && (insert || importer || start != vma->vm_start))
anon_vma = vma->anon_vma;
if (anon_vma) {
/*
* Easily overlooked: when mprotect shifts the boundary,
* make sure the expanding vma has anon_vma set if the
* shrinking vma had, to cover any anon pages imported.
*/
if (importer && !importer->anon_vma) {
/* Block reverse map lookups until things are set up. */
if (anon_vma_clone(importer, vma)) {
if (exporter && exporter->anon_vma && !importer->anon_vma) {
if (anon_vma_clone(importer, exporter))
return -ENOMEM;
}
importer->anon_vma = anon_vma;
importer->anon_vma = exporter->anon_vma;
}
}

Expand Down

0 comments on commit 287d97a

Please sign in to comment.