remove_migration_pte() makes the restored PTE writable for a writable migration entry, and only looks at the uffd bit of the entry otherwise. A writable migration entry that has the uffd bit set is therefore restored as a writable PTE without the uffd bit, and the page is no longer write-protected. An entry with the uffd bit set must be restored write-protected, whether the entry is writable or not. remove_migration_pmd() gets this right: it makes the PMD writable and sets the uffd bit as two independent steps, and pmd_mkuffd() write-protects the result. To fix it, do the same at the PTE level, where pte_mkuffd() write-protects the PTE as well. The only known path that created such entries is PAGEMAP_SCAN. Now that PAGEMAP_SCAN downgrades them like change_protection() does, we should no longer see them, so this change alone should not be visible to userspace. If another path ever creates a writable migration entry with the uffd bit set, the page now stays write-protected instead of losing the uffd bit. Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Tal Zussman --- mm/migrate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/migrate.c b/mm/migrate.c index 28a8d7734735..d200f64b5f98 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -399,7 +399,7 @@ static bool remove_migration_pte(struct folio *folio, if (softleaf_is_migration_write(entry)) pte = pte_mkwrite(pte, vma); - else if (pte_swp_uffd(old_pte)) + if (pte_swp_uffd(old_pte)) pte = pte_mkuffd(pte); /* See do_swap_page(): restore PAGE_NONE for RWP */ -- 2.39.5