All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiayuan Chen <jiayuan.chen@linux.dev>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-mm@kvack.org, Jiayuan Chen <jiayuan.chen@shopee.com>,
	Chris Li <chrisl@kernel.org>, Kairui Song <kasong@tencent.com>,
	Kemeng Shi <shikemeng@huaweicloud.com>,
	Nhat Pham <nphamcs@gmail.com>, Baoquan He <baoquan.he@linux.dev>,
	Barry Song <baohua@kernel.org>,
	Youngjun Park <youngjun.park@lge.com>,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH] mm/swap: fix swap_cache_get_folio() returning a folio that left the swap cache
Date: Fri, 16 Oct 2026 10:19:01 +0800	[thread overview]
Message-ID: <37f1af42-9788-4552-86a4-516299241ee8@linux.dev> (raw)
In-Reply-To: <20261015190619.deccb92624e356bf81084a24@linux-foundation.org>

Hi Andrew,

On 10/16/26 10:06 AM, Andrew Morton wrote:
> On Thu, 15 Oct 2026 14:05:27 +0800 Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
>> 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.
> LLM says to me:
>
> : The original folio could conceivably be freed, its physical page
> : reused, and the same PFN inserted into the same swap-table slot before
> : the second lookup.  That would pass the check.  However, the returned
> : reference would then belong to the newly inserted folio, and the later
> : swap-entry validation would still be required.  I don't see that as
> : invalidating this fix.



I had the same worry at first, data corruption or something worse, but
after thinking it through I agree. Passing the check there is fine:
the folio we return is the one currently in the slot, which is what
the function promises, and callers still check folio_matches_swap_entry()
under the folio lock before using it. That's why I didn't mention it in 
the changelog.


>> --- 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;
> This return is now unreachable...


Right, and it never was reachable, the loop has no break. It should have 
been dropped earlier...



      reply	other threads:[~2026-10-16  2:19 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15  6:05 [PATCH] mm/swap: fix swap_cache_get_folio() returning a folio that left the swap cache Jiayuan Chen
2026-10-16  1:40 ` Andrew Morton
2026-10-16  2:10   ` Jiayuan Chen
2026-10-16  2:22     ` Kairui Song
2026-10-16  3:00       ` Andrew Morton
2026-10-16  2:06 ` Andrew Morton
2026-10-16  2:19   ` Jiayuan Chen [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=37f1af42-9788-4552-86a4-516299241ee8@linux.dev \
    --to=jiayuan.chen@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=baohua@kernel.org \
    --cc=baoquan.he@linux.dev \
    --cc=chrisl@kernel.org \
    --cc=jiayuan.chen@shopee.com \
    --cc=kasong@tencent.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=nphamcs@gmail.com \
    --cc=shikemeng@huaweicloud.com \
    --cc=youngjun.park@lge.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.