All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
From: Konstantin Khorenko <khorenko@virtuozzo.com>
To: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
	Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>,
	Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Cc: devel@openvz.org
Subject: Re: [Devel] [PATCH hci-8.1 7/8] exec: protect preserved memory against surviving shared-mm tasks
Date: Wed, 21 Oct 2026 18:46:23 +0200	[thread overview]
Message-ID: <a353e8c2-2663-431c-b3fb-bdddfd3baaa8@virtuozzo.com> (raw)
In-Reply-To: <010701a13b71d002-8786f746-5d27-43bf-bc0b-e4c019bd0aa4-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 2 potential issue(s) to consider:
> - [Low] fork: copy_mm()/copy_process() take page_table_lock twice per shared-mm clone
> - [Low] exec: VM_EXEC_KEEP scan in reserve_preserved_mem() is stale by exec_mmap()
> --
> 
> commit 3fae5b8a82902f91e53a5e8e729322cb7b034228
> Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
> 
> exec: protect preserved memory against surviving shared-mm tasks
> 
> This patch makes an exec that accepts preserved memory fail with EBUSY
> when a task outside the thread group still uses the mm and the mm has a
> VM_EXEC_KEEP VMA. copy_mm() refuses and counts CLONE_VM, non-thread clones
> while the mm is marked. exec_mmap() takes the preserve path only when the
> mm was marked.
> 
> 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
> 
> [ ... ]
> 
>> +static int reserve_preserved_mem(struct linux_binprm *bprm)
>> +{
>> +	struct mm_struct *mm = current->mm;
> 
> [ ... ]
> 
>> +	mmap_read_unlock(mm);
>> +	if (!preserve)
>> +		return 0;
> 
> [Severity: Low]
> Can the result of this scan be stale by the time exec_mmap() runs?


  Yes, a range another task marks after the scan is not preserved. I
  think that is acceptable:

  - A sibling thread is killed by de_thread() right after. Its madvise()
    races with the execve() in userspace either way; the scan is where
    the exec decides, and a mark that comes later counts as one made
    after the exec.
  - A CLONE_VM task keeps the old mm, marks included, so it loses nothing.

  Nothing is left half done either: without preserving_mem, exec_mmap()
  takes the normal path and the marked VMAs go away with the old mm, as
  on any exec.

> 
> When no VM_EXEC_KEEP VMA is found here, nothing is reserved:
> exec_keep_active and bprm->preserving_mem both stay unset.  Sibling
> threads have not yet been removed by de_thread(), and copy_mm() only
> refuses clones while exec_keep_active is set.  So a sibling thread or a
> CLONE_VM task can still call madvise(MADV_DOEXEC) after mmap_read_unlock()
> and set VM_EXEC_KEEP on a range.
> 
>> @@ -1040,7 +1112,7 @@ static int exec_mmap(struct linux_binprm *bprm)
>>  			up_write(&tsk->signal->exec_update_lock);
>>  			return ret;
>>  		}
>> -		if (unlikely(bprm->accepts_preserved_mem)) {
>> +		if (unlikely(bprm->preserving_mem)) {
> 
> Here the preserve path (write lock plus vma_dup_some()) is now selected by
> the early sample rather than by the VMAs present under the lock at copy
> time.  A VM_EXEC_KEEP VMA created after the scan in
> reserve_preserved_mem() would not be copied, and the range would be lost
> with the old mm without any error.
> 
> Before this patch, accepts_preserved_mem alone selected the path, and
> vma_dup_some() copied any VM_EXEC_KEEP VMA present under
> mmap_write_lock.  Should the decision be revalidated, or the mark taken
> before the scan, so that a range marked during begin_new_exec() is not
> silently dropped?
> 
> [ ... ]
> 
>> diff --git a/kernel/fork.c b/kernel/fork.c
>> --- a/kernel/fork.c
>> +++ b/kernel/fork.c
> 
> [ ... ]
> 
>> +/* Keep unpublished CLONE_VM tasks visible to preserving-exec admission. */
>> +static int exec_keep_clone_begin(struct mm_struct *mm)
>> +{
>> +	int ret = 0;
>> +
>> +	spin_lock(&mm->page_table_lock);
>> +	if (mm->exec_keep_active)
>> +		ret = -EBUSY;
>> +	else
>> +		mm->exec_keep_clones++;
>> +	spin_unlock(&mm->page_table_lock);
>> +	return ret;
>> +}
>> +
>> +static void exec_keep_clone_end(struct mm_struct *mm)
>> +{
>> +	spin_lock(&mm->page_table_lock);
>> +	if (!WARN_ON_ONCE(!mm->exec_keep_clones))
>> +		mm->exec_keep_clones--;
>> +	spin_unlock(&mm->page_table_lock);
>> +}
> 
> [Severity: Low]
> This isn't a correctness problem, but is the cost of this accounting
> intended for every spawn?
> 
> exec_keep_clone_begin() in copy_mm() and exec_keep_clone_end() in
> copy_process() each take and release the parent's mm->page_table_lock.
> This happens on every CLONE_VM clone that is not a thread (vfork(),
> posix_spawn(), clone(CLONE_VM)), whether or not the mm has any
> VM_EXEC_KEEP VMA and whether or not an exec is in progress.
> 
> page_table_lock also protects page table operations, so a multithreaded
> parent taking page faults can contend with it.
> 
> Would an atomic counter, or skipping the accounting until the mm has ever
> had MADV_DOEXEC applied, avoid the two lock round trips per spawn?


  Only CLONE_VM clones that are not threads pay them, vfork() and
  posix_spawn() among them, and two uncontended round trips are small
  next to the rest of such a clone. A flag set by the first MADV_DOEXEC
  would remove them; I would leave that for a follow-up if it ever shows
  in a profile.

> 
> [ ... ]
> 

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

  reply	other threads:[~2026-10-21 16:47 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
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 [this message]
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=a353e8c2-2663-431c-b3fb-bdddfd3baaa8@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.