From: "Jose A. Perez de Azpillaga" <azpijr@gmail.com>
To: "Mike Rapoport (Microsoft)" <rppt@kernel.org>,
Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Xu <peterx@redhat.com>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
Subject: Re: [PATCH] userfaultfd: clear uffd state in child PTEs if uffd is released during fork
Date: Sat, 31 Oct 2026 14:40:16 +0100 [thread overview]
Message-ID: <2c132a52-1118-4773-866b-a8c713c4933e@gmail.com> (raw)
In-Reply-To: <20261031-uffd-prot-bits-fork-event-v1-1-3572921c2111@kernel.org>
On 10/31/26 1:35 PM, Mike Rapoport (Microsoft) wrote:
> With UFFD_FEATURE_EVENT_FORK the userfaultfd context is duplicated for the
> child process, the child VMAs are registered with the new context, and uffd
> protection state from the parent PTEs is copied into the child.
>
> If the parent's userfaultfd is released before the fork event is read,
> userfaultfd_release_new() resets the VMA flags but keeps uffd state in the
> child PTEs.
>
> This leaves the child's VMAs in an inconsistent state: they are not
> registered with userfaultfd but their mappings carry uffd protection state.
>
> If the child registers the range with userfaultfd again, the stale state
> turns into protection the child never asked for.
>
> When a write fault makes such a PTE writable without copying the page, the
> uffd bit remains set and page_table_check_pte_flags() warns about it.
>
> Split resolving of the uffd PTE state out of userfaultfd_clear_vma() and
> call it from userfaultfd_release_new() before resetting the VMA.
>
> Fixes: f369b07c8614 ("mm/uffd: reset write protection when unregister with wp-mode")
> Cc: stable@vger.kernel.org
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> ---
> mm/userfaultfd.c | 43 ++++++++++++++++++++++++++-----------------
> 1 file changed, 26 insertions(+), 17 deletions(-)
>
> diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> index 3b71712edab4c..2c11ac5dad3d5 100644
> --- a/mm/userfaultfd.c
> +++ b/mm/userfaultfd.c
> @@ -2220,6 +2220,28 @@ static void userfaultfd_reset_ctx(struct vm_area_struct *vma)
> userfaultfd_set_ctx(vma, NULL, 0);
> }
>
> +/* Clear the uffd bit and/or restore protnone PTEs */
> +static void userfaultfd_clear_ptes(struct vm_area_struct *vma,
> + unsigned long start, unsigned long end)
> +{
> + unsigned int mm_cp_flags = 0;
> + struct mmu_gather tlb;
> +
> + if (!userfaultfd_protected(vma))
> + return;
> +
> + if (userfaultfd_wp(vma))
> + mm_cp_flags |= MM_CP_UFFD_WP_RESOLVE;
> + if (userfaultfd_rwp(vma))
> + mm_cp_flags |= MM_CP_UFFD_RWP_RESOLVE;
> + if (vma_wants_manual_pte_write_upgrade(vma))
> + mm_cp_flags |= MM_CP_TRY_CHANGE_WRITABLE;
> +
> + tlb_gather_mmu(&tlb, vma->vm_mm);
> + change_protection(&tlb, vma, start, end, mm_cp_flags);
> + tlb_finish_mmu(&tlb);
> +}
> +
> static struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi,
> struct vm_area_struct *prev,
> struct vm_area_struct *vma,
> @@ -2239,22 +2261,7 @@ static struct vm_area_struct *userfaultfd_clear_vma(struct vma_iterator *vmi,
> if (start == vma->vm_start && end == vma->vm_end)
> give_up_on_oom = true;
>
> - /* Clear the uffd bit and/or restore protnone PTEs */
> - if (userfaultfd_protected(vma)) {
> - unsigned int mm_cp_flags = 0;
> - struct mmu_gather tlb;
> -
> - if (userfaultfd_wp(vma))
> - mm_cp_flags |= MM_CP_UFFD_WP_RESOLVE;
> - if (userfaultfd_rwp(vma))
> - mm_cp_flags |= MM_CP_UFFD_RWP_RESOLVE;
> - if (vma_wants_manual_pte_write_upgrade(vma))
> - mm_cp_flags |= MM_CP_TRY_CHANGE_WRITABLE;
> -
> - tlb_gather_mmu(&tlb, vma->vm_mm);
> - change_protection(&tlb, vma, start, end, mm_cp_flags);
> - tlb_finish_mmu(&tlb);
> - }
> + userfaultfd_clear_ptes(vma, start, end);
>
> ret = vma_modify_flags_uffd(vmi, prev, vma, start, end,
> &new_vma_flags, NULL_VM_UFFD_CTX,
> @@ -2353,8 +2360,10 @@ static void userfaultfd_release_new(struct userfaultfd_ctx *ctx)
> /* the various vma->vm_userfaultfd_ctx still points to it */
> mmap_write_lock(mm);
> for_each_vma(vmi, vma) {
> - if (vma->vm_userfaultfd_ctx.ctx == ctx)
> + if (vma->vm_userfaultfd_ctx.ctx == ctx) {
> + userfaultfd_clear_ptes(vma, vma->vm_start, vma->vm_end);
> userfaultfd_reset_ctx(vma);
> + }
> }
> mmap_write_unlock(mm);
> }
>
reproduced the leftover bits in the child with wp and rwp on all memory
types, and they're gone with this. tested on x86_64 in qemu. LGTM so:
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
Tested-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
--
cheers,
jose a. p-a
next prev parent reply other threads:[~2026-10-31 13:40 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 12:35 Mike Rapoport (Microsoft)
2026-10-31 13:40 ` Jose A. Perez de Azpillaga [this message]
2026-10-31 14:58 ` Tal Zussman
2026-10-31 15:09 ` Mike Rapoport
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=2c132a52-1118-4773-866b-a8c713c4933e@gmail.com \
--to=azpijr@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=peterx@redhat.com \
--cc=rppt@kernel.org \
--cc=stable@vger.kernel.org \
/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
all inboxes | Powered by JetHome®