From: Konstantin Khorenko <khorenko@virtuozzo.com>
To: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>,
Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Cc: devel@openvz.org
Subject: Re: [Devel] [PATCH hci-8.1 6/8] oracle/mm: zap the preserved ranges of the old mm after copying them
Date: Wed, 21 Oct 2026 18:44:53 +0200 [thread overview]
Message-ID: <ddd66b43-7408-45c1-a306-c0febef4b045@virtuozzo.com> (raw)
In-Reply-To: <010701a13b71fa00-58d13ddf-f053-4152-80a5-977e12044dd5-000000@eu-central-1.amazonses.com>
On 10/14/26 19:24, no-reply@sashiko.virtuozzo.com wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] mm: zap_vma_mt() clears old_mm PTEs without vma_start_write()
> --
>
> commit f34df816bcbe51e2402a6b8ae419dea8ef679f49
> Author: Konstantin Khorenko <khorenko@virtuozzo.com>
>
> oracle/mm: zap the preserved ranges of the old mm after copying them
>
> After the VM_EXEC_KEEP ranges are copied into the new mm, this patch zaps
> them from the old mm under its write lock. This is meant to leave the
> copies as the only mappings of the preserved pages. A new zap_vma_mt()
> helper spreads the zap of an anonymous VMA over several threads.
>
> Link: https://virtuozzo.atlassian.net/browse/VSTOR-150231
>
>> diff --git a/fs/exec.c b/fs/exec.c
>> --- a/fs/exec.c
>> +++ b/fs/exec.c
>
> [ ... ]
>
>> @@ -997,7 +997,15 @@ static int vma_dup_some(struct mm_struct *old_mm, struct mm_struct *new_mm)
>> }
>>
>> mmap_write_unlock(new_mm);
>> - return ret;
>> + if (ret)
>> + return ret;
>> +
>> + /* Whoever keeps old_mm alive past the exec must not reach the pages through it. */
>> + vma_iter_set(&vmi, 0);
>> + for_each_vma(vmi, vma)
>> + if (vma->vm_flags & VM_EXEC_KEEP)
>> + zap_vma_mt(vma);
>> + return 0;
>> }
>
> [Severity: Medium]
> Can this zap race with a per-VMA lock holder on old_mm?
No. The old VMA is write-locked before the copy and stays so through
the zap.
vma_dup() clears VM_ACCOUNT on the source with
vm_flags_clear(old_vma, VM_ACCOUNT) before copy_page_range_mt(), and
vm_flags_clear() starts with vma_start_write(). That waits for the
per-VMA readers already inside, such as uffd_move_lock() or
uffd_lock_vma(), and makes lock_vma_under_rcu() fail for new ones until
the mmap lock is dropped. exec_mmap() keeps old_mm write-locked from
vma_dup_some() until after zap_vma_mt(), so the lock taken in vma_dup()
still covers the zap.
An UFFDIO_MOVE from a task that holds the uffd therefore falls back to
mmap_read_lock() and waits for the exec. Whatever it does to old_mm
afterwards no longer reaches the preserved pages: they are zapped by
then.
The write lock is a side effect of vm_flags_clear(), though. "mm: allow
MADV_DOEXEC on shared PFN mappings of device memory" makes that clear
conditional and adds an explicit vma_start_write(old_vma) for this
reason.
>
> The loop calls zap_vma_mt() on old_mm VMAs without calling
> vma_start_write(vma) first:
>
> vma_dup_some()
> for_each_vma(vmi, vma)
> zap_vma_mt(vma)
>
> mmap_write_lock() alone does not drain holders that already took the
> per-VMA read lock through lock_vma_under_rcu().
>
> One such holder is UFFDIO_MOVE:
>
> uffd_move_lock()
> *src_vmap = lock_vma_under_rcu(mm, src_start);
>
> This path never takes mmap_lock. A task that owns a userfaultfd context
> bound to old_mm has mm_users on old_mm, and its own t->mm is a different
> mm. reserve_preserved_mem() only rejects tasks whose t->mm == mm, so such
> a task is not excluded.
>
> It could therefore run folio_move_anon_rmap() or a fault in old_mm while
> the zap clears the same PTEs. That would reintroduce the problems the
> commit message describes, since the preserved pages still carry
> PageAnonExclusive in both mms.
>
> Would it make sense to call vma_start_write(vma) before zapping each
> VM_EXEC_KEEP VMA, either in vma_dup_some() or at the start of
> zap_vma_mt()?
>
>> diff --git a/mm/memory.c b/mm/memory.c
>> --- a/mm/memory.c
>> +++ b/mm/memory.c
>
> [ ... ]
>
>> +void zap_vma_mt(struct vm_area_struct *vma)
>> +{
>> +#ifdef CONFIG_PADATA
>> + struct padata_mt_job job = {
>
> [ ... ]
>
>> + mmap_assert_write_locked(vma->vm_mm);
>> + if (vma_is_anonymous(vma)) {
>> + lru_add_drain();
>> + mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma->vm_mm,
>> + vma->vm_start, vma->vm_end);
>> + update_hiwater_rss(vma->vm_mm);
>> + mmu_notifier_invalidate_range_start(&range);
>> + padata_do_multithreaded(&job);
>> + mmu_notifier_invalidate_range_end(&range);
>> + return;
>> + }
>> +#endif
>> + zap_page_range_single(vma, vma->vm_start, vma->vm_end - vma->vm_start,
>> + NULL);
>> +}
>
> [Severity: Medium]
> The same concern applies here. zap_vma_mt() only asserts the mmap write
> lock with mmap_assert_write_locked() before clearing PTEs, either in the
> padata worker threads through zap_vma_chunk() or in
> zap_page_range_single().
>
> It never calls vma_start_write(vma).
>
> For the anonymous case, can PTEs be cleared while a per-VMA locked fault
> or UFFDIO_MOVE is still operating on the same VMA? Both the padata path
> and the zap_page_range_single() path appear to be exposed.
>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
next prev parent reply other threads:[~2026-10-21 16:46 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 17:11 [Devel] [PATCH hci-8.1 0/8] mm: keep DMA-pinned MADV_DOEXEC memory working across exec Konstantin Khorenko
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 1/8] oracle/mm: fix VM_BUG_ON hit when preserving memory Konstantin Khorenko
2026-10-14 17:16 ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 2/8] oracle/mm: hold the source mmap write lock when copying PTEs Konstantin Khorenko
2026-10-14 17:15 ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 3/8] oracle/mm: do not write protect COW mappings when preserving across exec Konstantin Khorenko
2026-10-14 17:20 ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 4/8] oracle/mm: write protect preserved PTEs but keep them exclusive Konstantin Khorenko
2026-10-14 17:18 ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 5/8] oracle/mm: set MMF_HAS_PINNED on the mm receiving preserved pages Konstantin Khorenko
2026-10-14 17:19 ` no-reply
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 6/8] oracle/mm: zap the preserved ranges of the old mm after copying them Konstantin Khorenko
2026-10-14 17:24 ` no-reply
2026-10-21 16:44 ` Konstantin Khorenko [this message]
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 7/8] exec: protect preserved memory against surviving shared-mm tasks Konstantin Khorenko
2026-10-14 17:24 ` no-reply
2026-10-21 16:46 ` Konstantin Khorenko
2026-10-14 17:11 ` [Devel] [PATCH hci-8.1 8/8] selftests/mm: test memory preservation across exec Konstantin Khorenko
2026-10-14 17:33 ` no-reply
2026-10-21 16:48 ` Konstantin Khorenko
2026-11-04 19:21 ` [Devel] [PATCH hci-8.1 0/8] mm: keep DMA-pinned MADV_DOEXEC memory working " Konstantin Khorenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ddd66b43-7408-45c1-a306-c0febef4b045@virtuozzo.com \
--to=khorenko@virtuozzo.com \
--cc=andrey.drobyshev@virtuozzo.com \
--cc=devel@openvz.org \
--cc=ptikhomirov@virtuozzo.com \
--cc=vasileios.almpanis@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.