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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox