OpenVZ / Virtuozzo kernel development (devel@openvz.org)
 help / color / mirror / Atom feed
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

  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