* [PATCH] mm/swap: fix swap_cache_get_folio() returning a folio that left the swap cache
@ 2026-10-15 6:05 Jiayuan Chen
0 siblings, 0 replies; only message in thread
From: Jiayuan Chen @ 2026-10-15 6:05 UTC (permalink / raw)
To: linux-mm
Cc: Jiayuan Chen, Jiayuan Chen, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Youngjun Park,
linux-kernel
From: Jiayuan Chen <jiayuan.chen@shopee.com>
swap_cache_get_folio() reads the folio from the swap table and takes a
reference with folio_try_get(), without any lock. If the folio is
removed from the swap cache and freed in between, and its memory gets
reused, folio_try_get() still succeeds and we return a page that has
nothing to do with the swap entry:
CPU 0 CPU 1
do_swap_page()
swap_cache_get_folio()
swp_tb = swap_table_get()
folio = swp_tb_to_folio(swp_tb)
madvise(MADV_DONTNEED)
zap_nonpresent_ptes()
swap_put_entries_direct()
swap_put_entries_cluster()
__try_to_reclaim_swap()
swap_cache_del_folio()
folio_put() <- freed
page reused, e.g. as an anon
folio of another task
folio_try_get(folio) <- succeeds
return folio
swap_update_readahead() <- may clear PG_readahead on it
folio_lock_or_retry() <- locks it
folio_matches_swap_entry() fails, unlock and put
filemap_get_entry() does the same lockless lookup for the page cache,
and after folio_try_get() it rechecks the xarray, because the folio may
have been freed and reused in between. swap_cache_get_folio() lost
that recheck when the swap cache moved to the swap table.
So CPU 0 writes to a folio it doesn't own: it may clear PG_readahead and
takes the folio lock of someone else's page, before
folio_matches_swap_entry() finds out the folio is not ours.
Recheck the slot after folio_try_get() and retry if it no longer holds
this folio.
Fixes: 8578e0c00dcf ("mm, swap: use the swap table for the swap cache and switch API")
Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
---
https://sashiko.dev/#/patchset/20261014090821.227366-1-jiayuan.chen@linux.dev
---
mm/swap_state.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/mm/swap_state.c b/mm/swap_state.c
index ebef568cd62e..d9e36c505398 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -92,17 +92,27 @@ void show_swap_cache_info(void)
*/
struct folio *swap_cache_get_folio(swp_entry_t entry)
{
+ struct swap_cluster_info *ci = __swap_entry_to_cluster(entry);
+ unsigned int ci_off = swp_cluster_offset(entry);
unsigned long swp_tb;
struct folio *folio;
for (;;) {
- swp_tb = swap_table_get(__swap_entry_to_cluster(entry),
- swp_cluster_offset(entry));
+ swp_tb = swap_table_get(ci, ci_off);
if (!swp_tb_is_folio(swp_tb))
return NULL;
folio = swp_tb_to_folio(swp_tb);
- if (likely(folio_try_get(folio)))
+ if (!folio_try_get(folio))
+ continue;
+ /*
+ * The folio may have been freed and its memory reused before
+ * we got the reference. Make sure it is still in the slot.
+ */
+ swp_tb = swap_table_get(ci, ci_off);
+ if (likely(swp_tb_is_folio(swp_tb) &&
+ swp_tb_to_folio(swp_tb) == folio))
return folio;
+ folio_put(folio);
}
return NULL;
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-15 6:05 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15 6:05 [PATCH] mm/swap: fix swap_cache_get_folio() returning a folio that left the swap cache Jiayuan Chen
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.