userfaultfd/pagemap: pre-existing fixes
Subsystems (8)
Kiryl Shutsemau (Meta)
reviewercover userfaultfd/pagemap: pre-existing fixes
These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this.
All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review <[email protected]>. They are independent of RWP, apply to mm-new directly, and carry Cc: stable@.
1: fs/proc/task_mmu: a missing huge_ptep_modify_prot_start() in
make_uffd_wp_huge_pte() can lose hardware Dirty/Accessed updates when PAGEMAP_SCAN write-protects a hugetlb PTE.
2: fs/proc/task_mmu: pagemap_scan_hugetlb_entry() compares the range
against HPAGE_SIZE rather than the hstate page size, so it never write-protects gigantic hugetlb pages.
3: fs/proc/task_mmu: PAGEMAP_SCAN with PM_SCAN_WP_MATCHING over an
unpopulated hugetlb range self-deadlocks -- pagemap_scan_pte_hole() calls uffd_wp_range() while walk_hugetlb_range() holds the hugetlb vma lock for read, and hugetlb_change_protection() then takes it for write. Install the marker inline instead.
4: mm/huge_memory: change_non_present_huge_pmd() drops pmd_swp_uffd_wp
on a device-private PMD permission downgrade, silently losing the uffd-wp marker.
5: userfaultfd: must_wait() applies pte_write() to a locklessly read
PTE without checking pte_present(), so swap/migration entries decode random offset bits and a thread can stay parked on a stale fault.
6: userfaultfd: __VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT (41) to
mk_vma_flags() unconditionally, an out-of-bounds write into the single-word vma_flags_t on 32-bit. Build the mask from config-gated per-mode masks so an unavailable bit is never materialised.
[1] https://lore.kernel.org/all/[email protected]/
Kiryl Shutsemau (Meta) (6):
fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry() fs/proc/task_mmu: fix hugetlb self-deadlock in pagemap_scan_pte_hole() mm/huge_memory: preserve pmd_swp_uffd_wp on device-private PMD
downgrade
userfaultfd: gate must_wait writability check on pte_present()
userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks
fs/proc/task_mmu.c | 73 ++++++++++++++++++++++++++++++++--- include/linux/mm.h | 39 +++++++++++++++++++ include/linux/userfaultfd_k.h | 4 +- mm/huge_memory.c | 2 + mm/userfaultfd.c | 20 ++++++++++ 5 files changed, 130 insertions(+), 8 deletions(-)
base-commit: 449a5df98f8dffa9b037e3b6838fc5af327df072
Signature · 1 line
2.54.0
Kiryl Shutsemau (Meta)
reviewerPATCH 1/6 fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race
make_uffd_wp_huge_pte() arms the UFFD_WP bit on a present HugeTLB PTE by calling huge_ptep_modify_prot_commit() with a ptent snapshot that was fetched without the corresponding huge_ptep_modify_prot_start(). The start helper is what atomically clears the entry so the kernel-owned snapshot stays consistent until the commit; without it, the hardware may set Dirty or Accessed in the live PTE between the original read and the commit, and huge_ptep_modify_prot_commit() (whose generic implementation just calls set_huge_pte_at()) then writes the stale snapshot back over the live hardware bits, losing the update.
The non-hugetlb sibling make_uffd_wp_pte() does this correctly via ptep_modify_prot_start() / ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against.
--- fs/proc/task_mmu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
fs/proc/task_mmu.c +8 -4
@@ -2610,12 +2610,16 @@ static void make_uffd_wp_huge_pte(struct vm_area_struct *vma,if (softleaf_is_hwpoison (entry) ||softleaf_is_marker (entry))return ; -if (softleaf_is_migration (entry)) +if (softleaf_is_migration (entry)) {set_huge_pte_at (vma->vm_mm, addr, ptep,pte_swp_mkuffd_wp (ptent), psize); -else -huge_ptep_modify_prot_commit (vma, addr, ptep, ptent, - huge_pte_mkuffd_wp(ptent)); + }else { +pte_t old_pte, new_pte; + + old_pte =huge_ptep_modify_prot_start (vma, addr, ptep); + new_pte =huge_pte_mkuffd_wp (old_pte); +huge_ptep_modify_prot_commit (vma, addr, ptep, old_pte, new_pte); + } }#endif /* CONFIG_HUGETLB_PAGE */
Signature · 1 line
2.54.0
Re: [PATCH 1/6] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race
Quoted 3 lines · Kiryl Shutsemau (Meta)
make_uffd_wp_huge_pte()arms theUFFD_WPbit on a present HugeTLB PTE by callinghuge_ptep_modify_prot_commit()with a ptent snapshot that was fetched without the correspondinghuge_ptep_modify_prot_start().
The
Quoted 6 lines
start helper is what atomically clears the entry so the kernel-owned snapshot stays consistent until the commit; without it, the hardware may set Dirty or Accessed in the live PTE between the original read and the commit, and
huge_ptep_modify_prot_commit()(whose generic implementation just callsset_huge_pte_at()) then writes the stale snapshot back over the live hardware bits, losing the update.
Very pedantic nit - this is a mammoth paragraph :P maybe add a linebreak where I did above?
Quoted 8 lines
The non-hugetlb sibling
make_uffd_wp_pte()does this correctly viaptep_modify_prot_start()/ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against.Fixes 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")
I wonder if better as a separate patch? Not sure what Andrew thinks about it, I do recall him complaining about separate-hotfixes-as-part-of-a-series :>)
Quoted 3 lines
Reported-by Sashiko AI review <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>
LGTM, so:
Quoted 30 lines
--- fs/proc/task_mmu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)fs/proc/task_mmu.c +8
-5@@ -2610,12 +2610,16 @@ static void make_uffd_wp_huge_pte(struct vm_area_struct *vma,if (softleaf_is_hwpoison (entry) ||softleaf_is_marker (entry))return ; -if (softleaf_is_migration (entry)) +if (softleaf_is_migration (entry)) {set_huge_pte_at (vma->vm_mm, addr, ptep,pte_swp_mkuffd_wp (ptent), psize); -else -huge_ptep_modify_prot_commit (vma, addr, ptep, ptent, -huge_pte_mkuffd_wp (ptent)); + }else { +pte_t old_pte, new_pte; + + old_pte =huge_ptep_modify_prot_start (vma, addr, ptep); + new_pte =huge_pte_mkuffd_wp (old_pte); +huge_ptep_modify_prot_commit (vma, addr, ptep, old_pte, new_pte); + } }#endif /* CONFIG_HUGETLB_PAGE */ --
Quoted 2 lines
2.54.0
Cheers, Lorenzo
Lorenzo Stoakes
maintainerRe: [PATCH 1/6] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race
Quoted 5 lines · Lorenzo Stoakes
Quoted 1 line · Kiryl Shutsemau (Meta)
Fixes 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")I wonder if better as a separate patch? Not sure what Andrew thinks about it, I do recall him complaining about separate-hotfixes-as-part-of-a-series :>)
Ahhh it's a series of Fixes: :) ok ignore this then!
Cheers, Lorenzo
Re: [PATCH 1/6] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race
In reply to Kiryl Shutsemau (Meta)Quoted 16 lines · Kiryl Shutsemau (Meta)
make_uffd_wp_huge_pte()arms theUFFD_WPbit on a present HugeTLB PTE by callinghuge_ptep_modify_prot_commit()with a ptent snapshot that was fetched without the correspondinghuge_ptep_modify_prot_start(). The start helper is what atomically clears the entry so the kernel-owned snapshot stays consistent until the commit; without it, the hardware may set Dirty or Accessed in the live PTE between the original read and the commit, andhuge_ptep_modify_prot_commit()(whose generic implementation just callsset_huge_pte_at()) then writes the stale snapshot back over the live hardware bits, losing the update.The non-hugetlb sibling
make_uffd_wp_pte()does this correctly viaptep_modify_prot_start()/ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against.
Quoted 5 lines
Fixes 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")Reported-by Sashiko AI review <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>---
LGTM
Quoted 28 lines
fs/proc/task_mmu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)fs/proc/task_mmu.c +8
-4@@ -2610,12 +2610,16 @@ static void make_uffd_wp_huge_pte(struct vm_area_struct *vma,if (softleaf_is_hwpoison (entry) ||softleaf_is_marker (entry))return ; -if (softleaf_is_migration (entry)) +if (softleaf_is_migration (entry)) {set_huge_pte_at (vma->vm_mm, addr, ptep,pte_swp_mkuffd_wp (ptent), psize); -else -huge_ptep_modify_prot_commit (vma, addr, ptep, ptent, - huge_pte_mkuffd_wp(ptent)); + }else { +pte_t old_pte, new_pte; + + old_pte =huge_ptep_modify_prot_start (vma, addr, ptep); + new_pte =huge_pte_mkuffd_wp (old_pte); +huge_ptep_modify_prot_commit (vma, addr, ptep, old_pte, new_pte); + } }#endif /* CONFIG_HUGETLB_PAGE */
Re: [PATCH 1/6] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race
In reply to Kiryl Shutsemau (Meta)Hello Kiryl,
Quoted 5 lines · Kiryl Shutsemau (Meta)
The non-hugetlb sibling
make_uffd_wp_pte()does this correctly viaptep_modify_prot_start()/ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against.
I am seeing this on next-20261109 (arm64, PAGE_TABLE_CHECK) while the pagemap_ioctl kselftest runs next to a workload that migrates pages:
WARNING: mm/page_table_check.c:206 at __page_table_check_ptes_set+0x168/0x178, CPU#21: pagemap_ioctl/894691
Call trace:
__page_table_check_ptes_set+0x168/0x178 (P)
__set_ptes_anysz+0x50/0x240
make_uffd_wp_pte+0x104/0x1c8
pagemap_scan_pmd_entry+0x1b98/0x1df8
walk_pgd_range+0x7b4/0xd00
__walk_page_range+0x108/0x510
walk_page_range_mm_unsafe+0x150/0x3e8
do_pagemap_cmd+0x418/0x7d0
__arm64_sys_ioctl+0xc08/0x1338given you've touched this recently, I am wondering if you've seen it.
--breno
Kiryl Shutsemau
reviewerRe: [PATCH 1/6] fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race
Quoted 24 lines · Breno Leitao
Hello Kiryl,
Quoted 5 lines · Kiryl Shutsemau (Meta)
The non-hugetlb sibling
make_uffd_wp_pte()does this correctly viaptep_modify_prot_start()/ptep_modify_prot_commit(). Mirror that pattern for the present-PTE branch. The migration case stays as-is -- migration entries are non-present, so there's no hardware update to race against.I am seeing this on next-20261109 (arm64,
PAGE_TABLE_CHECK) while thepagemap_ioctlkselftest runs next to a workload that migrates pages:WARNING: mm/page_table_check.c:206 at __page_table_check_ptes_set+0x168/0x178, CPU#21: pagemap_ioctl/894691 Call trace: __page_table_check_ptes_set+0x168/0x178 (P) __set_ptes_anysz+0x50/0x240 make_uffd_wp_pte+0x104/0x1c8 pagemap_scan_pmd_entry+0x1b98/0x1df8 walk_pgd_range+0x7b4/0xd00 __walk_page_range+0x108/0x510 walk_page_range_mm_unsafe+0x150/0x3e8 do_pagemap_cmd+0x418/0x7d0 __arm64_sys_ioctl+0xc08/0x1338
Quoted 1 line
given you've touched this recently, I am wondering if you've seen it.
I have not. But I think this patch should help:
https://lore.kernel.org/all/[email protected]
Signature · 1 line
Kiryl Shutsemau / Kirill A. Shutemov
Kiryl Shutsemau (Meta)
reviewerPATCH 2/6 fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry()
In reply to Kiryl Shutsemau (Meta)The partial-page check compares against HPAGE_SIZE (PMD_SIZE), which is wrong for gigantic hugetlb hstates (e.g. 1G). The walker hands the callback a huge_page_size()-sized range, never start + HPAGE_SIZE, so the comparison always declares it partial and aborts the WP. Compare against the actual hstate's page size.
--- fs/proc/task_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/proc/task_mmu.c +1 -1
@@ -2960,7 +2960,7 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask,if (~categories &PAGE_IS_WRITTEN ) goto out_unlock; -if (end != start +HPAGE_SIZE ) { +if (end != start +huge_page_size (hstate_vma (vma))) {/* Partial HugeTLB page WP isn't possible. */ pagemap_scan_backout_range (p, start, end); p->arg.walk_end = start;
Signature · 1 line
2.54.0
Re: [PATCH 2/6] fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry()
Quoted 10 lines · Kiryl Shutsemau (Meta)
The partial-page check compares against
HPAGE_SIZE(PMD_SIZE), which is wrong for gigantic hugetlb hstates (e.g. 1G). The walker hands the callback a huge_page_size()-sized range, never start +HPAGE_SIZE, so the comparison always declares it partial and aborts the WP. Compare against the actual hstate's page size.Fixes 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")Reported-by Sashiko AI review <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>
LGTM, so:
Quoted 20 lines
--- fs/proc/task_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)fs/proc/task_mmu.c +1
-2@@ -2960,7 +2960,7 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask,if (~categories &PAGE_IS_WRITTEN ) goto out_unlock; -if (end != start +HPAGE_SIZE ) { +if (end != start +huge_page_size (hstate_vma (vma))) {/* Partial HugeTLB page WP isn't possible. */ pagemap_scan_backout_range (p, start, end); p->arg.walk_end = start; --2.54.0
Re: [PATCH 2/6] fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry()
In reply to Kiryl Shutsemau (Meta)Quoted 11 lines · Kiryl Shutsemau (Meta)
The partial-page check compares against
HPAGE_SIZE(PMD_SIZE), which is wrong for gigantic hugetlb hstates (e.g. 1G). The walker hands the callback a huge_page_size()-sized range, never start +HPAGE_SIZE, so the comparison always declares it partial and aborts the WP. Compare against the actual hstate's page size.Fixes 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")Reported-by Sashiko AI review <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>---
Quoted 16 lines
fs/proc/task_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)fs/proc/task_mmu.c +1
-1@@ -2960,7 +2960,7 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask,if (~categories &PAGE_IS_WRITTEN ) goto out_unlock; -if (end != start +HPAGE_SIZE ) { +if (end != start +huge_page_size (hstate_vma (vma))) {/* Partial HugeTLB page WP isn't possible. */ pagemap_scan_backout_range (p, start, end); p->arg.walk_end = start;
Kiryl Shutsemau (Meta)
reviewerPATCH 3/6 fs/proc/task_mmu: fix hugetlb self-deadlock in pagemap_scan_pte_hole()
In reply to Kiryl Shutsemau (Meta)A PAGEMAP_SCAN ioctl requesting PM_SCAN_WP_MATCHING on a hugetlb VMA hangs the calling thread, unkillably, as soon as the scan reaches an unpopulated part of the range:
do_pagemap_scan()
walk_page_range()
walk_hugetlb_range()
hugetlb_vma_lock_read() # take the vma lock for read ...
pagemap_scan_pte_hole() # ... ->pte_hole() for a hole
uffd_wp_range()
change_protection()
hugetlb_change_protection()
hugetlb_vma_lock_write() # ... and block taking it for write
walk_hugetlb_range() holds the hugetlb vma lock for read across the whole walk. A present entry goes to ->hugetlb_entry(); an unpopulated one goes to ->pte_hole(), i.e. pagemap_scan_pte_hole(). To write-protect the hole that handler calls uffd_wp_range(), which on a hugetlb VMA reaches hugetlb_change_protection() and takes the same vma lock for write. The thread then blocks in down_write() waiting for the read lock it is itself holding.
The populated path avoids this: pagemap_scan_hugetlb_entry() write-protects the entry inline under the page-table lock and never enters hugetlb_change_protection().
Do the same for holes. Fault in the page table and install the uffd-wp marker directly with make_uffd_wp_huge_pte() under the page-table lock, rather than routing through uffd_wp_range(). That is the same sequence hugetlb_change_protection() runs for an unpopulated entry, minus the vma write lock -- which is safe to skip because PMD sharing is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() runs at registration), leaving nothing for that lock to serialise against.
--- fs/proc/task_mmu.c | 59 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-)
fs/proc/task_mmu.c +58 -1
@@ -2977,8 +2977,62 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask, return ret; } + +/* +* Write-protect the unpopulated hugetlb entries covering [addr, end) by +* installing uffd-wp markers inline, exactly as pagemap_scan_hugetlb_entry() +* does for populated entries. +* +* walk_hugetlb_range() currently calls ->pte_hole() once per huge page, so the +* loop normally runs a single iteration; it is written to cover the full range +* in case the walker ever coalesces adjacent holes. +* +* The obvious route -- uffd_wp_range() -> hugetlb_change_protection() -- +* cannot be used here: it takes hugetlb_vma_lock_write(), but the page-table +* walker (walk_hugetlb_range()) already holds hugetlb_vma_lock_read() on the +* same VMA, so the scanning thread would deadlock against itself. PMD sharing +* is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() at registration), so +* the vma lock guards nothing that matters for these entries anyway. +*/ +static int pagemap_scan_hugetlb_hole_wp (struct vm_area_struct *vma, +unsigned long addr,unsigned long end) +{ +struct hstate *h =hstate_vma (vma); +unsigned long psize =huge_page_size (h); +struct mm_struct *mm = vma->vm_mm; +spinlock_t *ptl; +pte_t *ptep; +pte_t pte; + +for (addr =ALIGN_DOWN (addr, psize); addr < end; addr += psize) { + ptep =huge_pte_alloc (mm, vma, addr, psize); +if (!ptep) +return -ENOMEM ; + +i_mmap_lock_write (vma->vm_file->f_mapping); + ptl =huge_pte_lock (h, mm, ptep); + pte =huge_ptep_get (mm, addr, ptep); +make_uffd_wp_huge_pte (vma, addr, ptep, pte); +/* +* A none entry has no cached translation, so installing the +* marker needs no TLB flush. Flush only if a fault populated +* the entry between huge_pte_alloc() and the page table lock. +*/ +if (!huge_pte_none (pte)) +flush_hugetlb_tlb_range (vma, addr, addr + psize); +spin_unlock (ptl); +i_mmap_unlock_write (vma->vm_file->f_mapping); + } + +return 0 ; +}#else #define pagemap_scan_hugetlb_entry NULL +static int pagemap_scan_hugetlb_hole_wp (struct vm_area_struct *vma, +unsigned long addr,unsigned long end) +{ +return 0 ; +}#endif static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end,
@@ -2998,7 +3052,10 @@ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end,if (~p->arg.flags &PM_SCAN_WP_MATCHING )return ret; - err =uffd_wp_range (vma, addr, end - addr, true); +if (is_vm_hugetlb_page (vma)) + err =pagemap_scan_hugetlb_hole_wp (vma, addr, end); +else + err =uffd_wp_range (vma, addr, end - addr, true);if (err <0 ) ret = err;
Signature · 1 line
2.54.0
Kiryl Shutsemau (Meta)
reviewerPATCH 4/6 mm/huge_memory: preserve pmd_swp_uffd_wp on device-private PMD downgrade
In reply to Kiryl Shutsemau (Meta)change_non_present_huge_pmd() rewrites a writable device-private PMD swap entry into a readable one without carrying pmd_swp_uffd_wp() across. The PTE-level change_softleaf_pte() does this correctly; mirror that here, matching what copy_huge_pmd() does for the fork path. Without the carry, a plain mprotect() over a UFFD_WP-marked device-private THP strips the bit and the trap is bypassed on swap-in.
--- mm/huge_memory.c | 2 ++ 1 file changed, 2 insertions(+)
mm/huge_memory.c +2 -0
@@ -2663,6 +2663,8 @@ static void change_non_present_huge_pmd(struct mm_struct *mm, }else if (softleaf_is_device_private_write (entry)) { entry =make_readable_device_private_entry (swp_offset (entry)); newpmd =swp_entry_to_pmd (entry); +if (pmd_swp_uffd_wp (*pmd)) + newpmd =pmd_swp_mkuffd_wp (newpmd); }else { newpmd = *pmd; }
Signature · 1 line
2.54.0
Re: [PATCH 4/6] mm/huge_memory: preserve pmd_swp_uffd_wp on device-private PMD downgrade
Quoted 21 lines · Kiryl Shutsemau (Meta)
change_non_present_huge_pmd()rewrites a writable device-private PMD swap entry into a readable one without carryingpmd_swp_uffd_wp()across. The PTE-levelchange_softleaf_pte()does this correctly; mirror that here, matching whatcopy_huge_pmd()does for the fork path. Without the carry, a plainmprotect()over a UFFD_WP-marked device-private THP strips the bit and the trap is bypassed on swap-in.Fixes 368076f52ebe ("mm/huge_memory: add device-private THP support to PMD operations")Reported-by Sashiko AI review <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>--- mm/huge_memory.c | 2 ++ 1 file changed, 2 insertions(+)mm/huge_memory.c +0
-0
mm/huge_memory.c static void change_non_present_huge_pmd(struct mm_struct *mm, · line 2670
Quoted 8 lines
mm/huge_memory.c +2
-0@@ -2663,6 +2663,8 @@ static void change_non_present_huge_pmd(struct mm_struct *mm, }else if (softleaf_is_device_private_write (entry)) { entry =make_readable_device_private_entry (swp_offset (entry)); newpmd =swp_entry_to_pmd (entry); +if (pmd_swp_uffd_wp (*pmd)) + newpmd =pmd_swp_mkuffd_wp (newpmd); }else { newpmd = *pmd; }
Balbir
Kiryl Shutsemau (Meta)
reviewerPATCH 5/6 userfaultfd: gate must_wait writability check on pte_present()
In reply to Kiryl Shutsemau (Meta)userfaultfd_must_wait() and userfaultfd_huge_must_wait() read the PTE without taking the page table lock and then apply pte_write() / huge_pte_write() to it. Those accessors decode bits from the present encoding only; on a swap or migration entry they read the offset bits that happen to share the same position and return an undefined result.
The intent of the check is "is this fault still WP-blocked?". A non-marker swap entry means the page is in transit -- the userfault context the original fault delivered against is no longer the same, and the swap-in or migration completion path will re-deliver a fresh fault if userspace still needs to handle it. Worst case under the current code the garbage write bit says "wait", and the thread stays asleep until a UFFDIO_WAKE that may never arrive.
Gate the writability check on pte_present() so the lockless re-check only inspects present-PTE bits when the entry is actually present. The non-present, non-marker case returns "don't wait" and lets the fault path retry.
--- mm/userfaultfd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
mm/userfaultfd.c +20 -0
@@ -2535,6 +2535,15 @@ static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx,/* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker (pte))return true; +/* +* Concurrent migration may have replaced the present PTE with a +* non-marker swap entry between fault delivery and this lockless +* re-check. huge_pte_write() on a swap entry decodes random offset +* bits, so gate it on pte_present(). The migration completion path +* will re-deliver the fault if it still needs userspace. +*/ +if (!pte_present (pte)) +return false;/* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault.
@@ -2621,6 +2630,17 @@ static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx,/* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker (ptent)) goto out; +/* +* Concurrent swap-out / migration may have replaced the present PTE +* with a non-marker swap entry between fault delivery and this +* lockless re-check. pte_write() on a swap entry decodes random +* offset bits, so gate it on pte_present(). The page-in path will +* re-deliver the fault if it still needs userspace. +*/ +if (!pte_present (ptent)) { + ret = false; + goto out; + }/* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault.
Signature · 1 line
2.54.0
Re: [PATCH 5/6] userfaultfd: gate must_wait writability check on pte_present()
Quoted 19 lines · Kiryl Shutsemau (Meta)
userfaultfd_must_wait()anduserfaultfd_huge_must_wait()read the PTE without taking the page table lock and then applypte_write()/huge_pte_write()to it. Those accessors decode bits from the present encoding only; on a swap or migration entry they read the offset bits that happen to share the same position and return an undefined result.The intent of the check is "is this fault still WP-blocked?". A non-marker swap entry means the page is in transit -- the userfault context the original fault delivered against is no longer the same, and the swap-in or migration completion path will re-deliver a fresh fault if userspace still needs to handle it. Worst case under the current code the garbage write bit says "wait", and the thread stays asleep until a
UFFDIO_WAKEthat may never arrive.Gate the writability check on
pte_present()so the lockless re-check only inspects present-PTE bits when the entry is actually present. The non-present, non-marker case returns "don't wait" and lets the fault path retry.
Quoted 5 lines
Fixes 369cd2121be4 ("userfaultfd: hugetlbfs: userfaultfd_huge_must_wait for hugepmd ranges")Fixes 63b2d4174c4a ("userfaultfd: wp: add the writeprotect API to userfaultfd ioctl")Reported-by Sashiko AI review <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>
One tiny nit is maybe could mention softleaf :P but it's not important!
LGTM, so:
Quoted 45 lines
--- mm/userfaultfd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)mm/userfaultfd.c +20
-1@@ -2535,6 +2535,15 @@ static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx,/* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker (pte))return true; +/* +* Concurrent migration may have replaced the present PTE with a +* non-marker swap entry between fault delivery and this lockless +* re-check. huge_pte_write() on a swap entry decodes random offset +* bits, so gate it on pte_present(). The migration completion path +* will re-deliver the fault if it still needs userspace. +*/ +if (!pte_present (pte)) +return false;/* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault. @@ -2621,6 +2630,17 @@ static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx,/* UFFD PTE markers require userspace to resolve the fault. */ if (pte_is_uffd_marker (ptent)) goto out; +/* +* Concurrent swap-out / migration may have replaced the present PTE +* with a non-marker swap entry between fault delivery and this +* lockless re-check. pte_write() on a swap entry decodes random +* offset bits, so gate it on pte_present(). The page-in path will +* re-deliver the fault if it still needs userspace. +*/ +if (!pte_present (ptent)) { + ret = false; + goto out; + }/* * If VMA has UFFD WP faults enabled and WP fault, wait for userspace to * resolve the fault. -- 2.54.0
Re: [PATCH 5/6] userfaultfd: gate must_wait writability check on pte_present()
In reply to Kiryl Shutsemau (Meta)Quoted 19 lines · Kiryl Shutsemau (Meta)
userfaultfd_must_wait()anduserfaultfd_huge_must_wait()read the PTE without taking the page table lock and then applypte_write()/huge_pte_write()to it. Those accessors decode bits from the present encoding only; on a swap or migration entry they read the offset bits that happen to share the same position and return an undefined result.The intent of the check is "is this fault still WP-blocked?". A non-marker swap entry means the page is in transit -- the userfault context the original fault delivered against is no longer the same, and the swap-in or migration completion path will re-deliver a fresh fault if userspace still needs to handle it. Worst case under the current code the garbage write bit says "wait", and the thread stays asleep until a
UFFDIO_WAKEthat may never arrive.Gate the writability check on
pte_present()so the lockless re-check only inspects present-PTE bits when the entry is actually present. The non-present, non-marker case returns "don't wait" and lets the fault path retry.
Quoted 5 lines
Fixes 369cd2121be4 ("userfaultfd: hugetlbfs: userfaultfd_huge_must_wait for hugepmd ranges")Fixes 63b2d4174c4a ("userfaultfd: wp: add the writeprotect API to userfaultfd ioctl")Reported-by Sashiko AI review <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>
Quoted 3 lines
--- mm/userfaultfd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
Signature · 2 lines
Sincerely yours, Mike.
Kiryl Shutsemau (Meta)
reviewerPATCH 6/6 userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks
In reply to Kiryl Shutsemau (Meta)The VMA flags bitmap is a single word today: NUM_VMA_FLAG_BITS is BITS_PER_LONG, so on 32-bit vma_flags_t holds only 32 bits. (The bitmap type exists so this can grow past BITS_PER_LONG later; until it does, anything declared above the first word is out of range on 32-bit.) The bit enum nevertheless declares some bits unconditionally above BITS_PER_LONG -- VMA_UFFD_MINOR_BIT is 41, with VM_UFFD_MINOR == VM_NONE on 32-bit so no VMA actually carries the bit.
__VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT to mk_vma_flags() unconditionally. On 32-bit that becomes __set_bit(41, &one_long), a write one word past the end of the single-word bitmap. The compiler folds the out-of-bounds store with wraparound (1UL << (41 % 32) == bit 9) into the first word; bit 9 is already in __VMA_UFFD_FLAGS so the mask happens to come out right today, but it is an out-of-bounds write all the same, and any high-numbered bit whose mod-BITS_PER_LONG position is otherwise unused would silently OR an extra bit into the mask.
Rather than feed bit numbers that may not exist on the current build to mk_vma_flags(), build the mask from whole per-mode masks that collapse to EMPTY_VMA_FLAGS when their feature is unavailable. Add mk_vma_flags_from_masks() for that, and define VMA_UFFD_MISSING / _WP / _MINOR alongside the VM_UFFD_* flags, gating VMA_UFFD_MINOR on the same config as VM_UFFD_MINOR (which implies 64BIT, where bit 41 fits). An out-of-range bit is then never materialised, on any arch, and the in-range fast path stays a compile-time constant.
--- include/linux/mm.h | 39 +++++++++++++++++++++++++++++++++++ include/linux/userfaultfd_k.h | 4 ++-- 2 files changed, 41 insertions(+), 2 deletions(-)
include/linux/mm.h +39 -0
@@ -496,6 +496,21 @@ enum {#else #define VM_UFFD_MINOR VM_NONE#endif + +/* +* vma_flags_t masks for the userfaultfd VMA flags. VMA_UFFD_MINOR is gated on +* the same config as VM_UFFD_MINOR -- which implies 64BIT, where the bit fits +* -- so an out-of-range bit is never fed to mk_vma_flags() on a build whose +* bitmap cannot hold it. +*/ +#define VMA_UFFD_MISSING mk_vma_flags(VMA_UFFD_MISSING_BIT) +#define VMA_UFFD_WP mk_vma_flags(VMA_UFFD_WP_BIT) +#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR +#define VMA_UFFD_MINOR mk_vma_flags(VMA_UFFD_MINOR_BIT) +#else +#define VMA_UFFD_MINOR EMPTY_VMA_FLAGS +#endif +#ifdef CONFIG_64BIT #define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED)#define VM_SEALED INIT_VM_FLAG(SEALED)
@@ -1238,6 +1253,30 @@ static __always_inline void vma_flags_set_mask(vma_flags_t *flags,#define vma_flags_set (flags, ...) \ vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__)) +static __always_inline vma_flags_t __mk_vma_flags_from_masks(size_t count, +const vma_flags_t *masks) +{ +vma_flags_t flags =EMPTY_VMA_FLAGS ; +size_t i; + +for (i =0 ; i < count; i++) +vma_flags_set_mask (&flags, masks[i]); +return flags; +} + +/* +* Combine pre-computed vma_flags_t masks into one value, e.g.: +* +* vma_flags_t flags = mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR); +* +* Unlike mk_vma_flags(), which takes bit numbers, this takes whole masks -- +* each of which may be EMPTY_VMA_FLAGS when its feature is unavailable -- so a +* bit that does not exist on the current build is never materialised. +*/ +#define mk_vma_flags_from_masks (...) \ + __mk_vma_flags_from_masks(COUNT_ARGS(__VA_ARGS__), \ + (const vma_flags_t []){__VA_ARGS__}) +/* Clear all of the to-clear flags in flags, non-atomically. */ static __always_inline void vma_flags_clear_mask(vma_flags_t *flags,vma_flags_t to_clear)
include/linux/userfaultfd_k.h +2 -2
@@ -23,8 +23,8 @@/* The set of all possible UFFD-related VM flags. */ #define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR) -#define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT, \ - VMA_UFFD_MINOR_BIT) +#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \ + VMA_UFFD_MINOR)/* * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
Signature · 1 line
2.54.0
Re: [PATCH 6/6] userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks
Quoted 26 lines · Kiryl Shutsemau (Meta)
The VMA flags bitmap is a single word today:
NUM_VMA_FLAG_BITSisBITS_PER_LONG, so on 32-bitvma_flags_tholds only 32 bits. (The bitmap type exists so this can grow pastBITS_PER_LONGlater; until it does, anything declared above the first word is out of range on 32-bit.) The bit enum nevertheless declares some bits unconditionally aboveBITS_PER_LONG--VMA_UFFD_MINOR_BITis 41, withVM_UFFD_MINOR==VM_NONEon 32-bit so no VMA actually carries the bit.
__VMA_UFFD_FLAGSfeedsVMA_UFFD_MINOR_BITtomk_vma_flags()unconditionally. On 32-bit that becomes__set_bit(41, &one_long), a write one word past the end of the single-word bitmap. The compiler folds the out-of-bounds store with wraparound (1UL << (41 % 32) == bit 9) into the first word; bit 9 is already in__VMA_UFFD_FLAGSso the mask happens to come out right today, but it is an out-of-bounds write all the same, and any high-numbered bit whose mod-BITS_PER_LONG position is otherwise unused would silently OR an extra bit into the mask.Rather than feed bit numbers that may not exist on the current build to
mk_vma_flags(), build the mask from whole per-mode masks that collapse toEMPTY_VMA_FLAGSwhen their feature is unavailable. Addmk_vma_flags_from_masks()for that, and defineVMA_UFFD_MISSING/_WP/_MINORalongside the VM_UFFD_* flags, gatingVMA_UFFD_MINORon the same config asVM_UFFD_MINOR(which implies 64BIT, where bit 41 fits). An out-of-range bit is then never materialised, on any arch, and the in-range fast path stays a compile-time constant.
Quoted 1 line
Fixes 9ea35a25d51b ("mm: introduce VMA flags bitmap type")
Hmm, I think commit a06eb2f8279e ("mm/vma: convert vma_modify_flags[_uffd]() to use vma_flags_t") is more appropriate isn't it?
As that's the commit that added __VMA_UFFD_FLAGS :)
Quoted 4 lines
Reported-by Sashiko AI review <[email protected]>Suggested-by Lorenzo Stoakes <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>
The change LGTM, so:
Quoted 81 lines
Assisted-by Claude:claude-opus-4-8--- include/linux/mm.h | 39 +++++++++++++++++++++++++++++++++++ include/linux/userfaultfd_k.h | 4 ++-- 2 files changed, 41 insertions(+), 2 deletions(-)include/linux/mm.h +39
-0@@ -496,6 +496,21 @@ enum {#else #define VM_UFFD_MINOR VM_NONE#endif + +/* +* vma_flags_t masks for the userfaultfd VMA flags. VMA_UFFD_MINOR is gated on +* the same config as VM_UFFD_MINOR -- which implies 64BIT, where the bit fits +* -- so an out-of-range bit is never fed to mk_vma_flags() on a build whose +* bitmap cannot hold it. +*/ +#define VMA_UFFD_MISSING mk_vma_flags(VMA_UFFD_MISSING_BIT) +#define VMA_UFFD_WP mk_vma_flags(VMA_UFFD_WP_BIT) +#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR +#define VMA_UFFD_MINOR mk_vma_flags(VMA_UFFD_MINOR_BIT) +#else +#define VMA_UFFD_MINOR EMPTY_VMA_FLAGS +#endif +#ifdef CONFIG_64BIT #define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED)#define VM_SEALED INIT_VM_FLAG(SEALED)@@ -1238,6 +1253,30 @@ static __always_inline void vma_flags_set_mask(vma_flags_t *flags,#define vma_flags_set (flags, ...) \ vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__)) +static __always_inline vma_flags_t __mk_vma_flags_from_masks(size_t count, +const vma_flags_t *masks) +{ +vma_flags_t flags =EMPTY_VMA_FLAGS ; +size_t i; + +for (i =0 ; i < count; i++) +vma_flags_set_mask (&flags, masks[i]); +return flags; +} + +/* +* Combine pre-computed vma_flags_t masks into one value, e.g.: +* +* vma_flags_t flags = mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR); +* +* Unlike mk_vma_flags(), which takes bit numbers, this takes whole masks -- +* each of which may be EMPTY_VMA_FLAGS when its feature is unavailable -- so a +* bit that does not exist on the current build is never materialised. +*/ +#define mk_vma_flags_from_masks (...) \ + __mk_vma_flags_from_masks(COUNT_ARGS(__VA_ARGS__), \ + (const vma_flags_t []){__VA_ARGS__}) +/* Clear all of the to-clear flags in flags, non-atomically. */ static __always_inline void vma_flags_clear_mask(vma_flags_t *flags,vma_flags_t to_clear)include/linux/userfaultfd_k.h +2
-3@@ -23,8 +23,8 @@/* The set of all possible UFFD-related VM flags. */ #define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR) -#define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT, \ - VMA_UFFD_MINOR_BIT) +#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \ + VMA_UFFD_MINOR)/* * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining -- 2.54.0
Re: [PATCH 6/6] userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks
In reply to Kiryl Shutsemau (Meta)Quoted 26 lines · Kiryl Shutsemau (Meta)
The VMA flags bitmap is a single word today:
NUM_VMA_FLAG_BITSisBITS_PER_LONG, so on 32-bitvma_flags_tholds only 32 bits. (The bitmap type exists so this can grow pastBITS_PER_LONGlater; until it does, anything declared above the first word is out of range on 32-bit.) The bit enum nevertheless declares some bits unconditionally aboveBITS_PER_LONG--VMA_UFFD_MINOR_BITis 41, withVM_UFFD_MINOR==VM_NONEon 32-bit so no VMA actually carries the bit.
__VMA_UFFD_FLAGSfeedsVMA_UFFD_MINOR_BITtomk_vma_flags()unconditionally. On 32-bit that becomes__set_bit(41, &one_long), a write one word past the end of the single-word bitmap. The compiler folds the out-of-bounds store with wraparound (1UL << (41 % 32) == bit 9) into the first word; bit 9 is already in__VMA_UFFD_FLAGSso the mask happens to come out right today, but it is an out-of-bounds write all the same, and any high-numbered bit whose mod-BITS_PER_LONG position is otherwise unused would silently OR an extra bit into the mask.Rather than feed bit numbers that may not exist on the current build to
mk_vma_flags(), build the mask from whole per-mode masks that collapse toEMPTY_VMA_FLAGSwhen their feature is unavailable. Addmk_vma_flags_from_masks()for that, and defineVMA_UFFD_MISSING/_WP/_MINORalongside the VM_UFFD_* flags, gatingVMA_UFFD_MINORon the same config asVM_UFFD_MINOR(which implies 64BIT, where bit 41 fits). An out-of-range bit is then never materialised, on any arch, and the in-range fast path stays a compile-time constant.
Quoted 6 lines
Fixes 9ea35a25d51b ("mm: introduce VMA flags bitmap type")Reported-by Sashiko AI review <[email protected]>Suggested-by Lorenzo Stoakes <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>Assisted-by Claude:claude-opus-4-8
Can you ask claude to produce more concise changelogs and better split it to paragraphs?
Quoted 80 lines
--- include/linux/mm.h | 39 +++++++++++++++++++++++++++++++++++ include/linux/userfaultfd_k.h | 4 ++-- 2 files changed, 41 insertions(+), 2 deletions(-)include/linux/mm.h +39
-0@@ -496,6 +496,21 @@ enum {#else #define VM_UFFD_MINOR VM_NONE#endif + +/* +* vma_flags_t masks for the userfaultfd VMA flags. VMA_UFFD_MINOR is gated on +* the same config as VM_UFFD_MINOR -- which implies 64BIT, where the bit fits +* -- so an out-of-range bit is never fed to mk_vma_flags() on a build whose +* bitmap cannot hold it. +*/ +#define VMA_UFFD_MISSING mk_vma_flags(VMA_UFFD_MISSING_BIT) +#define VMA_UFFD_WP mk_vma_flags(VMA_UFFD_WP_BIT) +#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR +#define VMA_UFFD_MINOR mk_vma_flags(VMA_UFFD_MINOR_BIT) +#else +#define VMA_UFFD_MINOR EMPTY_VMA_FLAGS +#endif +#ifdef CONFIG_64BIT #define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED)#define VM_SEALED INIT_VM_FLAG(SEALED)@@ -1238,6 +1253,30 @@ static __always_inline void vma_flags_set_mask(vma_flags_t *flags,#define vma_flags_set (flags, ...) \ vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__)) +static __always_inline vma_flags_t __mk_vma_flags_from_masks(size_t count, +const vma_flags_t *masks) +{ +vma_flags_t flags =EMPTY_VMA_FLAGS ; +size_t i; + +for (i =0 ; i < count; i++) +vma_flags_set_mask (&flags, masks[i]); +return flags; +} + +/* +* Combine pre-computed vma_flags_t masks into one value, e.g.: +* +* vma_flags_t flags = mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR); +* +* Unlike mk_vma_flags(), which takes bit numbers, this takes whole masks -- +* each of which may be EMPTY_VMA_FLAGS when its feature is unavailable -- so a +* bit that does not exist on the current build is never materialised. +*/ +#define mk_vma_flags_from_masks (...) \ + __mk_vma_flags_from_masks(COUNT_ARGS(__VA_ARGS__), \ + (const vma_flags_t []){__VA_ARGS__}) +/* Clear all of the to-clear flags in flags, non-atomically. */ static __always_inline void vma_flags_clear_mask(vma_flags_t *flags,vma_flags_t to_clear)include/linux/userfaultfd_k.h +2
-2@@ -23,8 +23,8 @@/* The set of all possible UFFD-related VM flags. */ #define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR) -#define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT, \ - VMA_UFFD_MINOR_BIT) +#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \ + VMA_UFFD_MINOR)/* * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining Signature · 1 line
2.54.0
Signature · 2 lines
Sincerely yours, Mike.
Kiryl Shutsemau
reviewerRe: [PATCH 6/6] userfaultfd: build __VMA_UFFD_FLAGS from config-gated masks
Quoted 34 lines · Mike Rapoport
Quoted 26 lines · Kiryl Shutsemau (Meta)
The VMA flags bitmap is a single word today:
NUM_VMA_FLAG_BITSisBITS_PER_LONG, so on 32-bitvma_flags_tholds only 32 bits. (The bitmap type exists so this can grow pastBITS_PER_LONGlater; until it does, anything declared above the first word is out of range on 32-bit.) The bit enum nevertheless declares some bits unconditionally aboveBITS_PER_LONG--VMA_UFFD_MINOR_BITis 41, withVM_UFFD_MINOR==VM_NONEon 32-bit so no VMA actually carries the bit.
__VMA_UFFD_FLAGSfeedsVMA_UFFD_MINOR_BITtomk_vma_flags()unconditionally. On 32-bit that becomes__set_bit(41, &one_long), a write one word past the end of the single-word bitmap. The compiler folds the out-of-bounds store with wraparound (1UL << (41 % 32) == bit 9) into the first word; bit 9 is already in__VMA_UFFD_FLAGSso the mask happens to come out right today, but it is an out-of-bounds write all the same, and any high-numbered bit whose mod-BITS_PER_LONG position is otherwise unused would silently OR an extra bit into the mask.Rather than feed bit numbers that may not exist on the current build to
mk_vma_flags(), build the mask from whole per-mode masks that collapse toEMPTY_VMA_FLAGSwhen their feature is unavailable. Addmk_vma_flags_from_masks()for that, and defineVMA_UFFD_MISSING/_WP/_MINORalongside the VM_UFFD_* flags, gatingVMA_UFFD_MINORon the same config asVM_UFFD_MINOR(which implies 64BIT, where bit 41 fits). An out-of-range bit is then never materialised, on any arch, and the in-range fast path stays a compile-time constant.Quoted 6 lines
Fixes 9ea35a25d51b ("mm: introduce VMA flags bitmap type")Reported-by Sashiko AI review <[email protected]>Suggested-by Lorenzo Stoakes <[email protected]>Signed-off-by Kiryl Shutsemau <[email protected]>Assisted-by Claude:claude-opus-4-8
Quoted 2 lines
Can you ask claude to produce more concise changelogs and better split it to paragraphs?
Will do!
Signature · 1 line
Kiryl Shutsemau / Kirill A. Shutemov
Andrew Morton
maintainerQuoted 9 lines · On Fri, 29 May 2026 18:23:24 +0100 "Kiryl Shutsemau (Meta)" <[email protected]>…
These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this.
All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review <[email protected]>. They are independent of RWP, apply to mm-new directly, and carry Cc: stable@.
And... this made Sashiko point at other stuff:
https://sashiko.dev/#/patchset/[email protected]
I can't figure out why four of the scans failed - "View Raw Log" comes up empty.
It's concerning how frequently Sashiko is finding pre-existing things. What would a full scan of mm/ tell us? Gulp.
Kiryl Shutsemau
reviewerQuoted 8 lines · Andrew Morton
Quoted 6 lines · On Fri, 29 May 2026 18:23:24 +0100 "Kiryl Shutsemau (Meta)" <[email protected]>…
These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this.
Quoted 8 lines
Quoted 3 lines
All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review <[email protected]>. They are independent of RWP, apply to mm-new directly, and carry Cc: stable@.
And... this made Sashiko point at other stuff:
https://sashiko.dev/#/patchset/[email protected]I can't figure out why four of the scans failed - "View Raw Log" comes up empty.
Seems to be passed now.
Quoted 2 lines
It's concerning how frequently Sashiko is finding pre-existing things. What would a full scan of
mm/tell us? Gulp.
Yeah... :/
I will look at the new batch of pre-existing issues when I have spare cycles. I am not sure I have the bandwidth to find the bottom of this rabbit hole.
What do you want me to with the main userfaultfd patchset? Sashiko failed to apply it because it is on top of the pre-existing fixes.
Signature · 1 line
Kiryl Shutsemau / Kirill A. Shutemov
David Hildenbrand (Arm)
maintainerQuoted 23 lines · Kiryl Shutsemau
Quoted 8 lines · Andrew Morton
Quoted 6 lines · On Fri, 29 May 2026 18:23:24 +0100 "Kiryl Shutsemau (Meta)" <[email protected]>…
These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this.
Quoted 8 lines
Quoted 3 lines
All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review <[email protected]>. They are independent of RWP, apply to mm-new directly, and carry Cc: stable@.
And... this made Sashiko point at other stuff:
https://sashiko.dev/#/patchset/[email protected]I can't figure out why four of the scans failed - "View Raw Log" comes up empty.
Seems to be passed now.
Quoted 2 lines
It's concerning how frequently Sashiko is finding pre-existing things. What would a full scan of
mm/tell us? Gulp.
Quoted 8 lines
Yeah... :/
I will look at the new batch of pre-existing issues when I have spare cycles. I am not sure I have the bandwidth to find the bottom of this rabbit hole.
What do you want me to with the main userfaultfd patchset? Sashiko failed to apply it because it is on top of the pre-existing fixes.
I suspect it might have a minor conflict with another incoming fix from Lorenzo as well.
Probably put it into mm-unstable early after the next release? At least I think it's not in mm-unstable yet and we are approaching the net release quickly ...
Signature · 3 lines
Cheers, David
Kiryl Shutsemau
reviewerQuoted 30 lines · David Hildenbrand (Arm)
Quoted 29 lines · Kiryl Shutsemau
Quoted 8 lines · Andrew Morton
Quoted 6 lines · On Fri, 29 May 2026 18:23:24 +0100 "Kiryl Shutsemau (Meta)" <[email protected]>…
These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this.
Quoted 8 lines
Quoted 3 lines
All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review <[email protected]>. They are independent of RWP, apply to mm-new directly, and carry Cc: stable@.
And... this made Sashiko point at other stuff:
https://sashiko.dev/#/patchset/[email protected]I can't figure out why four of the scans failed - "View Raw Log" comes up empty.
Seems to be passed now.
Quoted 2 lines
It's concerning how frequently Sashiko is finding pre-existing things. What would a full scan of
mm/tell us? Gulp.Yeah... :/
I will look at the new batch of pre-existing issues when I have spare cycles. I am not sure I have the bandwidth to find the bottom of this rabbit hole.
Quoted 8 lines
Quoted 2 lines
What do you want me to with the main userfaultfd patchset? Sashiko failed to apply it because it is on top of the pre-existing fixes.
I suspect it might have a minor conflict with another incoming fix from Lorenzo as well.
Probably put it into mm-unstable early after the next release? At least I think it's not in mm-unstable yet and we are approaching the net release quickly ...
I guess I would need to patch man-pages that got applied. I put 7.2 there...
Signature · 1 line
Kiryl Shutsemau / Kirill A. Shutemov
David Hildenbrand (Arm)
maintainerQuoted 15 lines · Kiryl Shutsemau
Quoted 14 lines · David Hildenbrand (Arm)
Quoted 12 lines · Kiryl Shutsemau
Seems to be passed now.
Yeah... :/
I will look at the new batch of pre-existing issues when I have spare cycles. I am not sure I have the bandwidth to find the bottom of this rabbit hole.
What do you want me to with the main userfaultfd patchset? Sashiko failed to apply it because it is on top of the pre-existing fixes.
Quoted 8 lines
Quoted 5 lines
I suspect it might have a minor conflict with another incoming fix from Lorenzo as well.
Probably put it into mm-unstable early after the next release? At least I think it's not in mm-unstable yet and we are approaching the net release quickly ...
I guess I would need to patch man-pages that got applied. I put 7.2 there...
We really should merge man page updates only after stuff is definitely in. :)
Signature · 3 lines
Cheers, David
Lorenzo Stoakes
maintainerQuoted 12 lines · Andrew Morton
Quoted 9 lines · On Fri, 29 May 2026 18:23:24 +0100 "Kiryl Shutsemau (Meta)" <[email protected]>…
These are pre-existing bug fixes that were carried at the front of the userfaultfd RWP working-set-tracking series up to v5 [1]. Per review feedback that fixes should not sit in the middle of a feature series, they are split out and sent on their own; the RWP series is reposted rebased on top of this.
All six were flagged by the Sashiko AI review of the RWP series and carry Reported-by: Sashiko AI review <[email protected]>. They are independent of RWP, apply to mm-new directly, and carry Cc: stable@.
Quoted 8 lines
And... this made Sashiko point at other stuff:
https://sashiko.dev/#/patchset/[email protected]I can't figure out why four of the scans failed - "View Raw Log" comes up empty.
It's concerning how frequently Sashiko is finding pre-existing things. What would a full scan of
mm/tell us? Gulp.
Yeah, but I also kinda wish it didn't do this :) It's similar to sending a series, and somebody replying saying 'well this is totally unrelated but <blah blah blah>' and expecting a reply :>)
You'd want that person to just submit a patch for the issue or a [DISCUSSION] or [BUG] thread or something, but interrupting, possibly delaying + taking people's (usually very limited) time on something unrelated is just unhelpful I think.
I'd prefer Sashiko had a passive mode that checked for existing stuff elsewhere, and it saved the tokens doing it for a new series :)
Just my 2 pence!
Cheers, Lorenzo