mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] mm/mremap: account unmoved locked pages in dontunmap_complete()
@ 2026-09-19 21:06 Hui Peng
  2026-09-20 10:15 ` Lorenzo Stoakes (ARM)
  0 siblings, 1 reply; 2+ messages in thread
From: Hui Peng @ 2026-09-19 21:06 UTC (permalink / raw)
  To: akpm, liam, ljs; +Cc: vbabka, jannh, pfalcato, bgeffon, linux-mm, linux-kernel

When MREMAP_DONTUNMAP is performed on a VM_LOCKED VMA,
dontunmap_complete() clears VMA_LOCKED_MASK on the entire source VMA
(vrm->vma) before vrm_stat_account() runs. This assumes that the entire
source VMA was moved into a distinct destination VMA (new_vma != vma) of
equal size, transferring the VM_LOCKED accounting 1:1 from vma to
new_vma.

However, two cases violate this assumption:

1. Partial MREMAP_DONTUNMAP on a multi-page VM_LOCKED VMA (vrm->old_len
   < vma->vm_end - vma->vm_start): vma_clear_flags_mask(vma,
   VMA_LOCKED_MASK) clears VM_LOCKED on the entire source VMA of size
   vma_pages(vma), while new_vma only inherits VM_LOCKED for
   vrm->old_len >> PAGE_SHIFT pages. The remaining unmoved pages in vma
   lose VM_LOCKED without decrementing mm->locked_vm.
2. Self-merge in copy_vma() (new_vma == vma, when new_addr is
   immediately adjacent to vma): copy_vma() expands vma by vrm->new_len,
   and dontunmap_complete() then clears VMA_LOCKED_MASK on the combined
   VMA. All originally locked pages lose VM_LOCKED without decrementing
   mm->locked_vm.

In both cases, subsequent munmap() of the VMAs sees VM_LOCKED cleared
and does not decrement mm->locked_vm, permanently leaking mm->locked_vm
until process exit and allowing unprivileged processes to exhaust
RLIMIT_MEMLOCK.

Fix this in dontunmap_complete() by subtracting the number of pages that
lose VM_LOCKED from current->mm->locked_vm before clearing
VMA_LOCKED_MASK.

Fixes: e346b3813067 ("mm/mremap: add MREMAP_DONTUNMAP to mremap()")
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>

---
 mm/mremap.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/mm/mremap.c b/mm/mremap.c
index 7c368440fafe..ff794e24f78f 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -1335,6 +1335,16 @@ static void dontunmap_complete(struct vma_remap_struct *vrm,
 	unsigned long old_start = vma->vm_start;
 	unsigned long old_end = vma->vm_end;
 
+	if (vma_test(vma, VMA_LOCKED_BIT)) {
+		unsigned long unl_pages = vma_pages(vma);
+
+		if (new_vma != vma)
+			unl_pages -= vrm->old_len >> PAGE_SHIFT;
+		else
+			unl_pages -= vrm->new_len >> PAGE_SHIFT;
+		current->mm->locked_vm -= unl_pages;
+	}
+
 	/* We always clear VMA_LOCKED[ONFAULT]_BIT on the old VMA. */
 	vma_clear_flags_mask(vma, VMA_LOCKED_MASK);
 
-- 
2.55.0.1082.g2b9226bbc0-goog


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-20 10:15 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-19 21:06 [PATCH] mm/mremap: account unmoved locked pages in dontunmap_complete() Hui Peng
2026-09-20 10:15 ` Lorenzo Stoakes (ARM)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®