[PATCH] jfs: hold a page reference on log pages while I/O is in flight

From: Zhijian Han

Date: Tue Nov 10 2026 - 05:13:43 EST


syzbot reports a use-after-free read in copy_folio_from_iter_atomic()
that has been reproducing for almost a year:

BUG: KASAN: use-after-free in copy_folio_from_iter_atomic
Read of size 4096 by task kworker/u32:9
Workqueue: loop0 loop_rootcg_workfn
lo_rw_aio
shmem_file_write_iter
generic_perform_write
copy_folio_from_iter_atomic

I reproduced the same KASAN report with the C reproducer from the
syzbot page: the loop worker reads a log page that jfs has already
freed.

The jfs log code builds one-page bios for its log device in
lbmStartIO()/lbmRead() and adds the page with __bio_add_page() without
taking a reference on it. The lifetime of the page is instead
guaranteed indirectly: the lbuf that owns the page is kept alive until
lbmIOWait() observes the lbmDONE flag, which lbmIODone() sets when the
bio completes.

That guarantee is not sufficient when the log device is an asynchronous
stacking device such as loop: log writes are pipelined through
lmGroupCommit(), and lbmStartIO() marks the sync-point write REQ_SYNC.
The block layer may complete the REQ_SYNC write before an earlier log
write has been processed by the loop worker, so lmLogShutdown() can
free all log pages while an earlier bio is still queued inside loop
and about to read the page it references.

Fix this by the book: take a page reference before adding the page to
the bio and drop it in the completion path (lbmIODone()), so the page
is guaranteed to stay alive for the whole lifetime of the bio. This
is the same lifetime rule every other filesystem follows for pages it
submits.

This bug was found and analyzed with the help of LLM tooling; the fix
was authored and verified by the signer.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Link: https://syzkaller.appspot.com/bug?extid=2cec257b63d85bb983fc
Reported-by: syzbot+2cec257b63d85bb983fc@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=2cec257b63d85bb983fc
Tested-by: Zhijian Han <hanzhijian1991@xxxxxxxxx>
Signed-off-by: Zhijian Han <hanzhijian1991@xxxxxxxxx>
---
fs/jfs/jfs_logmgr.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
index 306165e61438..ef9ef11ddb1e 100644
--- a/fs/jfs/jfs_logmgr.c
+++ b/fs/jfs/jfs_logmgr.c
@@ -1971,6 +1971,7 @@ static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp)

bio = bio_alloc(file_bdev(log->bdev_file), 1, REQ_OP_READ, GFP_NOFS);
bio->bi_iter.bi_sector = bp->l_blkno << (log->l2bsize - 9);
+ get_page(bp->l_page);
__bio_add_page(bio, bp->l_page, LOGPSIZE, bp->l_offset);
BUG_ON(bio->bi_iter.bi_size != LOGPSIZE);

@@ -2117,6 +2118,7 @@ static void lbmStartIO(struct lbuf * bp)
bio = bio_alloc(bdev, 1, REQ_OP_WRITE | REQ_SYNC,
GFP_NOFS);
bio->bi_iter.bi_sector = bp->l_blkno << (log->l2bsize - 9);
+ get_page(bp->l_page);
__bio_add_page(bio, bp->l_page, LOGPSIZE, bp->l_offset);
BUG_ON(bio->bi_iter.bi_size != LOGPSIZE);

@@ -2184,6 +2186,7 @@ static void lbmIODone(struct bio *bio)
jfs_err("lbmIODone: I/O error in JFS log");
}

+ put_page(bp->l_page);
bio_put(bio);

/*
--
2.43.0