[PATCH] jfs: fix self-deadlock on discarded metapage in __get_metapage()

From: Nguyen Ngoc Thang

Date: Wed Sep 23 2026 - 10:55:58 EST


__get_metapage() holds the folio lock when it finds a cached metapage
flagged META_discard on a non-new lookup. It reports the corruption
and calls discard_metapage(), which goes through release_metapage() and
tries to take the same folio lock again:

release_metapage+0x103/0xab0 fs/jfs/jfs_metapage.c:868
__get_metapage+0x9a0/0xde0 fs/jfs/jfs_metapage.c:751
jfs_readdir+0x1e01/0x3ae0 fs/jfs/jfs_dtree.c:3041

The task then sleeps forever in D state with i_rwsem held, which
syzbot reports as a hung task.

Split release_metapage() into a locked variant and use it here. The
locked variant also unlocks and puts the folio, so return NULL directly
instead of going through the unlock label.

Reported-by: syzbot+5570e0eb2edddd308514@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=5570e0eb2edddd308514
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Nguyen Ngoc Thang <ngocthang2710.1999@xxxxxxxxx>
---
fs/jfs/jfs_metapage.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index 41fe12e641ce..b3585df259cd 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -681,6 +681,8 @@ const struct address_space_operations jfs_metapage_aops = {
.migrate_folio = metapage_migrate_folio,
};

+static void release_metapage_locked(struct metapage *mp);
+
struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
unsigned int size, int absolute,
unsigned long new)
@@ -751,8 +753,9 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
if (!new) {
jfs_error(inode->i_sb,
"using a discarded metapage\n");
- discard_metapage(mp);
- goto unlock;
+ clear_bit(META_dirty, &mp->flag);
+ release_metapage_locked(mp);
+ return NULL;
}
clear_bit(META_discard, &mp->flag);
}
@@ -863,12 +866,11 @@ void put_metapage(struct metapage *mp)
release_metapage(mp);
}

-void release_metapage(struct metapage * mp)
+/* Caller holds the folio lock; it is dropped along with the folio ref. */
+static void release_metapage_locked(struct metapage *mp)
{
struct folio *folio = mp->folio;
- jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag);

- folio_lock(folio);
unlock_metapage(mp);

assert(mp->count);
@@ -896,6 +898,14 @@ void release_metapage(struct metapage * mp)
folio_put(folio);
}

+void release_metapage(struct metapage *mp)
+{
+ jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag);
+
+ folio_lock(mp->folio);
+ release_metapage_locked(mp);
+}
+
void __invalidate_metapages(struct inode *ip, s64 addr, int len)
{
sector_t lblock;
--
2.43.0