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: <[email protected]>
>> Signed-off-by: Donggeun Yoo <[email protected]>
>> ---
>>  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

Reply via email to