All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chao Yu via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net>
To: jaegeuk@kernel.org
Cc: linux-kernel@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net
Subject: [f2fs-dev] [PATCH 1/2] f2fs: cache: pin cached block in f2fs_end_cache_writeback()
Date: Wed, 30 Sep 2026 23:54:34 +0000	[thread overview]
Message-ID: <20260930235435.3789910-1-chao@kernel.org> (raw)

From: Chao Yu <chao@kernel.org>

Like page cache folios, writeback does not hold an active reference on
the cached block during I/O flight, relying instead on truncation or
shrinker callers to wait on F2FS_BLOCK_WRITEBACK before releasing the entry.

However, in f2fs_end_cache_writeback(), clear_and_wake_up_bit() executes
two distinct steps:
1. clear_bit_unlock(F2FS_BLOCK_WRITEBACK, &entry->state): clears the bit.
2. wake_up_bit(&entry->state, F2FS_BLOCK_WRITEBACK): hashes &entry->state
   to look up the waitqueue and wakes waiting tasks.

Once step 1 clears the bit, a concurrent waiter in f2fs_do_truncate_cache()
or f2fs_do_shrink_cache() is immediately unblocked. The waiter can proceed
to delete the entry from the radix tree, drop the final reference, and
kfree() the entry before step 2 finishes, causing wake_up_bit() to access
freed memory:

CPU 0 (I/O completion)               CPU 1 (Truncation / Shrinker)
- f2fs_cache_write_end_io()
 - f2fs_end_cache_writeback(entry)
  - clear_and_wake_up_bit()
   - clear_bit_unlock(WRITEBACK)
   : bit is cleared!
                                     - f2fs_do_truncate_cache(entry)
                                      - f2fs_cache_wait_writeback(entry)
                                      : sees WRITEBACK cleared!
                                      - radix_tree_delete(&cache->root, ...)
                                      - f2fs_put_cache(entry, true)
                                       - atomic_dec_and_test(&refcount) == 0
                                       - f2fs_do_free_cache(entry)
                                        - kfree(entry->data);
                                        - kfree(entry); <--- FREED!
   - smp_mb__after_atomic()
   - wake_up_bit(&entry->state, ...)
   : Dereferences &entry->state on freed entry! (UAF)

Mirror the logic in folio_end_writeback() by acquiring a temporary reference
via f2fs_cache_get() before clear_and_wake_up_bit() and releasing it with
f2fs_cache_put() once wake_up_bit() completes. This guarantees the entry
can not be freed until wake_up_bit() has finished.

This fixes commit 399410a90ca7 ("f2fs: cache: implement metadata cache").

Signed-off-by: Chao Yu <chao@kernel.org>
---
 fs/f2fs/cache.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/fs/f2fs/cache.c b/fs/f2fs/cache.c
index 38fc5eb17f92..7831e53f5678 100644
--- a/fs/f2fs/cache.c
+++ b/fs/f2fs/cache.c
@@ -117,7 +117,15 @@ void f2fs_end_cache_writeback(struct f2fs_cached_block *entry)
 	 */
 	f2fs_cache_update_tag(entry, F2FS_CACHE_TAG_WRITEBACK,
 						F2FS_CACHE_TAG_NONE);
+	/*
+	 * Writeback does not hold an entry reference of its own, relying
+	 * on truncation to wait for the clearing of F2FS_BLOCK_WRITEBACK.
+	 * But here we must make sure that the entry is not freed and
+	 * reused before clear_and_wake_up_bit().
+	 */
+	f2fs_cache_get(entry);
 	clear_and_wake_up_bit(F2FS_BLOCK_WRITEBACK, &entry->state);
+	f2fs_cache_put(entry);
 }
 
 static int f2fs_cache_refcount(struct f2fs_cached_block *entry)
-- 
2.49.0



_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

             reply	other threads:[~2026-09-30 23:54 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-30 23:54 Chao Yu via Linux-f2fs-devel [this message]
2026-09-30 23:54 ` [f2fs-dev] [PATCH 2/2] f2fs: cache: pin cached block in f2fs_unlock_cache() Chao Yu via Linux-f2fs-devel

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=20260930235435.3789910-1-chao@kernel.org \
    --to=linux-f2fs-devel@lists.sourceforge.net \
    --cc=chao@kernel.org \
    --cc=jaegeuk@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /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.