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 --- 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