[PATCH] mm: migrate: transfer large_rmappable flag in folio_migrate_flags()
From: Usama Arif
Date: Wed Mar 11 2026 - 09:31:10 EST
folio_migrate_flags() transfers folio state from source to destination
during migration, but does not transfer the large_rmappable flag.
Migration allocators like alloc_migration_target() and
alloc_misplaced_dst_folio() use __folio_alloc() directly without
wrapping the result in page_rmappable_folio(), so the destination folio
never gets large_rmappable set.
This becomes a problem when a folio on the deferred split queue is
migrated: the destination folio can be added to the deferred split queue
via deferred_split_folio() (which does not check large_rmappable), but
when the folio is later freed, folio_unqueue_deferred_split() bails out
early because large_rmappable is not set:
if (folio_order(folio) <= 1 || !folio_test_large_rmappable(folio))
return false;
This leaves a stale entry on the deferred split queue, leading to
use-after-free when the shrinker walks the list.
Fix this by transferring large_rmappable in folio_migrate_flags(),
consistent with how all other folio flags are handled.
Fixes: dafff3f4c850 ("mm: split underused THPs")
Signed-off-by: Usama Arif <usama.arif@xxxxxxxxx>
---
mm/migrate.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/mm/migrate.c b/mm/migrate.c
index 3380021fd3db..ee1c7bc851dd 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -846,6 +846,9 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
folio_copy_owner(newfolio, folio);
pgalloc_tag_swap(newfolio, folio);
+ if (folio_test_large_rmappable(folio))
+ folio_set_large_rmappable(newfolio);
+
mem_cgroup_migrate(folio, newfolio);
}
EXPORT_SYMBOL(folio_migrate_flags);
--
2.52.0