If the unmap at the end of an mremap() move fails, the source VMA stays without VM_ACCOUNT. Neither munmap() nor exit_mmap() uncharges it then, so the charge for all of its pages stays in vm_committed_as after the process is gone. unmap_source_vma() clears the flag on the whole source VMA before the unmap and sets it again on the remaining parts afterwards, but its error path returns without doing so. This was reproduced with fault injection: each failure on an anonymous mapping left Committed_AS higher by the size of the whole VMA after everything was unmapped. Fix by setting VM_ACCOUNT again on everything in the range of the original VMA. A failed unmap may have split the VMA already, so walk the range. Fixes: 51df7bcb6151 ("mm/mremap: account memory on do_munmap() failure") Signed-off-by: Josef Bacik --- mm/mremap.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mm/mremap.c b/mm/mremap.c index 6f941aeb1b5f..256eac721a07 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -1246,9 +1246,20 @@ static void unmap_source_vma(struct vma_remap_struct *vrm) vrm->vma = NULL; /* Invalidated. */ vrm->vmi_needs_invalidate = true; if (err) { - /* OOM: unable to split vma, just get accounts right */ - if (accountable_move) + /* + * OOM: unable to split vma, just get accounts right. Both the + * old and the new range remain mapped, so charge the range a + * second time, and reinstate the flag on all that is left of + * the VMA so the charge is returned when it gets unmapped. + */ + if (accountable_move) { vm_acct_memory(len >> PAGE_SHIFT); + vma_iter_set(&vmi, vm_start); + for_each_vma_range(vmi, vma, vm_end) { + vma_start_write(vma); + vma_set_flags(vma, VMA_ACCOUNT_BIT); + } + } return; } -- 2.55.0