[PATCH 00/12] userfaultfd: assorted fixes (part 1)

From: Tal Zussman

Date: Wed Nov 11 2026 - 13:55:14 EST


This is a first batch of userfaultfd fixes, with more to come.

These were found by a mix of regular old LLM review and by having an LLM
write a TLA+ model of uffd interactions with PTE bits and other state
and seeing what issues fell out.

Most of these fix places where mm code loses uffd-wp or RWP protection
while moving, migrating or zapping an entry. Userspace then either
misses writes and accesses, or gets pages reported that nobody touched.
There are also two hangs (UFFDIO_MOVE and MADV_GUARD_INSTALL), and
UFFDIO_ZEROPAGE modifying a file through a private mapping.

Patches 1-2: UFFDIO_ZEROPAGE and UFFDIO_MOVE fixes, cc stable
Patches 3-5: PAGEMAP_SCAN write-protect vs. migration and
device-private entries
Patch 6: UFFDIO_MOVE of the huge zero page from an RWP source
Patch 7: mremap() losing uffd state when a move fails halfway
Patch 8: migrate_vma replacing a uffd-protected zero page
Patches 9-11: guard regions vs. uffd-wp
Patch 12: UFFDIO_UNREGISTER with a misaligned hugetlb end

A few of the fixes change what userspace sees:

- UFFDIO_ZEROPAGE on a private file mapping no longer fails with
-EEXIST where the file already has a page, on an mm that cannot use
the zero page (patch 1)
- UFFDIO_MOVE returns -EEXIST for any huge destination PMD, not only
for a present THP (patch 2)
- MADV_GUARD_INSTALL sends UFFD_EVENT_REMOVE, and can return -ENOMEM
or -EINVAL if the VMA goes away or changes while the lock is dropped
(patch 11)
- UFFDIO_UNREGISTER with a misaligned hugetlb end fails before
unregistering anything (patch 12)

Patch 6 has little user impact on its own. It is included because,
together with patch 7, it allows removing clear_uffd_wp_pmd().

Patch 7 adds spurious minor faults on shmem and hugetlb after mremap():
when dropping uffd protection there, change_protection() makes writable
pages that were never protected read-only. UFFDIO_UNREGISTER and
UFFDIO_WRITEPROTECT already behave this way and lead to the same
spurious faults. This will be fixed in change_protection() in a separate
series.

Based on mm-new.

---
Tal Zussman (12):
userfaultfd: use anonymous memory for UFFDIO_ZEROPAGE on private file mappings
userfaultfd: refuse UFFDIO_MOVE into a non-present huge destination PMD
fs/proc/task_mmu: downgrade writable migration entries in PAGEMAP_SCAN write-protect
mm/migrate: keep the uffd bit when restoring a writable migration entry
fs/proc/task_mmu: write-protect device-private THPs in PAGEMAP_SCAN
mm/huge_memory: build the moved huge zero PMD from the destination VMA
mm/mremap: resolve uffd state when the context is dropped, not during the move
mm/migrate_device: do not migrate a uffd-protected zero page
mm/madvise: let a guard marker carry the uffd-wp bit
mm: set the uffd-wp bit on a guard marker when write-protecting
mm/madvise: send UFFD_EVENT_REMOVE when installing guard regions
mm/userfaultfd: check hugetlb end alignment in UFFDIO_UNREGISTER pre-scan

fs/proc/task_mmu.c | 64 +++++++++++++++++++++++++++++++++++++++++---
include/linux/leafops.h | 18 +++++++++++++
mm/huge_memory.c | 31 +++-------------------
mm/hugetlb.c | 27 +------------------
mm/madvise.c | 62 +++++++++++++++++++++++++++++++++++--------
mm/migrate.c | 2 +-
mm/migrate_device.c | 23 ++++++++++++++--
mm/mprotect.c | 26 ++++++++++++++++--
mm/mremap.c | 46 +-------------------------------
mm/userfaultfd.c | 70 ++++++++++++++++++++++++-------------------------
10 files changed, 215 insertions(+), 154 deletions(-)
---
base-commit: 2dc0136b1fdac153434ab02778fcdb32db5d0dae
change-id: 20261106-uffd-state-fixes-d2111e2abd48

Best regards,
--
Tal Zussman <tz2294@xxxxxxxxxxxx>