Linux kernel -stable discussions
 help / color / mirror / Atom feed
* [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
       [not found] <20260919004630.1159895-1-donggeunyoo.kernel@gmail.com>
@ 2026-09-19  0:46 ` Donggeun Yoo
  2026-09-22  6:45   ` Mike Rapoport
                     ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Donggeun Yoo @ 2026-09-19  0:46 UTC (permalink / raw)
  To: akpm, rppt
  Cc: peterx, surenb, aarcange, david, ljs, liam, vbabka, mhocko, shuah,
	linux-mm, linux-kselftest, linux-kernel, donggeunyoo.kernel,
	stable

UFFDIO_MOVE on a swapped-out page installs the source PTE at the
destination unchanged, so a uffd bit set on the source lands in a
destination VMA that was never registered for write protection.  It is
then permanent: the bit is dropped only by change_protection() under
MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
and userfaultfd_clear_vma() issue only for a VMA registered in that
mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
refuses the range while the bit is set, because collapse_scan_pmd() is
strict about uffd on swap entries.

move_present_ptes() and move_zeropage_pte() build the destination PTE
from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
the destination's own registration decides the result.  move_swap_pte()
copies the source PTE instead and only ever sets the bit, never clears
it, so one UFFDIO_MOVE behaves differently depending on whether the page
happened to be resident.

Clear the uffd bit on the moved swap entry unless the destination is
RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
into a destination that may not be armed.  A WP-registered destination
stops inheriting the bit as well, which is already what it gets when the
moved page is resident.

Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
Cc: <stable@vger.kernel.org>
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
---
 mm/userfaultfd.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 74f04c323c50..6495666c596b 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
 	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
 	if (userfaultfd_rwp(dst_vma))
 		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
+	else
+		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
 	set_pte_at(mm, dst_addr, dst_pte, orig_src_pte);
 	double_pt_unlock(dst_ptl, src_ptl);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-19  0:46 ` [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte() Donggeun Yoo
@ 2026-09-22  6:45   ` Mike Rapoport
  2026-09-22 12:47     ` Donggeun Yoo
  2026-09-22 11:40   ` David Hildenbrand (Arm)
  2026-09-23 11:18   ` Kiryl Shutsemau
  2 siblings, 1 reply; 11+ messages in thread
From: Mike Rapoport @ 2026-09-22  6:45 UTC (permalink / raw)
  To: Donggeun Yoo
  Cc: akpm, peterx, surenb, aarcange, david, ljs, liam, vbabka, mhocko,
	shuah, linux-mm, linux-kselftest, linux-kernel, stable

Hi,

On Sat, Sep 19, 2026 at 09:46:29AM +0900, Donggeun Yoo wrote:
> UFFDIO_MOVE on a swapped-out page installs the source PTE at the
> destination unchanged, so a uffd bit set on the source lands in a
> destination VMA that was never registered for write protection.  It is
> then permanent: the bit is dropped only by change_protection() under
> MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
> and userfaultfd_clear_vma() issue only for a VMA registered in that
> mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
> refuses the range while the bit is set, because collapse_scan_pmd() is
> strict about uffd on swap entries.
> 
> move_present_ptes() and move_zeropage_pte() build the destination PTE
> from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
> the destination's own registration decides the result.  move_swap_pte()
> copies the source PTE instead and only ever sets the bit, never clears
> it, so one UFFDIO_MOVE behaves differently depending on whether the page
> happened to be resident.
> 
> Clear the uffd bit on the moved swap entry unless the destination is
> RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
> into a destination that may not be armed.  A WP-registered destination
> stops inheriting the bit as well, which is already what it gets when the
> moved page is resident.

This is a hard-to-read wall of text :(

Tell your LLM to better split it into paragraph, not to list every involved
function and in general make it more to the point.
 
> Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
> ---
>  mm/userfaultfd.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> index 74f04c323c50..6495666c596b 100644
> --- a/mm/userfaultfd.c
> +++ b/mm/userfaultfd.c
> @@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
>  	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
>  	if (userfaultfd_rwp(dst_vma))
>  		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
> +	else
> +		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
>  	set_pte_at(mm, dst_addr, dst_pte, orig_src_pte);
>  	double_pt_unlock(dst_ptl, src_ptl);
>  
> -- 
> 2.53.0
> 

-- 
Sincerely yours,
Mike.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-19  0:46 ` [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte() Donggeun Yoo
  2026-09-22  6:45   ` Mike Rapoport
@ 2026-09-22 11:40   ` David Hildenbrand (Arm)
  2026-09-22 12:41     ` Donggeun Yoo
                       ` (2 more replies)
  2026-09-23 11:18   ` Kiryl Shutsemau
  2 siblings, 3 replies; 11+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-22 11:40 UTC (permalink / raw)
  To: Donggeun Yoo, akpm, rppt
  Cc: peterx, surenb, aarcange, ljs, liam, vbabka, mhocko, shuah,
	linux-mm, linux-kselftest, linux-kernel, stable

On 9/19/26 02:46, Donggeun Yoo wrote:
> UFFDIO_MOVE on a swapped-out page installs the source PTE at the
> destination unchanged, so a uffd bit set on the source lands in a
> destination VMA that was never registered for write protection.  It is
> then permanent: the bit is dropped only by change_protection() under
> MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
> and userfaultfd_clear_vma() issue only for a VMA registered in that
> mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
> refuses the range while the bit is set, because collapse_scan_pmd() is
> strict about uffd on swap entries.
> 
> move_present_ptes() and move_zeropage_pte() build the destination PTE
> from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
> the destination's own registration decides the result.  move_swap_pte()
> copies the source PTE instead and only ever sets the bit, never clears
> it, so one UFFDIO_MOVE behaves differently depending on whether the page
> happened to be resident.
> 
> Clear the uffd bit on the moved swap entry unless the destination is
> RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
> into a destination that may not be armed.  A WP-registered destination
> stops inheriting the bit as well, which is already what it gets when the
> moved page is resident.
> 
> Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
> ---
>  mm/userfaultfd.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> index 74f04c323c50..6495666c596b 100644
> --- a/mm/userfaultfd.c
> +++ b/mm/userfaultfd.c
> @@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
>  	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
>  	if (userfaultfd_rwp(dst_vma))
>  		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
> +	else
> +		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
>  	set_pte_at(mm, dst_addr, dst_pte, orig_src_pte);
>  	double_pt_unlock(dst_ptl, src_ptl);
>  

In move_present_ptes() we don't run into that issue as we create a new PTE from
scratch

	orig_dst_pte = folio_mk_pte(src_folio, dst_vma->vm_page_prot);


Staring at the

	if (userfaultfd_rwp(dst_vma))

I do wonder why we don't have to take similar care about wp ... I'm sure the is
a good reason.

Code change itself LGTM.


-- 
Cheers,

David

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-22 11:40   ` David Hildenbrand (Arm)
@ 2026-09-22 12:41     ` Donggeun Yoo
  2026-09-22 16:45     ` Mike Rapoport
  2026-09-23 11:20     ` Kiryl Shutsemau
  2 siblings, 0 replies; 11+ messages in thread
From: Donggeun Yoo @ 2026-09-22 12:41 UTC (permalink / raw)
  To: david
  Cc: akpm, rppt, peterx, surenb, aarcange, ljs, liam, vbabka, mhocko,
	shuah, linux-mm, linux-kselftest, linux-kernel, stable,
	donggeunyoo.kernel

Thank you for the review.

My guess is that wp protection always leaves the destination PTE
non-none, and the move path has

1566	if (!pte_none(orig_dst_pte)) {
1567		ret = -EEXIST;
1568		goto out;
1569	}

The move fails when the destination PTE is not none. So there's nothing
to preserve for wp in the first place.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-22  6:45   ` Mike Rapoport
@ 2026-09-22 12:47     ` Donggeun Yoo
  0 siblings, 0 replies; 11+ messages in thread
From: Donggeun Yoo @ 2026-09-22 12:47 UTC (permalink / raw)
  To: rppt
  Cc: akpm, peterx, surenb, aarcange, david, ljs, liam, vbabka, mhocko,
	shuah, linux-mm, linux-kselftest, linux-kernel, stable,
	donggeunyoo.kernel

Thank you for the review.

I'll rewrite the changelog for readability and add the Assisted-by tag
in v2.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-22 11:40   ` David Hildenbrand (Arm)
  2026-09-22 12:41     ` Donggeun Yoo
@ 2026-09-22 16:45     ` Mike Rapoport
  2026-09-23 11:20     ` Kiryl Shutsemau
  2 siblings, 0 replies; 11+ messages in thread
From: Mike Rapoport @ 2026-09-22 16:45 UTC (permalink / raw)
  To: David Hildenbrand (Arm), Kiryl Shutsemau
  Cc: Donggeun Yoo, akpm, peterx, surenb, aarcange, ljs, liam, vbabka,
	mhocko, shuah, linux-mm, linux-kselftest, linux-kernel, stable

(cc Kiryl)

On Tue, Sep 22, 2026 at 01:40:51PM +0200, David Hildenbrand (Arm) wrote:
> On 9/19/26 02:46, Donggeun Yoo wrote:
> > UFFDIO_MOVE on a swapped-out page installs the source PTE at the
> > destination unchanged, so a uffd bit set on the source lands in a
> > destination VMA that was never registered for write protection.  It is
> > then permanent: the bit is dropped only by change_protection() under
> > MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
> > and userfaultfd_clear_vma() issue only for a VMA registered in that
> > mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
> > refuses the range while the bit is set, because collapse_scan_pmd() is
> > strict about uffd on swap entries.
> > 
> > move_present_ptes() and move_zeropage_pte() build the destination PTE
> > from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
> > the destination's own registration decides the result.  move_swap_pte()
> > copies the source PTE instead and only ever sets the bit, never clears
> > it, so one UFFDIO_MOVE behaves differently depending on whether the page
> > happened to be resident.
> > 
> > Clear the uffd bit on the moved swap entry unless the destination is
> > RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
> > into a destination that may not be armed.  A WP-registered destination
> > stops inheriting the bit as well, which is already what it gets when the
> > moved page is resident.
> > 
> > Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
> > Cc: <stable@vger.kernel.org>
> > Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
> > ---
> >  mm/userfaultfd.c | 2 ++
> >  1 file changed, 2 insertions(+)
> > 
> > diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> > index 74f04c323c50..6495666c596b 100644
> > --- a/mm/userfaultfd.c
> > +++ b/mm/userfaultfd.c
> > @@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
> >  	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
> >  	if (userfaultfd_rwp(dst_vma))
> >  		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
> > +	else
> > +		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
> >  	set_pte_at(mm, dst_addr, dst_pte, orig_src_pte);
> >  	double_pt_unlock(dst_ptl, src_ptl);
> >  
> 
> In move_present_ptes() we don't run into that issue as we create a new PTE from
> scratch
> 
> 	orig_dst_pte = folio_mk_pte(src_folio, dst_vma->vm_page_prot);
> 
> 
> Staring at the
> 
> 	if (userfaultfd_rwp(dst_vma))
> 
> I do wonder why we don't have to take similar care about wp ... I'm sure the is
> a good reason.
> 
> Code change itself LGTM.
> 
> 
> -- 
> Cheers,
> 
> David

-- 
Sincerely yours,
Mike.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-19  0:46 ` [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte() Donggeun Yoo
  2026-09-22  6:45   ` Mike Rapoport
  2026-09-22 11:40   ` David Hildenbrand (Arm)
@ 2026-09-23 11:18   ` Kiryl Shutsemau
  2026-09-25  4:37     ` Donggeun Yoo
  2026-09-25  7:01     ` David Hildenbrand (Arm)
  2 siblings, 2 replies; 11+ messages in thread
From: Kiryl Shutsemau @ 2026-09-23 11:18 UTC (permalink / raw)
  To: Donggeun Yoo
  Cc: akpm, rppt, peterx, surenb, aarcange, david, ljs, liam, vbabka,
	mhocko, shuah, linux-mm, linux-kselftest, linux-kernel, stable

On Sat, Sep 19, 2026 at 09:46:29AM +0900, Donggeun Yoo wrote:
> UFFDIO_MOVE on a swapped-out page installs the source PTE at the
> destination unchanged, so a uffd bit set on the source lands in a
> destination VMA that was never registered for write protection.  It is
> then permanent: the bit is dropped only by change_protection() under
> MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
> and userfaultfd_clear_vma() issue only for a VMA registered in that
> mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
> refuses the range while the bit is set, because collapse_scan_pmd() is
> strict about uffd on swap entries.
> 
> move_present_ptes() and move_zeropage_pte() build the destination PTE
> from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
> the destination's own registration decides the result.  move_swap_pte()
> copies the source PTE instead and only ever sets the bit, never clears
> it, so one UFFDIO_MOVE behaves differently depending on whether the page
> happened to be resident.
> 
> Clear the uffd bit on the moved swap entry unless the destination is
> RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
> into a destination that may not be armed.  A WP-registered destination
> stops inheriting the bit as well, which is already what it gets when the
> moved page is resident.
> 
> Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
> ---
>  mm/userfaultfd.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> index 74f04c323c50..6495666c596b 100644
> --- a/mm/userfaultfd.c
> +++ b/mm/userfaultfd.c
> @@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
>  	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
>  	if (userfaultfd_rwp(dst_vma))
>  		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
> +	else
> +		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);

Putting it in the 'else' is wrong. Clear uffd on the source
unconditionally and re-arm based on the target VMA:

	orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
	if (userfaultfd_rwp(dst_vma))
		orig_src_pte = pte_swp_mkuffd(orig_src_pte);

That is what move_pages_huge_pmd() does with clear_uffd_wp_pmd() before
its own re-arm, and it is the logically correct way to do this: the
source's state is gone, the destination decides.

>  	set_pte_at(mm, dst_addr, dst_pte, orig_src_pte);
>  	double_pt_unlock(dst_ptl, src_ptl);
>  
> -- 
> 2.53.0
> 

-- 
  Kiryl Shutsemau / Kirill A. Shutemov

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-22 11:40   ` David Hildenbrand (Arm)
  2026-09-22 12:41     ` Donggeun Yoo
  2026-09-22 16:45     ` Mike Rapoport
@ 2026-09-23 11:20     ` Kiryl Shutsemau
  2 siblings, 0 replies; 11+ messages in thread
From: Kiryl Shutsemau @ 2026-09-23 11:20 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: Donggeun Yoo, akpm, rppt, peterx, surenb, aarcange, ljs, liam,
	vbabka, mhocko, shuah, linux-mm, linux-kselftest, linux-kernel,
	stable

On Tue, Sep 22, 2026 at 01:40:51PM +0200, David Hildenbrand (Arm) wrote:
> On 9/19/26 02:46, Donggeun Yoo wrote:
> > UFFDIO_MOVE on a swapped-out page installs the source PTE at the
> > destination unchanged, so a uffd bit set on the source lands in a
> > destination VMA that was never registered for write protection.  It is
> > then permanent: the bit is dropped only by change_protection() under
> > MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
> > and userfaultfd_clear_vma() issue only for a VMA registered in that
> > mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
> > refuses the range while the bit is set, because collapse_scan_pmd() is
> > strict about uffd on swap entries.
> > 
> > move_present_ptes() and move_zeropage_pte() build the destination PTE
> > from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
> > the destination's own registration decides the result.  move_swap_pte()
> > copies the source PTE instead and only ever sets the bit, never clears
> > it, so one UFFDIO_MOVE behaves differently depending on whether the page
> > happened to be resident.
> > 
> > Clear the uffd bit on the moved swap entry unless the destination is
> > RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
> > into a destination that may not be armed.  A WP-registered destination
> > stops inheriting the bit as well, which is already what it gets when the
> > moved page is resident.
> > 
> > Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
> > Cc: <stable@vger.kernel.org>
> > Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
> > ---
> >  mm/userfaultfd.c | 2 ++
> >  1 file changed, 2 insertions(+)
> > 
> > diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> > index 74f04c323c50..6495666c596b 100644
> > --- a/mm/userfaultfd.c
> > +++ b/mm/userfaultfd.c
> > @@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
> >  	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
> >  	if (userfaultfd_rwp(dst_vma))
> >  		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
> > +	else
> > +		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
> >  	set_pte_at(mm, dst_addr, dst_pte, orig_src_pte);
> >  	double_pt_unlock(dst_ptl, src_ptl);
> >  
> 
> In move_present_ptes() we don't run into that issue as we create a new PTE from
> scratch
> 
> 	orig_dst_pte = folio_mk_pte(src_folio, dst_vma->vm_page_prot);
> 
> 
> Staring at the
> 
> 	if (userfaultfd_rwp(dst_vma))
> 
> I do wonder why we don't have to take similar care about wp ... I'm sure the is
> a good reason.

The RWP branch does not preserve anything from the source. It arms the
destination whether or not the source had the bit.

WP has nothing to arm. The bit is per-PTE state that userspace sets
explicitly with UFFDIO_WRITEPROTECT or UFFDIO_COPY_MODE_WP. Registration
alone protects nothing, and UFFDIO_MOVE has no MODE_WP flag, so a moved
page in a WP destination starts unprotected, same as a faulted or
copied one.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-23 11:18   ` Kiryl Shutsemau
@ 2026-09-25  4:37     ` Donggeun Yoo
  2026-09-25  7:01     ` David Hildenbrand (Arm)
  1 sibling, 0 replies; 11+ messages in thread
From: Donggeun Yoo @ 2026-09-25  4:37 UTC (permalink / raw)
  To: Kiryl Shutsemau
  Cc: Donggeun Yoo, akpm, rppt, peterx, surenb, aarcange, david, ljs,
	liam, vbabka, mhocko, shuah, linux-mm, linux-kselftest,
	linux-kernel, stable

On Wed, Sep 23, 2026 at 12:18:30PM +0100, Kiryl Shutsemau wrote:
> Putting it in the 'else' is wrong. Clear uffd on the source
> unconditionally and re-arm based on the target VMA:
>
> 	orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
> 	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
> 	if (userfaultfd_rwp(dst_vma))
> 		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
>
> That is what move_pages_huge_pmd() does with clear_uffd_wp_pmd() before
> its own re-arm, and it is the logically correct way to do this: the
> source's state is gone, the destination decides.

Done in v2:
https://lore.kernel.org/all/20260925042907.2330519-1-donggeunyoo.kernel@gmail.com/

Thanks,
Donggeun

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-23 11:18   ` Kiryl Shutsemau
  2026-09-25  4:37     ` Donggeun Yoo
@ 2026-09-25  7:01     ` David Hildenbrand (Arm)
  2026-09-25  8:47       ` Kiryl Shutsemau
  1 sibling, 1 reply; 11+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-25  7:01 UTC (permalink / raw)
  To: Kiryl Shutsemau, Donggeun Yoo
  Cc: akpm, rppt, peterx, surenb, aarcange, ljs, liam, vbabka, mhocko,
	shuah, linux-mm, linux-kselftest, linux-kernel, stable

On 9/23/26 13:18, Kiryl Shutsemau wrote:
> On Sat, Sep 19, 2026 at 09:46:29AM +0900, Donggeun Yoo wrote:
>> UFFDIO_MOVE on a swapped-out page installs the source PTE at the
>> destination unchanged, so a uffd bit set on the source lands in a
>> destination VMA that was never registered for write protection.  It is
>> then permanent: the bit is dropped only by change_protection() under
>> MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
>> and userfaultfd_clear_vma() issue only for a VMA registered in that
>> mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
>> refuses the range while the bit is set, because collapse_scan_pmd() is
>> strict about uffd on swap entries.
>>
>> move_present_ptes() and move_zeropage_pte() build the destination PTE
>> from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
>> the destination's own registration decides the result.  move_swap_pte()
>> copies the source PTE instead and only ever sets the bit, never clears
>> it, so one UFFDIO_MOVE behaves differently depending on whether the page
>> happened to be resident.
>>
>> Clear the uffd bit on the moved swap entry unless the destination is
>> RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
>> into a destination that may not be armed.  A WP-registered destination
>> stops inheriting the bit as well, which is already what it gets when the
>> moved page is resident.
>>
>> Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
>> Cc: <stable@vger.kernel.org>
>> Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
>> ---
>>  mm/userfaultfd.c | 2 ++
>>  1 file changed, 2 insertions(+)
>>
>> diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
>> index 74f04c323c50..6495666c596b 100644
>> --- a/mm/userfaultfd.c
>> +++ b/mm/userfaultfd.c
>> @@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
>>  	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
>>  	if (userfaultfd_rwp(dst_vma))
>>  		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
>> +	else
>> +		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
> 
> Putting it in the 'else' is wrong. Clear uffd on the source
> unconditionally and re-arm based on the target VMA:
> 
> 	orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
> 	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
> 	if (userfaultfd_rwp(dst_vma))
> 		orig_src_pte = pte_swp_mkuffd(orig_src_pte);

I'm confused why the original code proposed would be "wrong"?

I prefer your way of writing it as well, but I don't understand why it would be
"wrong"?

Maybe I need more coffee :)

-- 
Cheers,

David

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte()
  2026-09-25  7:01     ` David Hildenbrand (Arm)
@ 2026-09-25  8:47       ` Kiryl Shutsemau
  0 siblings, 0 replies; 11+ messages in thread
From: Kiryl Shutsemau @ 2026-09-25  8:47 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: Donggeun Yoo, akpm, rppt, peterx, surenb, aarcange, ljs, liam,
	vbabka, mhocko, shuah, linux-mm, linux-kselftest, linux-kernel,
	stable

On Fri, Sep 25, 2026 at 09:01:22AM +0200, David Hildenbrand (Arm) wrote:
> On 9/23/26 13:18, Kiryl Shutsemau wrote:
> > On Sat, Sep 19, 2026 at 09:46:29AM +0900, Donggeun Yoo wrote:
> >> UFFDIO_MOVE on a swapped-out page installs the source PTE at the
> >> destination unchanged, so a uffd bit set on the source lands in a
> >> destination VMA that was never registered for write protection.  It is
> >> then permanent: the bit is dropped only by change_protection() under
> >> MM_CP_UFFD_{WP,RWP}_RESOLVE, which uffd_wp_range(), mrwprotect_range()
> >> and userfaultfd_clear_vma() issue only for a VMA registered in that
> >> mode.  pagemap reports the page as uffd-tracked, and MADV_COLLAPSE
> >> refuses the range while the bit is set, because collapse_scan_pmd() is
> >> strict about uffd on swap entries.
> >>
> >> move_present_ptes() and move_zeropage_pte() build the destination PTE
> >> from dst_vma->vm_page_prot and arm RWP only when dst_vma asks for it, so
> >> the destination's own registration decides the result.  move_swap_pte()
> >> copies the source PTE instead and only ever sets the bit, never clears
> >> it, so one UFFDIO_MOVE behaves differently depending on whether the page
> >> happened to be resident.
> >>
> >> Clear the uffd bit on the moved swap entry unless the destination is
> >> RWP-registered, as copy_nonpresent_pte() does where it installs a PTE
> >> into a destination that may not be armed.  A WP-registered destination
> >> stops inheriting the bit as well, which is already what it gets when the
> >> moved page is resident.
> >>
> >> Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI")
> >> Cc: <stable@vger.kernel.org>
> >> Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
> >> ---
> >>  mm/userfaultfd.c | 2 ++
> >>  1 file changed, 2 insertions(+)
> >>
> >> diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
> >> index 74f04c323c50..6495666c596b 100644
> >> --- a/mm/userfaultfd.c
> >> +++ b/mm/userfaultfd.c
> >> @@ -1452,6 +1452,8 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
> >>  	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
> >>  	if (userfaultfd_rwp(dst_vma))
> >>  		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
> >> +	else
> >> +		orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
> > 
> > Putting it in the 'else' is wrong. Clear uffd on the source
> > unconditionally and re-arm based on the target VMA:
> > 
> > 	orig_src_pte = pte_swp_clear_uffd(orig_src_pte);
> > 	/* Re-arm RWP on the moved swap entry if dst_vma is RWP-registered. */
> > 	if (userfaultfd_rwp(dst_vma))
> > 		orig_src_pte = pte_swp_mkuffd(orig_src_pte);
> 
> I'm confused why the original code proposed would be "wrong"?
> 
> I prefer your way of writing it as well, but I don't understand why it would be
> "wrong"?
> 
> Maybe I need more coffee :)

It gives the right result, but communicates wrong reasoning to the reader.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-09-25  8:47 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260919004630.1159895-1-donggeunyoo.kernel@gmail.com>
2026-09-19  0:46 ` [PATCH v1 1/2] userfaultfd: clear the inherited uffd bit in move_swap_pte() Donggeun Yoo
2026-09-22  6:45   ` Mike Rapoport
2026-09-22 12:47     ` Donggeun Yoo
2026-09-22 11:40   ` David Hildenbrand (Arm)
2026-09-22 12:41     ` Donggeun Yoo
2026-09-22 16:45     ` Mike Rapoport
2026-09-23 11:20     ` Kiryl Shutsemau
2026-09-23 11:18   ` Kiryl Shutsemau
2026-09-25  4:37     ` Donggeun Yoo
2026-09-25  7:01     ` David Hildenbrand (Arm)
2026-09-25  8:47       ` Kiryl Shutsemau

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox