Skip to content

Commit dadbd85

Browse files
bgafftorvalds
authored andcommitted
mm: Fix MREMAP_DONTUNMAP accounting on VMA merge
When remapping a mapping where a portion of a VMA is remapped into another portion of the VMA it can cause the VMA to become split. During the copy_vma operation the VMA can actually be remerged if it's an anonymous VMA whose pages have not yet been faulted. This isn't normally a problem because at the end of the remap the original portion is unmapped causing it to become split again. However, MREMAP_DONTUNMAP leaves that original portion in place which means that the VMA which was split and then remerged is not actually split at the end of the mremap. This patch fixes a bug where we don't detect that the VMAs got remerged and we end up putting back VM_ACCOUNT on the next mapping which is completely unreleated. When that next mapping is unmapped it results in incorrectly unaccounting for the memory which was never accounted, and eventually we will underflow on the memory comittment. There is also another issue which is similar, we're currently accouting for the number of pages in the new_vma but that's wrong. We need to account for the length of the remap operation as that's all that is being added. If there was a mapping already at that location its comittment would have been adjusted as part of the munmap at the start of the mremap. A really simple repro can be seen in: https://gist.github.com/bgaff/e101ce99da7d9a8c60acc641d07f312c Fixes: e346b38 ("mm/mremap: add MREMAP_DONTUNMAP to mremap()") Reported-by: syzbot <[email protected]> Signed-off-by: Brian Geffon <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 86cc339 commit dadbd85

File tree

1 file changed

+12
-1
lines changed

1 file changed

+12
-1
lines changed

mm/mremap.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,20 @@ static unsigned long move_vma(struct vm_area_struct *vma,
413413
/* Always put back VM_ACCOUNT since we won't unmap */
414414
vma->vm_flags |= VM_ACCOUNT;
415415

416-
vm_acct_memory(vma_pages(new_vma));
416+
vm_acct_memory(new_len >> PAGE_SHIFT);
417417
}
418418

419+
/*
420+
* VMAs can actually be merged back together in copy_vma
421+
* calling merge_vma. This can happen with anonymous vmas
422+
* which have not yet been faulted, so if we were to consider
423+
* this VMA split we'll end up adding VM_ACCOUNT on the
424+
* next VMA, which is completely unrelated if this VMA
425+
* was re-merged.
426+
*/
427+
if (split && new_vma == vma)
428+
split = 0;
429+
419430
/* We always clear VM_LOCKED[ONFAULT] on the old vma */
420431
vma->vm_flags &= VM_LOCKED_CLEAR_MASK;
421432

0 commit comments

Comments
 (0)