From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail.openvz.org (unknown [69.168.225.77]) by lore.virtuozzo.com (Postfix) with ESMTPS id 8F8B38017D for ; Wed, 14 Oct 2026 17:13:06 +0000 (UTC) Received: from mail.openvz.org (localhost [127.0.0.1]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69EHBpGP001290; Wed, 14 Oct 2026 20:11:52 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHBpGP001290 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="wiHRT9fQ" Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69EHBIQD001193 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 14 Oct 2026 20:11:19 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHBIQD001193 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-4a16fefb7acso34611265e9.0 for ; Wed, 14 Oct 2026 10:11:19 -0700 (PDT) X-Gm-Message-State: AFq9FYLD+R8AxF/S3LOJ2IlM4JUTtdny1rabVGQl9hYdpZzX1yjPZeN4 193NcvOzeGlzowJSwAmThd9Tow/bvzPOPlIA3K6Gn1bX7SrMEkXT7CCWm9Ouqk2DYkpGpTa1W6d B1eVxVzhVLrNlmFLxYxnqBN2eZz1ByAfuVftqZLhluzD9632ARuUkTw== X-Gm-Gg: AYBFou1fqOn1k3/bdQxB83+HVM5aT7scF7DyfQEV8W328caC1fAZ+YGkHUqp0IvUVtJ FZayrASoCIwPUkIgeyoTaA8B2OHAuFJjhpxfX9/tOaPqiZr3XjuvhiGUf8DmUOazzF627LwhwDY zwCaIcEiZrm3u1DfdFOyXH+iUCU+zh6JsUq5pSjcREpWZ3h6J9qyxNa+NH0biS2m+mo17WtpYek OLff/imr3JewZeiqd4En9N0iNrGUo1skPmIuDAAYPDPjsDehdEr6wflfZEwgC7fhGWZxNJ0fpo0 v7KxJ6XOi/VEC744rcjnorYwVMaz65zaKdZ5B0IduZ51u0F0SKab66nM8qAVGCUSvMJRegTakY4 b514EQCY3INnYb4+jmA== X-Received: by 2002:a05:600c:4f86:b0:4a1:acaa:c355 with SMTP id 5b1f17b1804b1-4a1acaac488mr128129785e9.10.1791997878569; Wed, 14 Oct 2026 10:11:18 -0700 (PDT) X-Received: by 2002:a05:600c:4f86:b0:4a1:acaa:c355 with SMTP id 5b1f17b1804b1-4a1acaac488mr128129185e9.10.1791997878013; Wed, 14 Oct 2026 10:11:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791997878; cv=none; d=google.com; s=arc-20260327; b=OmnCVLsKcxsLQKkckmrZvP2N+MtrYvo8qjfCZfIiV4kjDIVXFy0DI9nTJ6dEoW7jEe /5I6sqzB6/gQGjWJkiZhHLEmtP2xCt/OPJMVFU89OvpgZdQal4JRuEi/k1NyU3MQxHPT Fn/C2G2IxLC6wxa60sLOR/Du02991wtHPM5pGBxwYkGV7WDrCFZOJU0OEqa+LpwdqkUm M6N1WDjFQKFPreebyEV/GHF8mRZCCZBqUk06I4zQWASrDiaQv6gkpl9fXtI4lwVWMcFq 1mgUxgNCy7Sjle2oM2qMWl/HNYb4wKWgVHgT4dg9khF5rBXzGCFMsoO8OSCDpbkWuWsZ yZig== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:dkim-signature; bh=web3FXuPZCZ+EfmkjGcxjxizVojhaeWF0t/GiT5hBbQ=; fh=aQfw1i5a4NQPWqzJLDKkrMTvdRoUn/cswL6yDWdmKfA=; b=bWWHkScyoSs1SbDl6a+3PZIDc5LdHKwdZ0S6cR7FgZ4//ScP0tnOQgOuGz6D54fQv8 Ie9kwxpWV3OjV1cnl1OxiIaSCvs5wFUV6O+lXy88SUfTWXMa/wFC3chikyUKPPIywDnU ceiA+UITJ50pRvQ5Yw10leHlFA5jH48GmEL5uajrSHlAFh3N1WxL5pRoe2MTQK8ax2N4 J+ccRzJd3LkrSVdSq/JwXbB9g40mBew5ML3NlNMg5/RoC8tTykx6M1Dpll4lrKaV5l4M TV3eoESN6YYVSWPGCJluiDuMssTODhw/s4GEX+8/Y8MUDEP7EoRTkN0SmR4cp+jzICxA z5rw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=wiHRT9fQ; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com Received: from relay.virtuozzo.com (relay.virtuozzo.com. [130.117.225.111]) by mx.google.com with ESMTPS id 5b1f17b1804b1-4a1bd9850cfsi4437325e9.8.2026.10.14.10.11.17 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Oct 2026 10:11:17 -0700 (PDT) Received-SPF: pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) client-ip=130.117.225.111; Authentication-Results: mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=wiHRT9fQ; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=Message-Id:Content-Type:MIME-Version:Subject:Date :From; bh=web3FXuPZCZ+EfmkjGcxjxizVojhaeWF0t/GiT5hBbQ=; b=wiHRT9fQ3u5U1pibtrA zruH5NXTMyXsjaMmv+vqUByfWNUKG0Eg5X3UcoIj8GBSjMJgsrQptetOu2VEdYj8mVKSgzFbqBRp9 rZisq3r8GFGHzgsyOQ6B/secRhjwfWajRLTdlsjg/4Q0hZE9R2sIwouPTo0NrBOIH/ONDp4Ey08iv JFlaGqPUIh5hYubPKd3D56WuArizm5ZzMjo6AWZLADYn0Zh8w/Mwg03gWCZ6VC89cmgZSOzHjND4+ CtCLGr56UhIvoKP864z1SaZ+JkplVEX3cOI2FsjH3xxpG60WgrSWXQU3LwlC4oJNUUwSPUo6sjKlk LtWpeC9deNAt92Q==; Received: from ch-demo-asa.virtuozzo.com ([130.117.225.8] helo=f0.sw.ru) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1xH2S8-0081J5-1a; Wed, 14 Oct 2026 19:11:16 +0200 From: Konstantin Khorenko Date: Wed, 14 Oct 2026 19:11:10 +0200 MIME-Version: 1.0 Message-Id: <20261014-b4-prep-khorenko-vstor-149847-2-doexec-pinned-memory-v1-7-36f1120f9b57@virtuozzo.com> References: <20261014-b4-prep-khorenko-vstor-149847-2-doexec-pinned-memory-v1-0-36f1120f9b57@virtuozzo.com> In-Reply-To: <20261014-b4-prep-khorenko-vstor-149847-2-doexec-pinned-memory-v1-0-36f1120f9b57@virtuozzo.com> To: Vasileios Almpanis , Andrey Drobyshev , Pavel Tikhomirov X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10060; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=axaUGuJBSMyutXMFZ4DdJTqa78z8Jzot65Qw46WVOwo=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBqz7eypiN9XYXc1mESegqzlksxm4OSqYBcbbp2W 8NePB91kTOJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCas+3sgAKCRBRlgpH+WFc Kg+VEACuVkXi/TaE2gI/6Valrt6OJFkF3AgCUNksMUY6Uo8k1O6ds8gXMrdoL0Zu6SEsLNjb3Rf /0m3dWVXmMthO2774744BkaR4lLRLvJZNfq21ZAYL2cUC37aA+sT+tQyHbqHCP3dq0W2EhSD2Qz w2p9uuZoxZgTNIEEz3Psy175rKPZVEZPBfWEsGj92yMc2is/VKxesTTvcbU5F85v5ey9OZ4pkl3 WwxRmWkMzd4nak0Zklx1reNsJQ0A3mgPilpqcJnYhyrpX4p4ljjB6Akk2jpE4f+4bEjXWMjJlpL DTqOG2iNwq8b8BCozOAomtP77d5DNViYu1Flx7/jE7afo38UcWAKhivE1il356WaBBoaMgJVxTE DlLh4Jx5wDk8QuGJIp9vnOlgmWPJJpbtp4ujLF7vYf/v4C8FlozxPOZEGXnF+GB1IfrX8PImkQh vlhDFdKBooxR1BGy4h8B7Jr94RZ9qyUNtk1sfJkUM5+XPQALyNQ7IsovmjFd3FfHlYUXJgxETHr 7Dlkhjpti6PxqJDXAI3pX+bpGAotjo7E6WKD/WYzVZCCFSDhxjEDvQOAFXCi71g7XdO3Oq/pUC9 TmOIsYunZ0JkC/RrpHFbYPODOKj1wWhUjKmC3Gsa+eT2sN29dCTbjKu951bT7p6X1dMzFAnuVr5 mf3BrSvr5bB1eLg== X-Developer-Key: i=khorenko@virtuozzo.com; a=openpgp; fpr=560F6DC83DC5D3D063BD082C51960A47F9615C2A X-OZ-Fwd: true Cc: devel@openvz.org Subject: [Devel] [PATCH hci-8.1 7/8] exec: protect preserved memory against surviving shared-mm tasks X-BeenThere: devel@openvz.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: OpenVZ development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: devel-bounces@openvz.org Errors-To: devel-bounces@openvz.org From: Andrey Drobyshev exec_mmap() copies the MADV_DOEXEC ranges into the new mm and then zaps them in the old one, so that the copies are the only mappings left. de_thread() removes the thread group, but a task sharing the mm via CLONE_VM without CLONE_THREAD survives exec. It races with the copy, writing into pages the new image takes over, and afterwards finds the ranges empty and silently goes on with fresh memory. Refuse such an exec before the point of no return: - begin_new_exec(): when the binary accepts preserved memory and the mm has a VM_EXEC_KEEP VMA, mark the mm as being preserved and return EBUSY if a task outside the thread group has it as its mm. Sibling threads, which de_thread() removes, and plain mm references such as /proc readers are allowed. - copy_mm(): refuse a CLONE_VM clone that is not a thread while the mm is marked, and count such clones in flight until the child is on the task list, so the check above cannot miss one. The check walks the task list under RCU; it sees every child whose count was dropped, since the drop follows the insertion and both are ordered by page_table_lock. - exec_mmap() and free_bprm(): clear the mark once the mm is switched or the exec fails. - exec_mmap(): take the preserve path, including the source mmap write lock, only when the mark was set (bprm->preserving_mem), so an accepting binary without a VM_EXEC_KEEP VMA takes only the read lock, as an exec without preserved memory does. A kernel thread running on the mm through kthread_use_mm() is outside the thread group as well, so it makes the exec fail the same way. This covers a vhost worker in kthread mode, the default here, for as long as its device has an owner: the caller has to reset the owner first, as the worker would otherwise go on writing guest memory into the old mm. A kernel thread that takes the mm only after the check, for instance to complete an AIO or fence request made before the exec, is not refused: a write before the zap of the old ranges reaches the preserved pages, as one made just before the exec would, and a later one lands in fresh memory of the old mm and is lost, as after any exec. Fixes: 37d9f55655ea ("oracle/mm: do not write protect COW mappings when preserving across exec") https://virtuozzo.atlassian.net/browse/VSTOR-150231 Feature: oracle/mm: MADV_DOEXEC madvise() flag Signed-off-by: Andrey Drobyshev [khorenko: rationale after the zap of the old ranges, count only clones that are not threads, walk the task list under RCU, keep the upstream begin_new_exec() comment, note kthread_use_mm() users] Assisted-by: LLM Signed-off-by: Konstantin Khorenko --- fs/exec.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++-- include/linux/binfmts.h | 4 ++- include/linux/mm_types.h | 4 +++ kernel/fork.c | 37 ++++++++++++++++++++++ 4 files changed, 124 insertions(+), 3 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 32567010a2f27..5bf856e829af3 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -978,6 +978,78 @@ ssize_t read_code(struct file *file, unsigned long addr, loff_t pos, size_t len) EXPORT_SYMBOL(read_code); #endif +/* The reservation owns no mm reference; release it before dropping old_mm. */ +static void release_preserved_mem(struct linux_binprm *bprm, struct mm_struct *mm) +{ + if (!bprm->preserving_mem) + return; + spin_lock(&mm->page_table_lock); + mm->exec_keep_active = false; + spin_unlock(&mm->page_table_lock); + bprm->preserving_mem = false; +} + +/* + * Reserve the marked address space before exec becomes irreversible. Tasks + * outside our thread group survive de_thread() and would find the preserved + * ranges empty once exec_mmap() zaps them in this mm. + */ +static int reserve_preserved_mem(struct linux_binprm *bprm) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + struct task_struct *p, *t; + bool preserve = false; + int ret = 0; + + if (!bprm->accepts_preserved_mem || !mm) + return 0; + + if (mmap_read_lock_killable(mm)) + return -EINTR; + { + VMA_ITERATOR(vmi, mm, 0); + + for_each_vma(vmi, vma) { + if (vma->vm_flags & VM_EXEC_KEEP) { + preserve = true; + break; + } + } + } + mmap_read_unlock(mm); + if (!preserve) + return 0; + + /* A clone is either counted here or already visible in the task list. */ + spin_lock(&mm->page_table_lock); + if (mm->exec_keep_active || mm->exec_keep_clones) + ret = -EBUSY; + else + mm->exec_keep_active = true; + spin_unlock(&mm->page_table_lock); + if (ret) + return ret; + bprm->preserving_mem = true; + + rcu_read_lock(); + for_each_process_thread(p, t) { + if (same_thread_group(current, t)) + continue; + task_lock(t); + if (t->mm == mm) + ret = -EBUSY; + task_unlock(t); + if (ret) + goto out; + } +out: + rcu_read_unlock(); + if (ret) + release_preserved_mem(bprm, mm); + return ret; +} + static int vma_dup_some(struct mm_struct *old_mm, struct mm_struct *new_mm) { struct vm_area_struct *vma; @@ -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)) { /* * Acquire the write lock to avoid changes to page * table entries while they are copied. @@ -1085,13 +1157,14 @@ static int exec_mmap(struct linux_binprm *bprm) task_unlock(tsk); lru_gen_use_mm(mm); if (old_mm) { - if (unlikely(bprm->accepts_preserved_mem)) + if (unlikely(bprm->preserving_mem)) mmap_write_unlock(old_mm); else mmap_read_unlock(old_mm); BUG_ON(active_mm != old_mm); setmax_mm_hiwater_rss(&tsk->signal->maxrss, old_mm); mm_update_next_owner(old_mm); + release_preserved_mem(bprm, old_mm); mmput(old_mm); return 0; } @@ -1300,6 +1373,10 @@ int begin_new_exec(struct linux_binprm * bprm) if (retval) return retval; + retval = reserve_preserved_mem(bprm); + if (retval) + return retval; + /* * This tracepoint marks the point before flushing the old exec where * the current task is still unchanged, but errors are fatal (point of @@ -1552,6 +1629,7 @@ static void do_close_execat(struct file *file) static void free_bprm(struct linux_binprm *bprm) { + release_preserved_mem(bprm, current->mm); if (bprm->mm) { acct_arg_size(bprm, 0); mmput(bprm->mm); diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 9f03c39498e92..f42487c043178 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -47,7 +47,9 @@ struct linux_binprm { * Set if the binary being exec'd will accept memory marked * for preservation by the outgoing process. */ - accepts_preserved_mem:1; + accepts_preserved_mem:1, + /* Outgoing mm has VM_EXEC_KEEP VMAs and is reserved for exec. */ + preserving_mem:1; #ifdef __alpha__ unsigned int taso:1; #endif diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index cf50c74516b09..befdf38ef855b 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -994,6 +994,10 @@ struct mm_struct { unsigned long flags; /* Must use atomic bitops to access */ unsigned int vps_dumpable:2; + + /* Shared-mm clone publication, serialized by page_table_lock. */ + unsigned int exec_keep_clones; + bool exec_keep_active; #ifdef CONFIG_AIO spinlock_t ioctx_lock; struct kioctx_table __rcu *ioctx_table; diff --git a/kernel/fork.c b/kernel/fork.c index 00d9b4febd85b..92a7e9aae9278 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1270,6 +1270,8 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, mt_init_flags(&mm->mm_mt, MM_MT_FLAGS); mt_set_external_lock(&mm->mm_mt, &mm->mmap_lock); atomic_set(&mm->mm_users, 1); + mm->exec_keep_clones = 0; + mm->exec_keep_active = false; atomic_set(&mm->mm_count, 1); seqcount_init(&mm->write_protect_seq); mmap_init_lock(mm); @@ -1710,9 +1712,32 @@ static struct mm_struct *dup_mm(struct task_struct *tsk, return NULL; } +/* 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); +} + static int copy_mm(unsigned long clone_flags, struct task_struct *tsk) { struct mm_struct *mm, *oldmm; + int ret; tsk->min_flt = tsk->maj_flt = 0; tsk->nvcsw = tsk->nivcsw = 0; @@ -1734,6 +1759,12 @@ static int copy_mm(unsigned long clone_flags, struct task_struct *tsk) return 0; if (clone_flags & CLONE_VM) { + /* de_thread() takes care of our own threads. */ + if (!(clone_flags & CLONE_THREAD)) { + ret = exec_keep_clone_begin(oldmm); + if (ret) + return ret; + } mmget(oldmm); mm = oldmm; } else { @@ -2638,6 +2669,10 @@ __latent_entropy struct task_struct *copy_process( syscall_tracepoint_update(p); write_unlock_irq(&tasklist_lock); + /* Admission can now find this shared-mm task in the task list. */ + if ((clone_flags & (CLONE_VM | CLONE_THREAD)) == CLONE_VM && p->mm) + exec_keep_clone_end(p->mm); + if (pidfile) fd_install(pidfd, pidfile); @@ -2679,6 +2714,8 @@ __latent_entropy struct task_struct *copy_process( exit_ve_namespace(p); bad_fork_cleanup_mm: if (p->mm) { + if ((clone_flags & (CLONE_VM | CLONE_THREAD)) == CLONE_VM) + exec_keep_clone_end(p->mm); mm_clear_owner(p->mm, p); mmput(p->mm); } -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel