jfs-discussion Mailing List for Journaled File System
Brought to you by:
blaschke-oss,
shaggyk
You can subscribe to this list here.
| 2001 |
Jan
|
Feb
|
Mar
|
Apr
(35) |
May
(47) |
Jun
(67) |
Jul
(147) |
Aug
(58) |
Sep
(65) |
Oct
(84) |
Nov
(34) |
Dec
(53) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2002 |
Jan
(89) |
Feb
(66) |
Mar
(79) |
Apr
(42) |
May
(22) |
Jun
(15) |
Jul
(51) |
Aug
(29) |
Sep
(38) |
Oct
(57) |
Nov
(30) |
Dec
(6) |
| 2003 |
Jan
(41) |
Feb
(19) |
Mar
(17) |
Apr
(14) |
May
(23) |
Jun
(17) |
Jul
(12) |
Aug
(8) |
Sep
(63) |
Oct
(14) |
Nov
(7) |
Dec
(15) |
| 2004 |
Jan
(31) |
Feb
(9) |
Mar
(72) |
Apr
(40) |
May
(38) |
Jun
(30) |
Jul
(62) |
Aug
(5) |
Sep
(51) |
Oct
(14) |
Nov
(25) |
Dec
(53) |
| 2005 |
Jan
(61) |
Feb
(22) |
Mar
(40) |
Apr
(37) |
May
(15) |
Jun
(53) |
Jul
(52) |
Aug
(40) |
Sep
(52) |
Oct
(51) |
Nov
(27) |
Dec
(12) |
| 2006 |
Jan
(24) |
Feb
(40) |
Mar
(8) |
Apr
(22) |
May
(12) |
Jun
(29) |
Jul
(33) |
Aug
(23) |
Sep
(19) |
Oct
(24) |
Nov
(28) |
Dec
(50) |
| 2007 |
Jan
(59) |
Feb
(21) |
Mar
(39) |
Apr
(16) |
May
(17) |
Jun
(27) |
Jul
(40) |
Aug
(62) |
Sep
(54) |
Oct
(62) |
Nov
(39) |
Dec
(28) |
| 2008 |
Jan
(34) |
Feb
(21) |
Mar
(59) |
Apr
(48) |
May
(45) |
Jun
(33) |
Jul
(40) |
Aug
(39) |
Sep
(46) |
Oct
(4) |
Nov
(1) |
Dec
(1) |
| 2009 |
Jan
(1) |
Feb
|
Mar
(10) |
Apr
(19) |
May
(12) |
Jun
(29) |
Jul
(44) |
Aug
(13) |
Sep
(19) |
Oct
(5) |
Nov
(10) |
Dec
(41) |
| 2010 |
Jan
(30) |
Feb
(22) |
Mar
(8) |
Apr
(10) |
May
(6) |
Jun
(17) |
Jul
(12) |
Aug
(2) |
Sep
(6) |
Oct
(13) |
Nov
(20) |
Dec
(6) |
| 2011 |
Jan
|
Feb
(4) |
Mar
(9) |
Apr
(22) |
May
(24) |
Jun
(13) |
Jul
(5) |
Aug
(5) |
Sep
(3) |
Oct
(3) |
Nov
(14) |
Dec
(23) |
| 2012 |
Jan
(1) |
Feb
|
Mar
(7) |
Apr
|
May
(10) |
Jun
(17) |
Jul
(36) |
Aug
(7) |
Sep
(17) |
Oct
(16) |
Nov
(7) |
Dec
(2) |
| 2013 |
Jan
(10) |
Feb
(2) |
Mar
(2) |
Apr
|
May
(23) |
Jun
(18) |
Jul
(5) |
Aug
(23) |
Sep
(5) |
Oct
(10) |
Nov
(19) |
Dec
(97) |
| 2014 |
Jan
(7) |
Feb
(9) |
Mar
(13) |
Apr
(10) |
May
(2) |
Jun
(2) |
Jul
(2) |
Aug
|
Sep
|
Oct
(107) |
Nov
(18) |
Dec
(5) |
| 2015 |
Jan
|
Feb
(10) |
Mar
(38) |
Apr
(18) |
May
(4) |
Jun
(3) |
Jul
(15) |
Aug
(5) |
Sep
(5) |
Oct
|
Nov
|
Dec
|
| 2016 |
Jan
|
Feb
(8) |
Mar
(8) |
Apr
(9) |
May
(2) |
Jun
(22) |
Jul
(5) |
Aug
(13) |
Sep
(2) |
Oct
(1) |
Nov
(4) |
Dec
(2) |
| 2017 |
Jan
(4) |
Feb
|
Mar
(5) |
Apr
(73) |
May
(98) |
Jun
(27) |
Jul
(21) |
Aug
(9) |
Sep
(3) |
Oct
(17) |
Nov
(6) |
Dec
(7) |
| 2018 |
Jan
(5) |
Feb
(3) |
Mar
|
Apr
(103) |
May
(64) |
Jun
(14) |
Jul
|
Aug
(19) |
Sep
(15) |
Oct
(3) |
Nov
(3) |
Dec
|
| 2019 |
Jan
(7) |
Feb
(1) |
Mar
(2) |
Apr
|
May
(5) |
Jun
(50) |
Jul
(23) |
Aug
(47) |
Sep
|
Oct
(4) |
Nov
(3) |
Dec
|
| 2020 |
Jan
(6) |
Feb
(2) |
Mar
(2) |
Apr
(3) |
May
(28) |
Jun
(3) |
Jul
(5) |
Aug
(3) |
Sep
(35) |
Oct
(4) |
Nov
(8) |
Dec
(21) |
| 2021 |
Jan
(95) |
Feb
(22) |
Mar
(19) |
Apr
|
May
(1) |
Jun
(10) |
Jul
(16) |
Aug
(58) |
Sep
(8) |
Oct
(182) |
Nov
(2) |
Dec
(1) |
| 2022 |
Jan
|
Feb
|
Mar
(11) |
Apr
(161) |
May
(28) |
Jun
(35) |
Jul
(1) |
Aug
(5) |
Sep
(25) |
Oct
(78) |
Nov
(44) |
Dec
(56) |
| 2023 |
Jan
(46) |
Feb
(11) |
Mar
(100) |
Apr
(56) |
May
(117) |
Jun
(84) |
Jul
(93) |
Aug
(51) |
Sep
(68) |
Oct
(79) |
Nov
(44) |
Dec
(12) |
| 2024 |
Jan
(86) |
Feb
(40) |
Mar
(18) |
Apr
(50) |
May
(18) |
Jun
(18) |
Jul
(33) |
Aug
(25) |
Sep
(39) |
Oct
(40) |
Nov
(51) |
Dec
(19) |
| 2025 |
Jan
(19) |
Feb
(56) |
Mar
(34) |
Apr
(105) |
May
(11) |
Jun
(81) |
Jul
(42) |
Aug
(29) |
Sep
(18) |
Oct
(86) |
Nov
(45) |
Dec
(34) |
| 2026 |
Jan
(231) |
Feb
(138) |
Mar
(239) |
Apr
(23) |
May
(4) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|
From: Tristan M. <tr...@gm...> - 2026-05-01 11:02:45
|
From: Tristan Madani <tr...@ta...> The jfsCommit kernel thread processes committed transactions from TxAnchor.unlock_queue via jfs_lazycommit(). During filesystem unmount, jfs_umount() calls jfs_flush_journal(log, 2) which waits for the log commit queue (log->cqueue) to drain. However, after log I/O completes, lazy transactions are moved to TxAnchor.unlock_queue for asynchronous processing by jfsCommit. If jfs_umount() proceeds to free the jfs_log (via lmLogClose) or jfs_sb_info (via kfree in jfs_put_super) while entries referencing this superblock remain on unlock_queue, the jfsCommit thread will access freed memory when it later processes these entries: - jfs_lazycommit reads sbi->commit_state (UAF of jfs_sb_info) - txLazyCommit accesses JFS_SBI(tblk->sb)->log and takes log->gclock (UAF of jfs_log) Add txLazyDrain() which waits for all entries in TxAnchor.unlock_queue belonging to the unmounting superblock to be processed, and also waits for any in-flight txLazyCommit (IN_LAZYCOMMIT) for this superblock to complete. Call it from both jfs_umount() and jfs_umount_rw() after jfs_flush_journal(). Reported-by: syz...@sy... Closes: https://syzkaller.appspot.com/bug?extid=c244f4a09ca85dd2ebc1 Tested-by: syz...@sy... Reported-by: syz...@sy... Closes: https://syzkaller.appspot.com/bug?extid=885a4f3281b8d99c48d8 Tested-by: syz...@sy... Fixes: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 ("Linux-2.6.12-rc2") Cc: st...@vg... Signed-off-by: Tristan Madani <tr...@ta...> --- fs/jfs/jfs_txnmgr.c | 35 +++++++++++++++++++++++++++++++++++ fs/jfs/jfs_txnmgr.h | 1 + fs/jfs/jfs_umount.c | 8 ++++++++ 3 files changed, 44 insertions(+) diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index 083dbbb0c3268..67a9908b5a4d9 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -2791,6 +2791,41 @@ void txLazyUnlock(struct tblock * tblk) LAZY_UNLOCK(flags); } + +/* + * txLazyDrain + * + * Wait for all pending lazy commit entries for this superblock + * to be processed by the jfsCommit thread. Must be called + * before freeing per-filesystem structures during unmount. + */ +void txLazyDrain(struct super_block *sb) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + struct tblock *tblk; + unsigned long flags; + bool found; + + do { + found = false; + LAZY_LOCK(flags); + list_for_each_entry(tblk, &TxAnchor.unlock_queue, cqueue) { + if (tblk->sb == sb) { + found = true; + break; + } + } + if (!found && (sbi->commit_state & IN_LAZYCOMMIT)) + found = true; + LAZY_UNLOCK(flags); + + if (found) { + wake_up(&jfs_commit_thread_wait); + schedule_timeout_uninterruptible(1); + } + } while (found); +} + static void LogSyncRelease(struct metapage * mp) { struct jfs_log *log = mp->log; diff --git a/fs/jfs/jfs_txnmgr.h b/fs/jfs/jfs_txnmgr.h index ba71eb5ced567..fbbaed26c52bd 100644 --- a/fs/jfs/jfs_txnmgr.h +++ b/fs/jfs/jfs_txnmgr.h @@ -291,6 +291,7 @@ extern void txFreelock(struct inode *); extern int lmLog(struct jfs_log *, struct tblock *, struct lrd *, struct tlock *); extern void txQuiesce(struct super_block *); +extern void txLazyDrain(struct super_block *sb); extern void txResume(struct super_block *); extern void txLazyUnlock(struct tblock *); extern int jfs_lazycommit(void *); diff --git a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c index 18569f1eaabdb..657707361be2a 100644 --- a/fs/jfs/jfs_umount.c +++ b/fs/jfs/jfs_umount.c @@ -58,6 +58,13 @@ int jfs_umount(struct super_block *sb) */ jfs_flush_journal(log, 2); + /* + * Drain any pending lazy commit entries for this filesystem so + * the jfsCommit thread does not access freed structures. + */ + if (log) + txLazyDrain(sb); + /* * Hold log lock so write_special_inodes (lmLogSync) cannot see * this sbi with a NULL inode pointer while iterating log->sb_list. @@ -142,6 +149,7 @@ int jfs_umount_rw(struct super_block *sb) * remove file system from log active file system list. */ jfs_flush_journal(log, 2); + txLazyDrain(sb); /* * Make sure all metadata makes it to disk -- 2.47.3 |
|
From: Tristan M. <tr...@gm...> - 2026-05-01 11:02:39
|
From: Tristan Madani <tr...@ta...> diWrite() copies btree root data from the in-memory inode to the on-disk dinode using lv->offset and lv->length from the transaction lock without bounds checking. When a corrupted JFS filesystem image provides inconsistent dtree or xtree metadata, the transaction log entries can reference slots beyond the root node boundaries (DTROOTMAXSLOT or XTROOTMAXSLOT), causing a slab-out-of-bounds write in the subsequent memcpy. For example, with a crafted directory inode where the dtree metadata produces lv->offset + lv->length > DTROOTMAXSLOT (9), the memcpy in the dtree copy loop writes 32 bytes past the dinode boundary into adjacent slab memory. Add bounds validation before each memcpy in both the xtree and dtree copy loops to ensure lv->offset + lv->length does not exceed XTROOTMAXSLOT (18) or DTROOTMAXSLOT (9) respectively. Reported-by: syz...@sy... Closes: https://syzkaller.appspot.com/bug?extid=aa6df9d3b383bf5f047f Tested-by: syz...@sy... Fixes: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 ("Linux-2.6.12-rc2") Cc: st...@vg... Signed-off-by: Tristan Madani <tr...@ta...> --- fs/jfs/jfs_imap.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index b84ba4d7dfb44..70d6a33597273 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -726,6 +726,11 @@ int diWrite(tid_t tid, struct inode *ip) xp = &dp->di_xtroot; lv = ilinelock->lv; for (n = 0; n < ilinelock->index; n++, lv++) { + if (lv->offset + lv->length > XTROOTMAXSLOT) { + jfs_err("diWrite: xtree lv out of bounds"); + release_metapage(mp); + return -EIO; + } memcpy(&xp->xad[lv->offset], &p->xad[lv->offset], lv->length << L2XTSLOTSIZE); } @@ -750,6 +755,11 @@ int diWrite(tid_t tid, struct inode *ip) xp = (dtpage_t *) & dp->di_dtroot; lv = ilinelock->lv; for (n = 0; n < ilinelock->index; n++, lv++) { + if (lv->offset + lv->length > DTROOTMAXSLOT) { + jfs_err("diWrite: dtree lv out of bounds"); + release_metapage(mp); + return -EIO; + } memcpy(&xp->slot[lv->offset], &p->slot[lv->offset], lv->length << L2DTSLOTSIZE); } -- 2.47.3 |
|
From: Jori K. <jko...@xs...> - 2026-05-01 09:58:08
|
> Op 01-05-2026 09:10 CEST schreef Hongling Zeng <zen...@ky...>:
>
>
> Return NULL instead of passing to ERR_PTR while err is zero
> Fixes these smatch warnings:
> - fs/jfs/namei.c:311 jfs_mkdir() warn: passing zero to 'ERR_PTR'
> - fs/orangefs/namei.c:369 orangefs_mkdir() warn: passing zero
> to 'ERR_PTR'
>
> Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *")
> Signed-off-by: Hongling Zeng <zen...@ky...>
> ---
> fs/jfs/namei.c | 2 +-
> fs/orangefs/namei.c | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
> index 60c4a0e0fca5..442d62679262 100644
> --- a/fs/jfs/namei.c
> +++ b/fs/jfs/namei.c
> @@ -309,7 +309,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip,
> out1:
>
> jfs_info("jfs_mkdir: rc:%d", rc);
> - return ERR_PTR(rc);
> + return rc ? ERR_PTR(rc) : NULL;
> }
>
> /*
> diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c
> index bec5475de094..75e65e72c2d6 100644
> --- a/fs/orangefs/namei.c
> +++ b/fs/orangefs/namei.c
> @@ -362,7 +362,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
> __orangefs_setattr(dir, &iattr);
> out:
> op_release(new_op);
> - return ERR_PTR(ret);
> + return ret ? ERR_PTR(ret) : NULL;
> }
>
> static int orangefs_rename(struct mnt_idmap *idmap,
> --
> 2.25.1
Sure. Feel free to add:
Reviewed-by: Jori Koolstra <jko...@xs...>
However, I will say jfs is kinda low priority, so maybe David will not pick it up.
|
|
From: Hongling Z. <zen...@ky...> - 2026-05-01 07:26:43
|
Return NULL instead of passing to ERR_PTR while err is zero
Fixes these smatch warnings:
- fs/jfs/namei.c:311 jfs_mkdir() warn: passing zero to 'ERR_PTR'
- fs/orangefs/namei.c:369 orangefs_mkdir() warn: passing zero
to 'ERR_PTR'
Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *")
Signed-off-by: Hongling Zeng <zen...@ky...>
---
fs/jfs/namei.c | 2 +-
fs/orangefs/namei.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 60c4a0e0fca5..442d62679262 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -309,7 +309,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip,
out1:
jfs_info("jfs_mkdir: rc:%d", rc);
- return ERR_PTR(rc);
+ return rc ? ERR_PTR(rc) : NULL;
}
/*
diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c
index bec5475de094..75e65e72c2d6 100644
--- a/fs/orangefs/namei.c
+++ b/fs/orangefs/namei.c
@@ -362,7 +362,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
__orangefs_setattr(dir, &iattr);
out:
op_release(new_op);
- return ERR_PTR(ret);
+ return ret ? ERR_PTR(ret) : NULL;
}
static int orangefs_rename(struct mnt_idmap *idmap,
--
2.25.1
|
|
From: syzbot <syz...@sy...> - 2026-04-29 20:32:48
|
Hello jfs maintainers/developers, This is a 31-day syzbot report for the jfs subsystem. All related reports/information can be found at: https://syzkaller.appspot.com/upstream/s/jfs During the period, 1 new issues were detected and 0 were fixed. In total, 69 issues are still open and 73 have already been fixed. Some of the still happening issues: Ref Crashes Repro Title <1> 99418 Yes kernel BUG in jfs_evict_inode https://syzkaller.appspot.com/bug?extid=9c0c58ea2e4887ab502e <2> 53092 Yes WARNING in dbAdjTree https://syzkaller.appspot.com/bug?extid=ab18fa9c959320611727 <3> 45669 Yes kernel BUG in txUnlock https://syzkaller.appspot.com/bug?extid=a63afa301d1258d09267 <4> 7788 Yes KASAN: slab-use-after-free Write in dtSplitPage https://syzkaller.appspot.com/bug?extid=a939a1121380d3212940 <5> 6948 Yes kernel BUG in dbFindLeaf https://syzkaller.appspot.com/bug?extid=dcea2548c903300a400e <6> 6184 Yes general protection fault in txEnd https://syzkaller.appspot.com/bug?extid=1d096d31de6a0491b55e <7> 4956 Yes INFO: task hung in lock_metapage https://syzkaller.appspot.com/bug?extid=1d84a1682e4673d5c4fb <8> 4460 Yes general protection fault in jfs_flush_journal https://syzkaller.appspot.com/bug?extid=194bfe3476f96782c0b6 <9> 4005 Yes KASAN: use-after-free Read in jfs_lazycommit https://syzkaller.appspot.com/bug?extid=885a4f3281b8d99c48d8 <10> 3252 Yes UBSAN: array-index-out-of-bounds in dtInsertEntry https://syzkaller.appspot.com/bug?extid=5f7f0caf9979e9d09ff8 --- This report is generated by a bot. It may contain errors. See https://goo.gl/tpsmEJ for more information about syzbot. syzbot engineers can be reached at syz...@go.... To disable reminders for individual bugs, reply with the following command: #syz set <Ref> no-reminders To change bug's subsystems, reply with: #syz set <Ref> subsystems: new-subsystem You may send multiple commands in a single email message. |
|
From: Mashiro C. <mas...@ma...> - 2026-04-24 21:10:36
|
During JFS log shutdown, log buffer pages can be freed while lower-layer loop workers are still copying from them, triggering a use-after-free reported by syzbot in copy_folio_from_iter_atomic(). Track in-flight log I/O in struct jfs_log and wait for completion before freeing log buffers. Add io_inflight/io_waitq, increment io_inflight before submitting BIOs in lbmRead() and lbmStartIO(), and decrement it from lbmIODone() on all completion paths, including READ and DIRECT early-return paths. This closes the teardown race between buffer page free and late I/O completion. Reported-by: syz...@sy... Closes: https://syzkaller.appspot.com/bug?extid=6cc93ec9a4035badb85f Signed-off-by: Mashiro Chen <mas...@ma...> --- fs/jfs/jfs_logmgr.c | 17 +++++++++++++++-- fs/jfs/jfs_logmgr.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index ada00d5bc214..a45c8e2559e4 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -1806,6 +1806,8 @@ static int lbmLogInit(struct jfs_log * log) * avoid deadlock here. */ init_waitqueue_head(&log->free_wait); + init_waitqueue_head(&log->io_waitq); + atomic_set(&log->io_inflight, 0); log->lbuf_free = NULL; @@ -1857,6 +1859,8 @@ static void lbmLogShutdown(struct jfs_log * log) jfs_info("lbmLogShutdown: log:0x%p", log); + wait_event(log->io_waitq, !atomic_read(&log->io_inflight)); + lbuf = log->lbuf_free; while (lbuf) { struct lbuf *next = lbuf->l_freelist; @@ -1978,6 +1982,7 @@ static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp) bio->bi_end_io = lbmIODone; bio->bi_private = bp; + atomic_inc(&log->io_inflight); /*check if journaling to disk has been disabled*/ if (log->no_integrity) { bio->bi_iter.bi_size = 0; @@ -2124,6 +2129,7 @@ static void lbmStartIO(struct lbuf * bp) bio->bi_end_io = lbmIODone; bio->bi_private = bp; + atomic_inc(&log->io_inflight); /* check if journaling to disk has been disabled */ if (log->no_integrity) { @@ -2170,7 +2176,7 @@ static void lbmIODone(struct bio *bio) { struct lbuf *bp = bio->bi_private; struct lbuf *nextbp, *tail; - struct jfs_log *log; + struct jfs_log *log = bp->l_log; unsigned long flags; /* @@ -2201,6 +2207,9 @@ static void lbmIODone(struct bio *bio) /* wakeup I/O initiator */ LCACHE_WAKEUP(&bp->l_ioevent); + if (atomic_dec_and_test(&log->io_inflight)) + wake_up(&log->io_waitq); + return; } @@ -2220,12 +2229,13 @@ static void lbmIODone(struct bio *bio) INCREMENT(lmStat.pagedone); /* update committed lsn */ - log = bp->l_log; log->clsn = (bp->l_pn << L2LOGPSIZE) + bp->l_ceor; if (bp->l_flag & lbmDIRECT) { LCACHE_WAKEUP(&bp->l_ioevent); LCACHE_UNLOCK(flags); + if (atomic_dec_and_test(&log->io_inflight)) + wake_up(&log->io_waitq); return; } @@ -2305,6 +2315,9 @@ static void lbmIODone(struct bio *bio) LCACHE_UNLOCK(flags); /* unlock+enable */ } + + if (atomic_dec_and_test(&log->io_inflight)) + wake_up(&log->io_waitq); } int jfsIOWait(void *arg) diff --git a/fs/jfs/jfs_logmgr.h b/fs/jfs/jfs_logmgr.h index 8b8994e48cd0..59cb0aca99c5 100644 --- a/fs/jfs/jfs_logmgr.h +++ b/fs/jfs/jfs_logmgr.h @@ -400,6 +400,8 @@ struct jfs_log { uuid_t uuid; /* 16: 128-bit uuid of log device */ int no_integrity; /* 3: flag to disable journaling to disk */ + atomic_t io_inflight; + wait_queue_head_t io_waitq; }; /* -- 2.54.0 |
|
From: <l1z...@gm...> - 2026-04-20 16:02:47
|
From: Haocheng Yu <l1z...@gm...>
A BUG: unable to handle kernel paging request in dbAllocBits issue
is reported by a modified Syzkaller-based kernel fuzzing tool that
we developed.
The issue arises because dbAllocBits() uses bmp->db_agfree[agno],
and agno is used without verification after being calculated using
`blkno >> bmp->db_agl2size;`. And blkno is calculated using
`blkno = le64_to_cpu(dp->start) + (word << L2DBWORD);`. If a
corrupted image is read at runtime, an unreasonable db->start might
be read, causing agno to be excessively large.
To fix this vulnerability, I added the db_valid_agno() helper
function to verify the validity of blkno and agno. This verification
is added before all similar places that use bmp->db_agfree[agno],
including dbAllocBits(), dbFreeBits(), and dbAllocDmapBU().
Signed-off-by: Haocheng Yu <l1z...@gm...>
---
fs/jfs/jfs_dmap.c | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 88afd108c2dd..5f144dd92336 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -106,6 +106,21 @@ static int dbInitTree(struct dmaptree * dtp);
static int dbInitDmapCtl(struct dmapctl * dcp, int level, int i);
static int dbGetL2AGSize(s64 nblocks);
+static bool db_valid_agno(struct bmap *bmp, s64 blkno, int *agno)
+{
+ int idx;
+
+ if (unlikely(blkno < 0 || blkno >= bmp->db_mapsize))
+ return false;
+
+ idx = blkno >> bmp->db_agl2size;
+ if (unlikely(idx < 0 || idx >= bmp->db_numag || idx >= MAXAG))
+ return false;
+
+ *agno = idx;
+ return true;
+}
+
/*
* buddy table
*
@@ -2235,12 +2250,17 @@ static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
le32_add_cpu(&dp->nfree, -nblocks);
BMAP_LOCK(bmp);
+ if (unlikely(!db_valid_agno(bmp, blkno, &agno))) {
+ BMAP_UNLOCK(bmp);
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "invalid agno in %s\n", __func__);
+ return;
+ }
/* if this allocation group is completely free,
* update the maximum allocation group number if this allocation
* group is the new max.
*/
- agno = blkno >> bmp->db_agl2size;
if (agno > bmp->db_maxag)
bmp->db_maxag = agno;
@@ -2383,7 +2403,12 @@ static int dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
/* update the free count for the allocation group and
* map.
*/
- agno = blkno >> bmp->db_agl2size;
+ if (unlikely(!db_valid_agno(bmp, blkno, &agno))) {
+ BMAP_UNLOCK(bmp);
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "invalid agno in %s\n", __func__);
+ return -EIO;
+ }
bmp->db_nfree += nblocks;
bmp->db_agfree[agno] += nblocks;
@@ -3296,7 +3321,12 @@ static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
* update the highest active allocation group number
* if this allocation group is the new max.
*/
- agno = blkno >> bmp->db_agl2size;
+ if (unlikely(!db_valid_agno(bmp, blkno, &agno))) {
+ BMAP_UNLOCK(bmp);
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "invalid agno in %s\n", __func__);
+ return -EIO;
+ }
if (agno > bmp->db_maxag)
bmp->db_maxag = agno;
base-commit: ffc253263a1375a65fa6c9f62a893e9767fbebfa
--
2.51.0
|
|
From: Edward A. D. <ea...@qq...> - 2026-04-20 14:11:40
|
On Mon, 20 Apr 2026 09:08:12 -0400, Sasha Levin wrote: > From: Edward Adam Davis <ea...@qq...> > > [ Upstream commit b15e4310633f90072d66cc9b6692acbf6b4d7d00 ] > > In lbmRead(), the I/O event waited for by wait_event() finishes before > it goes to sleep, and the lbmIODone() prematurely sets the flag to > lbmDONE, thus ending the wait. This causes wait_event() to return before > lbmREAD is cleared (because lbmDONE was set first), the premature return > of wait_event() leads to the release of lbuf before lbmIODone() returns, > thus triggering the use-after-free vulnerability reported in [1]. > > Moving the operation of setting the lbmDONE flag to after clearing lbmREAD > in lbmIODone() avoids the use-after-free vulnerability reported in [1]. > > [1] > BUG: KASAN: slab-use-after-free in rt_spin_lock+0x88/0x3e0 kernel/locking/spinlock_rt.c:56 > Call Trace: > blk_update_request+0x57e/0xe60 block/blk-mq.c:1007 > blk_mq_end_request+0x3e/0x70 block/blk-mq.c:1169 > blk_complete_reqs block/blk-mq.c:1244 [inline] > blk_done_softirq+0x10a/0x160 block/blk-mq.c:1249 > > Allocated by task 6101: > lbmLogInit fs/jfs/jfs_logmgr.c:1821 [inline] > lmLogInit+0x3d0/0x19e0 fs/jfs/jfs_logmgr.c:1269 > open_inline_log fs/jfs/jfs_logmgr.c:1175 [inline] > lmLogOpen+0x4e1/0xfa0 fs/jfs/jfs_logmgr.c:1069 > jfs_mount_rw+0xe9/0x670 fs/jfs/jfs_mount.c:257 > jfs_fill_super+0x754/0xd80 fs/jfs/super.c:532 > > Freed by task 6101: > kfree+0x1bd/0x900 mm/slub.c:6876 > lbmLogShutdown fs/jfs/jfs_logmgr.c:1864 [inline] > lmLogInit+0x1137/0x19e0 fs/jfs/jfs_logmgr.c:1415 > open_inline_log fs/jfs/jfs_logmgr.c:1175 [inline] > lmLogOpen+0x4e1/0xfa0 fs/jfs/jfs_logmgr.c:1069 > jfs_mount_rw+0xe9/0x670 fs/jfs/jfs_mount.c:257 > jfs_fill_super+0x754/0xd80 fs/jfs/super.c:532 > > Reported-by: syz...@sy... > Closes: https://syzkaller.appspot.com/bug?extid=1d38eedcb25a3b5686a7 > Signed-off-by: Edward Adam Davis <ea...@qq...> > Signed-off-by: Dave Kleikamp <dav...@or...> > Signed-off-by: Sasha Levin <sa...@ke...> > --- > > LLM Generated explanations, may be completely bogus: > > Now I have all the information needed to complete the full analysis. Let > me compile the results. I fixed this issue a couple of days ago. Please see: https://lore.kernel.org/all/ten...@qq... Edward BR |
|
From: Sasha L. <sa...@ke...> - 2026-04-20 13:34:01
|
From: João Paredes <jo...@ya...>
[ Upstream commit 679330e4a7af1d102d035b13b2b9d41bc1dfbbf7 ]
The filesystem UUID was only being loaded into super_block sb when an
external journal device was in use. When mounting without an external
journal, the UUID remained unset, which prevented the computation of
a filesystem ID (fsid), which could be confirmed via `stat -f -c "%i"`
and thus user space could not use fanotify correctly.
A missing filesystem ID causes fanotify to return ENODEV when marking
the filesystem for events like FAN_CREATE, FAN_DELETE, FAN_MOVED_TO,
and FAN_MOVED_FROM. As a result, applications relying on fanotify
could not monitor these events on JFS filesystems without an external
journal.
Moved the UUID initialization so it is always performed during mount,
ensuring the superblock UUID is consistently available.
Signed-off-by: João Paredes <jo...@ya...>
Signed-off-by: Dave Kleikamp <dav...@or...>
Signed-off-by: Sasha Levin <sa...@ke...>
---
LLM Generated explanations, may be completely bogus:
Now I have all the information needed for a thorough analysis. Let me
compile it.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
### Step 1.1: PARSE THE SUBJECT LINE
- **Subsystem:** JFS (Journaled File System)
- **Action verb:** "always load" (correcting behavior that should have
been unconditional)
- **Summary:** Ensures the filesystem UUID is always loaded during
mount, not just when external journal is configured
- Record: [JFS] [always load / ensure] [Fix UUID not being loaded for
inline-log configurations, breaking fsid and fanotify]
### Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
- **Signed-off-by: João Paredes <jo...@ya...>** - Author of the
fix (no prior JFS commits found)
- **Signed-off-by: Dave Kleikamp <dav...@or...>** - JFS
maintainer, signed off = reviewed and accepted
- No Fixes: tag, no Reported-by, no Cc: stable (expected for this
pipeline)
- Record: Fix accepted by JFS subsystem maintainer. No explicit stable
nomination.
### Step 1.3: ANALYZE THE COMMIT BODY TEXT
The commit clearly describes the bug:
- **Bug:** UUID only loaded when external journal used (`!JFS_INLINELOG`
path); inline log (the default) left `sbi->uuid` unset (all zeros)
- **Symptom:** `stat -f -c "%i"` returns 0 (no filesystem ID); fanotify
returns `ENODEV` for `FAN_CREATE`, `FAN_DELETE`, `FAN_MOVED_TO`,
`FAN_MOVED_FROM`
- **Root cause:** `uuid_copy` was inside the else branch of the inline-
log conditional
- **Impact:** Applications relying on fanotify cannot monitor filesystem
events on JFS
- Record: Real functional bug with clear user-visible symptoms. Fanotify
breaks for the default JFS configuration.
### Step 1.4: DETECT HIDDEN BUG FIXES
Not hidden at all. The commit message is direct about the bug mechanism
and user-visible impact.
## PHASE 2: DIFF ANALYSIS - LINE BY LINE
### Step 2.1: INVENTORY THE CHANGES
- **File:** `fs/jfs/jfs_mount.c` only
- **Lines added:** 2 (uuid_copy + blank line before if/else)
- **Lines removed:** 1 (uuid_copy from else block)
- **Net change:** +1 line
- **Function modified:** `chkSuper()`
- **Scope:** Single-file, surgical one-line fix
- Record: [fs/jfs/jfs_mount.c: +2/-1] [chkSuper()] [Single-file surgical
fix]
### Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
**Before:**
```381:387:fs/jfs/jfs_mount.c
if (sbi->mntflag & JFS_INLINELOG)
sbi->logpxd = j_sb->s_logpxd;
else {
sbi->logdev =
new_decode_dev(le32_to_cpu(j_sb->s_logdev));
uuid_copy(&sbi->uuid, &j_sb->s_uuid);
uuid_copy(&sbi->loguuid, &j_sb->s_loguuid);
}
```
**After:** `uuid_copy(&sbi->uuid, &j_sb->s_uuid)` is moved BEFORE the
if/else block, making it unconditional. `uuid_copy(&sbi->loguuid, ...)`
stays in else (correct - only needed for external log).
### Step 2.3: IDENTIFY THE BUG MECHANISM
- **Category:** Logic/correctness fix - conditional initialization that
should be unconditional
- **What was broken:** UUID needed unconditionally (for fsid computation
in `jfs_statfs()`), but was only loaded in the external-log path
- **How the fix works:** Moves the uuid_copy call outside the
conditional
### Step 2.4: ASSESS THE FIX QUALITY
- **Obviously correct:** Yes - the UUID is a filesystem property, not a
journal-specific property
- **Minimal:** Yes - moving one line
- **Regression risk:** Essentially zero. For the inline-log case, UUID
is now properly populated (was zeros before). For the external-log
case, behavior is identical.
## PHASE 3: GIT HISTORY INVESTIGATION
### Step 3.1: BLAME THE CHANGED LINES
- The if/else structure dates to `^1da177e4c3f41` (Linux 2.6.12-rc2,
2005-04-16, Linus Torvalds) - present since initial git history
- The `uuid_copy` line was updated by `2e3bc6125154c6` (Andy Shevchenko,
2019-01-10) - UUID API conversion only, didn't change the placement
- The original code ALWAYS had the UUID copy inside the else branch
- Record: Buggy placement dates to the initial Linux git import
(v2.6.12). Present in ALL stable trees.
### Step 3.2: THE REAL FIXES TARGET
The fsid computation using `sbi->uuid` was added by `b5c816a4f1776`
(Coly Li, 2009-01-21, "jfs: return f_fsid for statfs(2)"). This commit
added the CRC32-based fsid computation but didn't realize the UUID was
only loaded for external-log configurations. So the actual bug was
introduced in 2009 when the UUID became functionally significant beyond
just external log tracking.
### Step 3.3-3.5: RELATED CHANGES
- No recent JFS changes to jfs_mount.c affect this area
- This is a standalone fix with no dependencies
- Author João Paredes has no other JFS commits (first-time contributor),
but the fix was accepted by JFS maintainer Dave Kleikamp
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
### Step 4.1-4.5
- Could not find the original patch submission on lore.kernel.org due to
bot protection
- The fix was accepted by Dave Kleikamp (JFS maintainer) which provides
strong quality signal
- No CVE was found for this specific issue
## PHASE 5: CODE SEMANTIC ANALYSIS
### Step 5.1-5.2: KEY FUNCTIONS AND CALLERS
The impacted code path:
1. `chkSuper()` is called during `jfs_mount()` (line 83) and during
`jfs_mount_rw()` remount (line 232)
2. `sbi->uuid` is consumed by:
- `jfs_statfs()` in `fs/jfs/super.c` (lines 146-150) - computes
`f_fsid` via CRC32 of UUID
- `lmLogFileSystem()` in `fs/jfs/jfs_logmgr.c` (line 1713) - tracks
active filesystems in log
3. `jfs_statfs()` is the VFS `.statfs` operation, called via
`vfs_statfs()` and `vfs_get_fsid()`
4. `fanotify_test_fsid()` calls `vfs_get_fsid()` which calls
`jfs_statfs()` - confirmed in `fs/notify/fanotify/fanotify_user.c`
line 1771
### Step 5.3-5.4: FANOTIFY ENODEV MECHANISM CONFIRMED
In `fanotify_test_fsid()` (line 1776):
```1776:1779:fs/notify/fanotify/fanotify_user.c
if (!fsid->id.val[0] && !fsid->id.val[1]) {
err = -ENODEV;
goto weak;
}
```
When `sbi->uuid` is all zeros (kzalloc'd), `crc32_le(0, zeros, N) = 0`
(CRC is linear, zero input with zero init = zero output). So `f_fsid =
{0, 0}`, triggering the ENODEV path. The `weak` label returns `-ENODEV`
for non-inode marks (FAN_MARK_FILESYSTEM, FAN_MARK_MOUNT).
### Step 5.5: SIMILAR PATTERNS
The comment at line 1769 explicitly says: "Make sure dentry is not of a
filesystem with zero fsid (e.g. fuse)." JFS was silently in this same
broken category for inline-log configs.
## PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS
### Step 6.1: BUGGY CODE EXISTS IN ALL STABLE TREES
Confirmed via `git show v7.0-rc7:fs/jfs/jfs_mount.c` - the buggy
conditional UUID loading is present. The code has been this way since
the initial Linux git import and exists in ALL active stable trees.
### Step 6.2: BACKPORT COMPLICATIONS
None. The patch will apply cleanly - the surrounding code in
`chkSuper()` has been unchanged for years.
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
### Step 7.1: SUBSYSTEM CRITICALITY
- **Subsystem:** JFS filesystem (fs/jfs/)
- **Criticality:** IMPORTANT - filesystem correctness affects data and
application behavior
- JFS is a mature filesystem used particularly in enterprise
environments
### Step 7.2: SUBSYSTEM ACTIVITY
JFS receives minimal but ongoing maintenance, mostly bug fixes.
## PHASE 8: IMPACT AND RISK ASSESSMENT
### Step 8.1: WHO IS AFFECTED
All JFS users mounting without an external journal (the default
configuration). This is the vast majority of JFS users.
### Step 8.2: TRIGGER CONDITIONS
- **How common:** Every single mount of a JFS filesystem with inline log
- **User trigger:** Any application using fanotify with
`FAN_MARK_FILESYSTEM` or `FAN_MARK_MOUNT` on JFS
- **No special privileges needed** beyond normal fanotify access
### Step 8.3: FAILURE MODE SEVERITY
- **Failure:** Applications using fanotify (file monitoring, security
tools, backup software) get ENODEV and cannot monitor JFS filesystems
- **Severity:** HIGH - functional breakage of a standard kernel
interface for a standard filesystem configuration
- Not a crash or security issue, but a clear functional bug affecting
userspace applications
### Step 8.4: RISK-BENEFIT RATIO
- **Benefit:** HIGH - fixes fanotify for all JFS users with inline log
(the common case)
- **Risk:** VERY LOW - 1 line moved, obviously correct, no possible
regression
- **Ratio:** Excellent
## PHASE 9: FINAL SYNTHESIS
### Step 9.1: EVIDENCE COMPILATION
**FOR backporting:**
- Fixes a real, user-visible bug: fanotify returns ENODEV for default
JFS configurations
- Extremely small and surgical fix: 1 line moved
- Obviously correct: UUID is a filesystem property, not journal-specific
- Accepted by JFS maintainer (Dave Kleikamp)
- Bug affects ALL stable trees (code unchanged since initial git import)
- Patch applies cleanly
- Zero regression risk
- Bug has existed since 2009 (fsid computation added)
**AGAINST backporting:**
- No Fixes: tag or Cc: stable (expected - that's why it needs review)
- No syzbot/KASAN/crash - it's a functional bug, not a memory safety
issue
- Author is a first-time contributor (but fix accepted by maintainer)
### Step 9.2: STABLE RULES CHECKLIST
1. Obviously correct and tested? **YES** - trivially verifiable,
accepted by maintainer
2. Fixes a real bug? **YES** - fanotify ENODEV for default JFS
configuration
3. Important issue? **YES** - breaks standard kernel interface for
application monitoring
4. Small and contained? **YES** - 1 line moved in 1 file
5. No new features or APIs? **YES** - just fixes existing behavior
6. Can apply to stable? **YES** - will apply cleanly to all stable trees
### Step 9.3: EXCEPTION CATEGORIES
Not an exception category - this is a standard bug fix.
## Verification
- [Phase 1] Parsed tags: Signed-off-by from author and JFS maintainer
Dave Kleikamp
- [Phase 2] Diff analysis: 1 line moved from else block to unconditional
execution in `chkSuper()`
- [Phase 3] git blame: buggy conditional structure present since
`^1da177e4c3f41` (v2.6.12, 2005); fsid computation added in
`b5c816a4f1776` (2009) made it functionally significant
- [Phase 3] git show v7.0-rc7: confirmed buggy code exists unchanged in
v7.0 stable tree
- [Phase 5] Traced call chain: `fanotify_test_fsid()` ->
`vfs_get_fsid()` -> `jfs_statfs()` -> CRC32 of `sbi->uuid`; confirmed
zero-UUID produces zero fsid which triggers ENODEV at
fanotify_user.c:1776-1777
- [Phase 5] Confirmed `sbi` allocated via `kzalloc_obj` in super.c:452,
so uuid is zero-initialized
- [Phase 5] Confirmed `JFS_INLINELOG` (0x00000800) is the default/common
JFS configuration
- [Phase 6] Code is identical across all stable trees - patch applies
cleanly
- [Phase 8] Failure mode: fanotify returns -ENODEV for
FAN_MARK_FILESYSTEM/MOUNT on default JFS, severity HIGH
- UNVERIFIED: Could not access lore.kernel.org for original patch
discussion (bot protection blocked access)
**YES**
fs/jfs/jfs_mount.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/jfs/jfs_mount.c b/fs/jfs/jfs_mount.c
index 52e6b58c5dbd2..dac822f150701 100644
--- a/fs/jfs/jfs_mount.c
+++ b/fs/jfs/jfs_mount.c
@@ -378,11 +378,12 @@ static int chkSuper(struct super_block *sb)
sbi->nbperpage = PSIZE >> sbi->l2bsize;
sbi->l2nbperpage = L2PSIZE - sbi->l2bsize;
sbi->l2niperblk = sbi->l2bsize - L2DISIZE;
+ uuid_copy(&sbi->uuid, &j_sb->s_uuid);
+
if (sbi->mntflag & JFS_INLINELOG)
sbi->logpxd = j_sb->s_logpxd;
else {
sbi->logdev = new_decode_dev(le32_to_cpu(j_sb->s_logdev));
- uuid_copy(&sbi->uuid, &j_sb->s_uuid);
uuid_copy(&sbi->loguuid, &j_sb->s_loguuid);
}
sbi->fsckpxd = j_sb->s_fsckpxd;
--
2.53.0
|
|
From: Sasha L. <sa...@ke...> - 2026-04-20 13:30:35
|
From: Yun Zhou <yun...@wi...>
[ Upstream commit c83abc766aeb153e69cb46363bf7c9de0c9f3268 ]
Add check_dtroot() to validate dtroot_t integrity, focusing on preventing
index/pointer overflows from on-disk corruption.
Key checks:
- freecnt bounded by [0, DTROOTMAXSLOT-1] (slot[0] reserved for header).
- freelist validity: -1 when freecnt=0; 1~DTROOTMAXSLOT-1 when non-zero,
with linked list checks (no duplicates, proper termination via next=-1).
- stbl bounds: nextindex within stbl array size; entries within 0~8, no
duplicates (excluding idx=0).
Invoked in copy_from_dinode() when loading directory inodes, catching
corruption early before directory operations trigger out-of-bounds access.
This fixes the following UBSAN warning.
[ 101.832754][ T5960] ------------[ cut here ]------------
[ 101.832762][ T5960] UBSAN: array-index-out-of-bounds in fs/jfs/jfs_dtree.c:3713:8
[ 101.832792][ T5960] index -1 is out of range for type 'struct dtslot[128]'
[ 101.832807][ T5960] CPU: 2 UID: 0 PID: 5960 Comm: 5f7f0caf9979e9d Tainted: G E 6.18.0-rc4-00250-g2603eb907f03 #119 PREEMPT_{RT,(full
[ 101.832817][ T5960] Tainted: [E]=UNSIGNED_MODULE
[ 101.832819][ T5960] Hardware name: QEMU Ubuntu 25.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 101.832823][ T5960] Call Trace:
[ 101.832833][ T5960] <TASK>
[ 101.832838][ T5960] dump_stack_lvl+0x189/0x250
[ 101.832909][ T5960] ? __pfx_dump_stack_lvl+0x10/0x10
[ 101.832925][ T5960] ? __pfx__printk+0x10/0x10
[ 101.832934][ T5960] ? rt_mutex_slowunlock+0x493/0x8a0
[ 101.832959][ T5960] ubsan_epilogue+0xa/0x40
[ 101.832966][ T5960] __ubsan_handle_out_of_bounds+0xe9/0xf0
[ 101.833007][ T5960] dtInsertEntry+0x936/0x1430 [jfs]
[ 101.833094][ T5960] dtSplitPage+0x2c8b/0x3ed0 [jfs]
[ 101.833177][ T5960] ? __pfx_rt_mutex_slowunlock+0x10/0x10
[ 101.833193][ T5960] dtInsert+0x109b/0x6000 [jfs]
[ 101.833283][ T5960] ? rt_mutex_slowunlock+0x493/0x8a0
[ 101.833296][ T5960] ? __pfx_rt_mutex_slowunlock+0x10/0x10
[ 101.833307][ T5960] ? rt_spin_unlock+0x161/0x200
[ 101.833315][ T5960] ? __pfx_dtInsert+0x10/0x10 [jfs]
[ 101.833391][ T5960] ? txLock+0xaf9/0x1cb0 [jfs]
[ 101.833477][ T5960] ? dtInitRoot+0x22a/0x670 [jfs]
[ 101.833556][ T5960] jfs_mkdir+0x6ec/0xa70 [jfs]
[ 101.833636][ T5960] ? __pfx_jfs_mkdir+0x10/0x10 [jfs]
[ 101.833721][ T5960] ? generic_permission+0x2e5/0x690
[ 101.833760][ T5960] ? bpf_lsm_inode_mkdir+0x9/0x20
[ 101.833776][ T5960] vfs_mkdir+0x306/0x510
[ 101.833786][ T5960] do_mkdirat+0x247/0x590
[ 101.833795][ T5960] ? __pfx_do_mkdirat+0x10/0x10
[ 101.833804][ T5960] ? getname_flags+0x1e5/0x540
[ 101.833815][ T5960] __x64_sys_mkdir+0x6c/0x80
[ 101.833823][ T5960] do_syscall_64+0xfa/0xfa0
[ 101.833832][ T5960] ? lockdep_hardirqs_on+0x9c/0x150
[ 101.833840][ T5960] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 101.833847][ T5960] ? exc_page_fault+0xab/0x100
[ 101.833856][ T5960] entry_SYSCALL_64_after_hwframe+0x77/0x7f
Signed-off-by: Yun Zhou <yun...@wi...>
Signed-off-by: Dave Kleikamp <dav...@or...>
Signed-off-by: Sasha Levin <sa...@ke...>
---
LLM Generated explanations, may be completely bogus:
## Complete Analysis
### PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 - Subject Line:**
- Subsystem: `jfs` (JFS filesystem, directory B+-tree)
- Action verb: "add" (integrity check to "prevent" out-of-bounds)
- Summary: Adds dtroot_t integrity validation to catch corrupted on-disk
data before it triggers UBSAN/OOB access
**Step 1.2 - Tags:**
- `Signed-off-by: Yun Zhou <yun...@wi...>` - author
- `Signed-off-by: Dave Kleikamp <dav...@or...>` - JFS
maintainer; applied the patch
- No Fixes: tag (expected for manual review)
- No Reported-by: tag
- No Cc: stable tag
**Step 1.3 - Commit Body:**
- Bug: Corrupted on-disk `dtroot_t` structure can cause out-of-bounds
array access (index -1 into `struct dtslot[128]`)
- Concrete UBSAN trace provided: `index -1 is out of range for type
'struct dtslot[128]'` in `dtInsertEntry+0x936`
- Call chain: `do_syscall_64` -> `do_mkdirat` -> `vfs_mkdir` ->
`jfs_mkdir` -> `dtInsert` -> `dtSplitPage` -> `dtInsertEntry` -> crash
- Root cause: No validation of `freecnt`, `freelist`, or `stbl` fields
in dtroot after loading from disk
**Step 1.4 - Hidden Bug Fix?**
Yes. Although labeled "add check", this is a bug fix: it prevents a
concrete UBSAN out-of-bounds access from corrupted filesystem metadata.
The crash trace is real and reproducible.
---
### PHASE 2: DIFF ANALYSIS
**Step 2.1 - Inventory:**
- `fs/jfs/jfs_dtree.c`: +86 lines (new `check_dtroot()` function)
- `fs/jfs/jfs_dtree.h`: +2 lines (extern declaration)
- `fs/jfs/jfs_imap.c`: +4 lines (call site in `copy_from_dinode()`)
- Total: ~92 lines added, 0 removed
- Scope: Single new validation function + one call site
**Step 2.2 - Code Flow Change:**
- Before: `copy_from_dinode()` blindly copies dtroot data from disk
inode via `memcpy(&jfs_ip->u.dir, &dip->u._dir, 384)` with no
validation
- After: After the memcpy, `check_dtroot()` validates the structure. If
corrupt, returns `-EIO` early
**Step 2.3 - Bug Mechanism:**
Category: **Buffer overflow / out-of-bounds access** from on-disk
corruption
The crash path:
1. `copy_from_dinode()` loads a directory inode with corrupted `freelist
= -1` but `freecnt > 0`
2. `dtInsertEntry()` at line ~3651: `hsi = fsi = p->header.freelist;`
(fsi = -1)
3. Line ~3652: `h = &p->slot[fsi];` => `p->slot[-1]` => UBSAN out-of-
bounds
4. UBSAN warning confirmed in commit message at `jfs_dtree.c:3713`
The validation checks:
- `freecnt` bounded by [0, DTROOTMAXSLOT-1] (slot[0] is the header)
- `freelist = -1` when `freecnt = 0`; `freelist` in range [1,
DTROOTMAXSLOT-1] when non-zero
- Free list traversal: no duplicates, proper termination via `next = -1`
- `nextindex` within stbl array size
- stbl entries in valid range [0, 8], no duplicates
**Step 2.4 - Fix Quality:**
- Obviously correct: Each check validates a specific documented
constraint of the dtroot_t structure (see header definition at
`jfs_dtree.h:132-147`)
- Self-contained: entirely a new function + one call site
- Regression risk: Very low. Only adds validation at inode load time.
Worst case is a false positive rejecting a valid filesystem, but the
checks match the documented constraints precisely.
---
### PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1 - Blame:**
The buggy code in `copy_from_dinode()` at line 3103-3104
(`memcpy(&jfs_ip->u.dir, &dip->u._dir, 384)`) dates back to
`^1da177e4c3f41` (Linus Torvalds, 2005-04-16) - the initial Linux git
import. This means the vulnerability has existed since JFS was first
added to Linux.
**Step 3.2 - No Fixes: tag** to follow. Expected for manual review.
**Step 3.3 - Related Changes:**
JFS has a strong pattern of similar corruption-defense fixes:
- `a8dfb21689069` - "jfs: add index corruption check to DT_GETPAGE()"
(syzbot-reported)
- `5dff41a863775` - "jfs: fix array-index-out-of-bounds read in
add_missing_indices" (syzbot-reported)
- `27e56f59bab5d` - "UBSAN: array-index-out-of-bounds in dtSplitRoot"
- `7a5aa54fba2bd` - "jfs: Verify inode mode when loading from disk"
(syzbot-reported)
This commit follows the established pattern and is standalone (no
dependencies).
**Step 3.4 - Author:**
Yun Zhou (Wind River) is not the JFS maintainer but has contributed JFS
fixes before (linelock array bounds fix). Dave Kleikamp (Oracle, JFS
maintainer) signed off.
**Step 3.5 - Dependencies:**
No dependencies. The `check_dtroot()` function uses only existing
types/constants (`dtroot_t`, `DTROOTMAXSLOT`, `DECLARE_BITMAP`,
`jfs_err`) that exist in all stable trees. The call site in
`copy_from_dinode()` adds a simple check after an existing `memcpy`.
---
### PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1:** b4 dig could not find the original submission (the commit
may be too new or the patch-id didn't match). Web searches found related
patches by Yun Zhou on JFS (linelock fix) but not this specific patch.
Lore was blocked by anti-bot protection.
**Step 4.2:** Could not retrieve the full reviewer list. However, Dave
Kleikamp (JFS maintainer) applied the patch.
**Step 4.3:** No specific external bug report (no Reported-by tag). The
UBSAN trace in the commit message serves as the bug report.
**Step 4.4-4.5:** This appears to be a standalone patch, not part of a
series.
---
### PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 - Key Functions:**
- `check_dtroot()` - NEW function (validation)
- `copy_from_dinode()` - MODIFIED (call site)
**Step 5.2 - Callers of `copy_from_dinode()`:**
- `diRead()` (line 384) - main inode read path, called from `jfs_iget()`
- `diReadSpecial()` (line 459) - special inode read
Both are core inode loading paths. Every JFS inode read goes through
here.
**Step 5.3 - The crash path:**
`dtInsertEntry()` (line 3630) uses `p->header.freelist` as array index
without validation:
```3651:3652:fs/jfs/jfs_dtree.c
hsi = fsi = p->header.freelist;
h = &p->slot[fsi];
```
If `freelist = -1` (or any invalid value), `p->slot[fsi]` is out-of-
bounds.
**Step 5.4 - Call Chain Reachability:**
`mkdir` syscall -> `vfs_mkdir` -> `jfs_mkdir` -> `dtInsert` ->
`dtSplitPage` -> `dtInsertEntry` -> CRASH. This is reachable from
unprivileged userspace on any mounted JFS filesystem.
---
### PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS
**Step 6.1:** The buggy code (`copy_from_dinode` without dtroot
validation) exists in ALL stable trees since the code dates to the
initial git import (2005). Confirmed: `jfs_imap.c` is present in v5.15,
v6.1, and v6.6 stable trees with the same vulnerable pattern.
**Step 6.2:** The patch should apply cleanly. The `copy_from_dinode()`
function in `jfs_imap.c` has only had minor changes (e.g., nlink
checking, xtree definition). The core `if (S_ISDIR) memcpy` block is
unchanged across all stable trees.
**Step 6.3:** No existing fix for this specific dtroot validation issue
in any stable tree.
---
### PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
- Subsystem: JFS filesystem (`fs/jfs/`)
- Criticality: IMPORTANT - filesystem bugs can cause data corruption;
JFS is still used in production
- JFS is mature/stable - bugs have been present for decades
- Active pattern of syzbot-found corruption fixes being backported to
stable
---
### PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1 - Who is affected:** All JFS filesystem users
**Step 8.2 - Trigger:** Mounting a JFS filesystem with corrupted
directory inode metadata (can happen from disk failure, intentionally
crafted image)
**Step 8.3 - Failure mode:** UBSAN out-of-bounds array access →
potential memory corruption → kernel crash or security vulnerability.
Severity: **HIGH**
**Step 8.4 - Risk-Benefit:**
- BENEFIT: HIGH - prevents a concrete crash from corrupted on-disk data,
affecting a code path reachable from userspace
- RISK: LOW - purely additive validation code, self-contained, no
behavioral changes to normal operation
- Size concern: ~90 lines of new code is on the larger side, but it's
all straightforward bounds-checking logic
---
### PHASE 9: FINAL SYNTHESIS
**Evidence FOR backporting:**
1. Fixes a concrete UBSAN out-of-bounds access with a real crash trace
2. The crash is reachable from userspace (mkdir syscall on JFS)
3. Buggy code has existed since 2005 - affects ALL stable trees
4. JFS maintainer (Dave Kleikamp) signed off
5. Self-contained fix with no dependencies on other patches
6. Follows established JFS pattern of on-disk corruption checks (many
similar fixes already in stable)
7. The validation logic is straightforward and obviously correct
8. Should apply cleanly to all stable trees
**Evidence AGAINST backporting:**
1. ~90 lines of new code is somewhat large for stable
2. No Reported-by: or syzbot tag (though the UBSAN trace demonstrates
the issue)
3. No explicit Cc: stable nomination
**Assessment:** The benefits clearly outweigh the risks. The fix
prevents a real OOB access from corrupted filesystem metadata, follows
an established pattern of JFS hardening that has been extensively
backported, and is self-contained. The ~90 line size is acceptable
because it's purely validation logic.
---
### Verification
- [Phase 1] Parsed tags: Signed-off-by from author (Yun Zhou) and JFS
maintainer (Dave Kleikamp). No Fixes:/Reported-by:/Cc: stable.
- [Phase 2] Diff analysis: ~92 lines added across 3 files. New
check_dtroot() validation function + call in copy_from_dinode() +
header declaration.
- [Phase 2] Verified crash path: dtInsertEntry line 3651-3652 uses
p->header.freelist as array index without bounds checking.
- [Phase 3] git blame: Vulnerable code in copy_from_dinode dates to
^1da177e4c3f41 (2005, initial git import). Present in all stable
trees.
- [Phase 3] git log: Multiple similar JFS corruption fixes exist
(a8dfb21689069, 5dff41a863775, 27e56f59bab5d, 7a5aa54fba2bd).
- [Phase 3] Author check: Yun Zhou has prior JFS contributions; Dave
Kleikamp is JFS maintainer.
- [Phase 4] b4 dig: Could not find original submission thread.
- [Phase 4] Web search: Found related Yun Zhou JFS patches but not this
specific one.
- [Phase 5] Verified call chain: copy_from_dinode() called from diRead()
(line 384) and diReadSpecial() (line 459) - core inode loading.
- [Phase 5] Verified crash reachability: mkdir syscall -> jfs_mkdir ->
dtInsert -> dtSplitPage -> dtInsertEntry -> slot[freelist] OOB.
- [Phase 6] Verified stable tree presence: jfs_imap.c exists in
v5.15/v6.1/v6.6 stable with same vulnerable pattern.
- [Phase 6] No pre-existing fix for dtroot validation in any stable
tree.
- [Phase 7] JFS is mature filesystem with active syzbot-found fix
pattern.
- [Phase 8] Severity: HIGH - OOB access from userspace-reachable path on
corrupted fs.
- UNVERIFIED: Could not find original lore discussion or confirm
explicit reviewer feedback.
**YES**
fs/jfs/jfs_dtree.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++
fs/jfs/jfs_dtree.h | 2 ++
fs/jfs/jfs_imap.c | 4 +++
3 files changed, 92 insertions(+)
diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
index 9ab3f2fc61d17..8abd9c7663ea4 100644
--- a/fs/jfs/jfs_dtree.c
+++ b/fs/jfs/jfs_dtree.c
@@ -4297,3 +4297,89 @@ int dtModify(tid_t tid, struct inode *ip,
return 0;
}
+
+bool check_dtroot(dtroot_t *p)
+{
+ DECLARE_BITMAP(bitmap, DTROOTMAXSLOT) = {0};
+ int i;
+
+ /* freecnt cannot be negative or exceed DTROOTMAXSLOT-1
+ * (since slot[0] is occupied by the header).
+ */
+ if (unlikely(p->header.freecnt < 0 ||
+ p->header.freecnt > DTROOTMAXSLOT - 1)) {
+ jfs_err("Bad freecnt:%d in dtroot\n", p->header.freecnt);
+ return false;
+ } else if (p->header.freecnt == 0) {
+ /* No free slots: freelist must be -1 */
+ if (unlikely(p->header.freelist != -1)) {
+ jfs_err("freecnt=0, but freelist=%d in dtroot\n",
+ p->header.freelist);
+ return false;
+ }
+ } else {
+ int fsi, i;
+ /* When there are free slots, freelist must be a valid slot index in
+ * 1~DTROOTMAXSLOT-1(since slot[0] is occupied by the header).
+ */
+ if (unlikely(p->header.freelist < 1 ||
+ p->header.freelist >= DTROOTMAXSLOT)) {
+ jfs_err("Bad freelist:%d in dtroot\n", p->header.freelist);
+ return false;
+ }
+
+ /* Traverse the free list to check validity of all node indices */
+ fsi = p->header.freelist;
+ for (i = 0; i < p->header.freecnt - 1; i++) {
+ /* Check for duplicate indices in the free list */
+ if (unlikely(__test_and_set_bit(fsi, bitmap))) {
+ jfs_err("duplicate index%d in slot in dtroot\n", fsi);
+ return false;
+ }
+ fsi = p->slot[fsi].next;
+
+ /* Ensure the next slot index in the free list is valid */
+ if (unlikely(fsi < 1 || fsi >= DTROOTMAXSLOT)) {
+ jfs_err("Bad index:%d in slot in dtroot\n", fsi);
+ return false;
+ }
+ }
+
+ /* The last node in the free list must terminate with next = -1 */
+ if (unlikely(p->slot[fsi].next != -1)) {
+ jfs_err("Bad next:%d of the last slot in dtroot\n",
+ p->slot[fsi].next);
+ return false;
+ }
+ }
+
+ /* Validate nextindex (next free entry index in stbl)
+ * stbl array has size 8 (indices 0~7).
+ * It may get set to 8 when the last free slot has been filled.
+ */
+ if (unlikely(p->header.nextindex > ARRAY_SIZE(p->header.stbl))) {
+ jfs_err("Bad nextindex:%d in dtroot\n", p->header.nextindex);
+ return false;
+ }
+
+ /* Validate index validity of stbl array (8 elements)
+ * Each entry in stbl is a slot index, with valid range: -1 (invalid)
+ * or 0~8 (slot[0]~slot[8])
+ */
+ for (i = 0; i < p->header.nextindex; i++) {
+ int idx = p->header.stbl[i];
+
+ if (unlikely(idx < 0 || idx >= 9)) {
+ jfs_err("Bad index:%d of stbl[%d] in dtroot\n", idx, i);
+ return false; /* stbl entry points out of slot array range */
+ }
+
+ /* Check for duplicate valid indices (skip check for idx=0) */
+ if (unlikely(idx && __test_and_set_bit(idx, bitmap))) {
+ jfs_err("Duplicate index:%d in stbl in dtroot\n", idx);
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/fs/jfs/jfs_dtree.h b/fs/jfs/jfs_dtree.h
index 1758289647a0e..94dc16123c87e 100644
--- a/fs/jfs/jfs_dtree.h
+++ b/fs/jfs/jfs_dtree.h
@@ -253,4 +253,6 @@ extern int dtModify(tid_t tid, struct inode *ip, struct component_name * key,
ino_t * orig_ino, ino_t new_ino, int flag);
extern int jfs_readdir(struct file *file, struct dir_context *ctx);
+
+extern bool check_dtroot(dtroot_t *p);
#endif /* !_H_JFS_DTREE */
diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
index 294a67327c735..fbb5f7966b754 100644
--- a/fs/jfs/jfs_imap.c
+++ b/fs/jfs/jfs_imap.c
@@ -3102,6 +3102,10 @@ static int copy_from_dinode(struct dinode * dip, struct inode *ip)
if (S_ISDIR(ip->i_mode)) {
memcpy(&jfs_ip->u.dir, &dip->u._dir, 384);
+ if (!check_dtroot(&jfs_ip->i_dtroot)) {
+ jfs_error(ip->i_sb, "Corrupt dtroot\n");
+ return -EIO;
+ }
} else if (S_ISREG(ip->i_mode) || S_ISLNK(ip->i_mode)) {
memcpy(&jfs_ip->i_xtroot, &dip->di_xtroot, 288);
} else
--
2.53.0
|
|
From: Sasha L. <sa...@ke...> - 2026-04-20 13:29:25
|
From: Helen Koike <ko...@ig...> [ Upstream commit ca5848ae87d24886a7886f5a22278bd4045c15f8 ] write_special_inodes() function iterate through the log->sb_list and access the sbi fields, which can be set to NULL concurrently by umount. Fix concurrency issue by holding LOG_LOCK and checking for NULL. Reported-by: syz...@sy... Closes: https://syzkaller.appspot.com/bug?extid=e14b1036481911ae4d77 Signed-off-by: Helen Koike <ko...@ig...> Signed-off-by: Dave Kleikamp <dav...@or...> Signed-off-by: Sasha Levin <sa...@ke...> --- LLM Generated explanations, may be completely bogus: --- ## Full Analysis ### PHASE 1: COMMIT MESSAGE FORENSICS **Step 1.1: Subject Line** - Subsystem: `jfs` (JFS filesystem) - Action verb: "hold" (LOG_LOCK) + "avoid" (null-ptr-deref) - Summary: Hold LOG_LOCK during umount to prevent null pointer dereference in `write_special_inodes` Record: [jfs] [hold/avoid] [prevent null-ptr-deref by holding LOG_LOCK during umount teardown] **Step 1.2: Tags** - `Reported-by: syz...@sy...` - syzbot fuzzer found this (strong YES signal) - `Closes: https://syzkaller.appspot.com/bug?extid=e14b1036481911ae4d77` - syzbot bug tracker link - `Signed-off-by: Helen Koike <ko...@ig...>` - patch author - `Signed-off-by: Dave Kleikamp <dav...@or...>` - JFS maintainer signed off (strong quality signal) Record: syzbot-reported, JFS maintainer signed-off. No Fixes: tag (expected for review candidates). **Step 1.3: Commit Body** - Bug: `write_special_inodes()` iterates `log->sb_list` and accesses `sbi` fields (`ipbmap`, `ipimap`, `direct_inode`) that can be concurrently set to NULL by `jfs_umount()`. - Symptom: general protection fault / null-ptr-deref (kernel crash) - Fix: Hold LOG_LOCK during teardown in umount + add NULL checks in `write_special_inodes()` Record: Race condition between log sync and filesystem unmount, causing a null pointer dereference in `write_special_inodes`. Root cause is unsynchronized access to `sbi` fields during concurrent umount. **Step 1.4: Hidden Bug Fix Detection** This is explicitly a bug fix, not disguised. --- ### PHASE 2: DIFF ANALYSIS **Step 2.1: Inventory** - `fs/jfs/jfs_logmgr.c`: -9 lines (removes LOG_LOCK macros, adds NULL checks in `write_special_inodes`) - `fs/jfs/jfs_logmgr.h`: +7 lines (moves LOG_LOCK macros here so `jfs_umount.c` can use them) - `fs/jfs/jfs_umount.c`: +10 lines (adds `#include "jfs_logmgr.h"`, wraps teardown section with LOG_LOCK/LOG_UNLOCK) Total: +24/-9, 3 files. Small, well-contained fix. **Step 2.2: Code Flow Changes** Hunk 1 (jfs_logmgr.c): Moves `LOG_LOCK_INIT`/`LOG_LOCK`/`LOG_UNLOCK` macro definitions from `.c` to `.h` file. No behavior change. Hunk 2 (jfs_logmgr.c, `write_special_inodes`): - Before: Unconditionally dereferences `sbi->ipbmap->i_mapping`, `sbi->ipimap->i_mapping`, `sbi->direct_inode->i_mapping` - After: Adds NULL checks before each dereference Hunk 3 (jfs_logmgr.h): Adds the moved LOG_LOCK macros. Hunk 4 (jfs_umount.c): - Before: `jfs_umount()` tears down sbi fields (sets to NULL) without holding LOG_LOCK - After: Acquires LOG_LOCK before teardown, releases after `filemap_write_and_wait()`, before `updateSuper()` **Step 2.3: Bug Mechanism** Category: Race condition / NULL pointer dereference The race window: 1. Thread A: `jfs_sync_fs()` -> `jfs_syncpt()` -> `lmLogSync()` -> `write_special_inodes()` iterates `log->sb_list` 2. Thread B: `jfs_umount()` sets `sbi->ipimap = NULL`, `sbi->ipbmap = NULL` etc. 3. The `list_del(&sbi->log_list)` (which removes sbi from sb_list) only happens later in `lmLogClose()` (line 1445) 4. Window: sbi is still on `sb_list` but its fields are NULL Fix mechanism: Hold LOG_LOCK in umount during teardown. Since `jfs_syncpt()` also holds LOG_LOCK before calling `lmLogSync()`, the two paths are now serialized. Additionally, NULL checks in `write_special_inodes` provide belt-and-suspenders safety. **Step 2.4: Fix Quality** - Obviously correct: LOG_LOCK is the existing per-log serialization mechanism, and `jfs_syncpt` already uses it - Minimal and surgical: only adds synchronization around existing teardown code - Regression risk: Very low. The LOG_LOCK is a mutex. `jfs_umount` already calls `jfs_flush_journal(log, 2)` before this code which does `write_special_inodes` itself, so the lock ordering is safe (no deadlock risk since `jfs_flush_journal` doesn't hold LOG_LOCK during its `write_special_inodes` calls) --- ### PHASE 3: GIT HISTORY **Step 3.1: Blame** - `write_special_inodes` introduced by commit `67e6682f18b3bf` (Dave Kleikamp, 2007-10-10) - present since v2.6.24 - The umount code setting sbi fields to NULL goes back to `1da177e4c3f41` (Linus Torvalds, 2005-04-16) - the initial Linux tree import - The `sbi->ipbmap = NULL` was fixed in `d0e482c45c501` (2022) - before that it was a typo (`sbi->ipimap = NULL` was set twice) Record: Bug has existed since `write_special_inodes` was introduced in 2007, affecting ALL stable trees. **Step 3.2: No Fixes: tag** - expected for review candidates. **Step 3.3: File History** - None of the recent changes to these files affect the buggy code paths. The race condition code is untouched ancient code. **Step 3.4: Author** - Helen Koike is a kernel contributor (drm/ci primarily), not the JFS maintainer. But the commit was signed off by Dave Kleikamp, the JFS maintainer (`sh...@ke...`). **Step 3.5: Dependencies** - The only "dependency" is moving LOG_LOCK macros to the header, which is done in the same commit. Fully self- contained. --- ### PHASE 4: MAILING LIST RESEARCH **b4 dig**: Found the original submission at `https://patch.msgid.link/202...@ig...`. Single version (v1), no revisions needed. **Syzbot report**: The bug page confirms: - Crash type: "general protection fault in lmLogSync" - KASAN: null-ptr- deref - First reported: ~1295 days ago (September 2022) - Still actively crashing as recently as 9h45m before page load - Fix commit identified as `ca5848ae87d2` - **Similar bugs exist on linux-5.15, linux-6.1, and linux-6.6** stable trees (all marked 0/N patched) - The bug has been in syzbot monthly reports for 36+ months **Recipients**: Sent to JFS maintainer (sh...@ke...), jfs- discussion list, linux-kernel, linux-fsdevel. --- ### PHASE 5: CODE SEMANTIC ANALYSIS **Callers of `write_special_inodes`:** 1. `lmLogSync()` (line 935/937) - called with LOG_LOCK held from `lmLog()` (line 321) and `jfs_syncpt()` (line 1038) 2. `jfs_flush_journal()` (lines 1572/1581) - called WITHOUT LOG_LOCK **Call chain for crash:** `jfs_sync_fs()` -> `jfs_syncpt()` -> LOG_LOCK -> `lmLogSync()` -> `write_special_inodes()` -> dereference `sbi->ipbmap` (NULL) -> CRASH `sync_filesystem()` in VFS -> `jfs_sync_fs()` -> same path **Race counterpart:** `generic_shutdown_super()` -> `kill_block_super()` -> ... -> `jfs_umount()` -> sets sbi fields to NULL -> `lmLogClose()` does `list_del` The crash trace from syzbot confirms exactly this path: ``` write_special_inodes fs/jfs/jfs_logmgr.c:208 lmLogSync+0x244/0x9f0 fs/jfs/jfs_logmgr.c:937 jfs_syncpt+0x7b/0x90 fs/jfs/jfs_logmgr.c:1041 jfs_sync_fs+0x87/0xa0 fs/jfs/super.c:650 sync_filesystem+0x1ce/0x250 fs/sync.c:66 generic_shutdown_super+0x77/0x2d0 fs/super.c:625 ``` --- ### PHASE 6: STABLE TREE ANALYSIS - The buggy code (`write_special_inodes` from 2007, umount NULL assignments from 2005) exists in ALL active stable trees - Syzbot confirms active crashing on linux-5.15, linux-6.1, linux-6.6 - None of these stable trees have been patched (0/N patched status on syzbot) - No intermediate changes to the affected code in stable trees - Backport should be clean: the affected code is ancient and unchanged --- ### PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT - Subsystem: JFS filesystem (fs/jfs/) - Criticality: IMPORTANT - JFS is a real filesystem used in production - Maintainer: Dave Kleikamp signed off, confirming this fix - JFS is mature/stable - minimal recent development, making this longstanding bug more important --- ### PHASE 8: IMPACT AND RISK ASSESSMENT **Who is affected:** All JFS users **Trigger conditions:** Concurrent `sync` and `umount` operations on JFS filesystems. The syzbot reproducer triggers this from userspace (umount syscall racing with sync). Triggerable by unprivileged users if they can unmount. **Failure mode:** Kernel oops / general protection fault (NULL pointer dereference). Severity: **CRITICAL** (system crash) **Frequency:** Syzbot has been hitting this continuously for 3.5+ years **Benefit:** Prevents a real kernel crash on all JFS systems. HIGH benefit. **Risk:** Very LOW. The fix is 24 lines net, adds synchronization using existing infrastructure (LOG_LOCK mutex), and adds defensive NULL checks. No new APIs, no behavior change, no regression risk. --- ### PHASE 9: FINAL SYNTHESIS **Evidence FOR backporting:** 1. Fixes a confirmed, reproducible kernel crash (null-ptr-deref / GPF) 2. Syzbot-reported with C reproducer, crashing continuously for 3.5+ years 3. Bug exists in ALL active stable trees (5.15, 6.1, 6.6) - syzbot confirms 4. Small, surgical fix: +24/-9 lines across 3 files 5. Obviously correct: uses existing LOG_LOCK mechanism + NULL checks 6. JFS maintainer (Dave Kleikamp) signed off 7. Standalone, self-contained fix with no dependencies 8. Triggerable from userspace (sync + umount race) **Evidence AGAINST backporting:** - None identified **Stable Rules Checklist:** 1. Obviously correct and tested? YES - uses existing locking, syzbot confirms fix 2. Fixes a real bug? YES - null-ptr-deref / kernel crash 3. Important issue? YES - CRITICAL severity (system crash) 4. Small and contained? YES - 3 files, +24/-9 lines 5. No new features/APIs? YES - only adds synchronization and safety checks 6. Can apply to stable? YES - code is unchanged in stable trees --- ### Verification - [Phase 1] Parsed tags: Reported-by syzbot, Closes syzkaller link, SOB from JFS maintainer Dave Kleikamp - [Phase 2] Diff analysis: NULL checks added to `write_special_inodes()`, LOG_LOCK held in `jfs_umount()` during teardown - [Phase 3] git blame: `write_special_inodes` introduced in commit `67e6682f18b3bf` (2007), umount NULL assignments from `1da177e4c3f41` (2005) - present in all stable trees - [Phase 3] git log: no intermediate fixes for this race condition - [Phase 3] Verified `list_del(&sbi->log_list)` happens in `lmLogClose()` (line 1445), AFTER umount sets fields to NULL, confirming the race window - [Phase 4] b4 dig: found original submission at `https://patch.msgid.link/202...@ig...`, single version (v1) - [Phase 4] b4 dig -w: sent to JFS maintainer and relevant lists - [Phase 4] Syzbot page: confirms crash "general protection fault in lmLogSync (2)", first crash ~1295 days ago, still actively reproducing, similar bugs on linux-5.15/6.1/6.6 (all unpatched) - [Phase 5] Traced call chain: `jfs_sync_fs -> jfs_syncpt -> LOG_LOCK -> lmLogSync -> write_special_inodes` races with `jfs_umount` setting sbi fields to NULL - [Phase 5] Verified `lmLogSync` is normally called under LOG_LOCK (from `lmLog()` line 321, `jfs_syncpt` line 1039) - [Phase 5] Verified `jfs_flush_journal` calls `write_special_inodes` WITHOUT LOG_LOCK (lines 1572/1581), but fix in umount still protects the window - [Phase 6] No changes to affected code in stable trees since 6.1; patch should apply cleanly - [Phase 6] Syzbot confirms active crashing on 5.15, 6.1, 6.6 stable trees (0/N patched) - [Phase 8] Failure mode: kernel oops (null-ptr-deref), severity CRITICAL - [Phase 8] Risk: very low (24 lines, uses existing LOG_LOCK, adds NULL checks) **YES** fs/jfs/jfs_logmgr.c | 16 +++++++--------- fs/jfs/jfs_logmgr.h | 7 +++++++ fs/jfs/jfs_umount.c | 10 ++++++++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index ada00d5bc2146..d8266220776e8 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -74,12 +74,6 @@ static struct lbuf *log_redrive_list; static DEFINE_SPINLOCK(log_redrive_lock); -/* - * log read/write serialization (per log) - */ -#define LOG_LOCK_INIT(log) mutex_init(&(log)->loglock) -#define LOG_LOCK(log) mutex_lock(&((log)->loglock)) -#define LOG_UNLOCK(log) mutex_unlock(&((log)->loglock)) /* @@ -204,9 +198,13 @@ static void write_special_inodes(struct jfs_log *log, struct jfs_sb_info *sbi; list_for_each_entry(sbi, &log->sb_list, log_list) { - writer(sbi->ipbmap->i_mapping); - writer(sbi->ipimap->i_mapping); - writer(sbi->direct_inode->i_mapping); + /* These pointers can be NULL before list_del during umount */ + if (sbi->ipbmap) + writer(sbi->ipbmap->i_mapping); + if (sbi->ipimap) + writer(sbi->ipimap->i_mapping); + if (sbi->direct_inode) + writer(sbi->direct_inode->i_mapping); } } diff --git a/fs/jfs/jfs_logmgr.h b/fs/jfs/jfs_logmgr.h index 8b8994e48cd08..09e0ef6aeccef 100644 --- a/fs/jfs/jfs_logmgr.h +++ b/fs/jfs/jfs_logmgr.h @@ -402,6 +402,13 @@ struct jfs_log { int no_integrity; /* 3: flag to disable journaling to disk */ }; +/* + * log read/write serialization (per log) + */ +#define LOG_LOCK_INIT(log) mutex_init(&(log)->loglock) +#define LOG_LOCK(log) mutex_lock(&((log)->loglock)) +#define LOG_UNLOCK(log) mutex_unlock(&((log)->loglock)) + /* * Log flag */ diff --git a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c index 8ec43f53f6865..18569f1eaabdb 100644 --- a/fs/jfs/jfs_umount.c +++ b/fs/jfs/jfs_umount.c @@ -20,6 +20,7 @@ #include "jfs_superblock.h" #include "jfs_dmap.h" #include "jfs_imap.h" +#include "jfs_logmgr.h" #include "jfs_metapage.h" #include "jfs_debug.h" @@ -57,6 +58,12 @@ int jfs_umount(struct super_block *sb) */ jfs_flush_journal(log, 2); + /* + * Hold log lock so write_special_inodes (lmLogSync) cannot see + * this sbi with a NULL inode pointer while iterating log->sb_list. + */ + if (log) + LOG_LOCK(log); /* * close fileset inode allocation map (aka fileset inode) */ @@ -95,6 +102,9 @@ int jfs_umount(struct super_block *sb) */ filemap_write_and_wait(sbi->direct_inode->i_mapping); + if (log) + LOG_UNLOCK(log); + /* * ensure all file system file pages are propagated to their * home blocks on disk (and their in-memory buffer pages are -- 2.53.0 |
|
From: Sasha L. <sa...@ke...> - 2026-04-20 13:25:47
|
From: Yun Zhou <yun...@wi...>
[ Upstream commit 3c778ec882084626ac915d6c6ec88aff87b82221 ]
This patch resolves the "list_add corruption. next is NULL" Oops
reported by syzkaller in dbUpdatePMap(). The root cause is uninitialized
synclist nodes in struct metapage and struct TxBlock, plus improper list
node removal using list_del() (which leaves nodes in an invalid state).
This fixes the following Oops reported by syzkaller.
list_add corruption. next is NULL.
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:28!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 122 Comm: jfsCommit Not tainted syzkaller #0
PREEMPT_{RT,(full)}
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
Google 10/02/2025
RIP: 0010:__list_add_valid_or_report+0xc3/0x130 lib/list_debug.c:27
Code: 4c 89 f2 48 89 d9 e8 0c 88 a4 fc 90 0f 0b 48 c7 c7 20 de 3d 8b e8
fd 87 a4 fc 90 0f 0b 48 c7 c7 c0 de 3d 8b e8 ee 87 a4 fc 90 <0f> 0b 48
89 df e8 13 c3 7d fd 42 80 7c 2d 00 00 74 08 4c 89 e7 e8
RSP: 0018:ffffc9000395fa20 EFLAGS: 00010246
RAX: 0000000000000022 RBX: 0000000000000000 RCX: 270c5dfadb559700
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 00000000000f0000 R08: 0000000000000000 R09: 0000000000000000
R10: dffffc0000000000 R11: fffff5200072bee9 R12: 0000000000000000
R13: dffffc0000000000 R14: 0000000000000004 R15: 1ffff92000632266
FS: 0000000000000000(0000) GS:ffff888126ef9000(0000)
knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000056341fdb86c0 CR3: 0000000040a18000 CR4: 00000000003526f0
Call Trace:
<TASK>
__list_add_valid include/linux/list.h:96 [inline]
__list_add include/linux/list.h:158 [inline]
list_add include/linux/list.h:177 [inline]
dbUpdatePMap+0x7e4/0xeb0 fs/jfs/jfs_dmap.c:577
txAllocPMap+0x57d/0x6b0 fs/jfs/jfs_txnmgr.c:2426
txUpdateMap+0x81e/0x9c0 fs/jfs/jfs_txnmgr.c:2364
txLazyCommit fs/jfs/jfs_txnmgr.c:2665 [inline]
jfs_lazycommit+0x3f1/0xa10 fs/jfs/jfs_txnmgr.c:2734
kthread+0x711/0x8a0 kernel/kthread.c:463
ret_from_fork+0x4bc/0x870 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
Reported-by: syz...@sy...
Closes: https://syzkaller.appspot.com/bug?extid=4d0a0feb49c5138cac46
Tested-by: syz...@sy...
Signed-off-by: Yun Zhou <yun...@wi...>
Signed-off-by: Dave Kleikamp <dav...@or...>
Signed-off-by: Sasha Levin <sa...@ke...>
---
LLM Generated explanations, may be completely bogus:
Now I have all the information needed to produce the complete analysis.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
### Step 1.1: Subject Line
- **Subsystem:** `jfs` (JFS filesystem)
- **Action verb:** "fix" — directly claims to fix a bug
- **Summary:** Fixes corrupted list in `dbUpdatePMap` by initializing
list nodes and using safe deletion
### Step 1.2: Tags
- **Reported-by:**
`syz...@sy...` — fuzzer-
found, reproducible
- **Closes:**
`https://syzkaller.appspot.com/bug?extid=4d0a0feb49c5138cac46` —
syzbot link
- **Tested-by:** `syz...@sy...`
— syzbot confirmed the fix
- **Signed-off-by:** Yun Zhou (author), Dave Kleikamp (JFS maintainer
committed it)
- No Fixes: tag, no Cc: stable — expected for manual review
### Step 1.3: Commit Body
The commit describes a **kernel BUG/oops** in
`__list_add_valid_or_report` triggered from `dbUpdatePMap()`. The root
cause is two-fold:
1. **Uninitialized `synclist` nodes** in `struct metapage` and `struct
tblock` — `next`/`prev` pointers are NULL/garbage
2. **Improper `list_del()`** usage that poisons nodes (sets `next` =
`LIST_POISON1`), making subsequent `list_add()` fail when the node is
reused
The crash is triggered in the `jfsCommit` kernel thread during
transaction commit via `jfs_lazycommit -> txLazyCommit -> txUpdateMap ->
txAllocPMap -> dbUpdatePMap`.
### Step 1.4: Hidden Bug Fix Detection
This is NOT a hidden bug fix — it is an explicit, well-documented crash
fix with full stack trace and syzbot confirmation.
Record: **Explicit bug fix for a kernel BUG/oops (crash)**
---
## PHASE 2: DIFF ANALYSIS
### Step 2.1: Inventory
- **Files changed:** 2 files (`fs/jfs/jfs_metapage.c`,
`fs/jfs/jfs_txnmgr.c`)
- **Lines added:** 3 lines
- **Lines changed:** 2 lines (`list_del` → `list_del_init`)
- **Functions modified:**
- `alloc_metapage()` — +1 line: `INIT_LIST_HEAD(&mp->synclist)`
- `remove_from_logsync()` — changed `list_del` to `list_del_init`
- `txInit()` — +1 line: `INIT_LIST_HEAD(&TxBlock[k].synclist)`
- `txUnlock()` — changed `list_del` to `list_del_init`
- **Scope:** Single-subsystem surgical fix, 5 changed lines total
### Step 2.2: Code Flow Changes
1. **`alloc_metapage()`**: Before: `mp->synclist` left uninitialized
after allocation. After: `synclist` is properly initialized to an
empty list head.
2. **`txInit()`**: Before: `TxBlock[k].synclist` left uninitialized.
After: each tblock's `synclist` initialized during transaction
manager setup.
3. **`remove_from_logsync()`**: Before: `list_del(&mp->synclist)`
poisons the node. After: `list_del_init(&mp->synclist)` resets to
clean empty state.
4. **`txUnlock()`**: Before: `list_del(&tblk->synclist)` poisons the
node. After: `list_del_init(&tblk->synclist)` resets to clean empty
state.
### Step 2.3: Bug Mechanism
**Category:** Uninitialized data + improper list state management
The crash scenario:
1. A `metapage` is allocated via `alloc_metapage()` → `synclist.next`
and `synclist.prev` are uninitialized (NULL or garbage)
2. Code path reaches `dbUpdatePMap()` at line 577:
`list_add(&mp->synclist, &tblk->synclist)`
3. `list_add()` validates that `next != NULL` → BUG because
`tblk->synclist.next` is NULL (uninitialized)
Alternative scenario:
1. A node is on the logsync list, then removed with `list_del()` → `next
= LIST_POISON1`
2. The node is reused, and `list_add()` is called → `next =
LIST_POISON1` triggers BUG
### Step 2.4: Fix Quality
- **Obviously correct:** Yes — `INIT_LIST_HEAD` is the standard
initialization for list nodes; `list_del_init` is the standard safe
deletion for reusable nodes
- **Minimal/surgical:** Yes — 5 effective lines changed
- **Regression risk:** Essentially zero — these are the textbook correct
patterns for Linux list operations
- **Red flags:** None
---
## PHASE 3: GIT HISTORY INVESTIGATION
### Step 3.1: Blame
- `alloc_metapage()`: The initialization code was written by David
Rientjes in commit `ee1462458cb543` (2015), but the function itself
dates to `1da177e4c3f41` (original Linux import, 2005). The `synclist`
member was never initialized here.
- `remove_from_logsync()`: Written by Dave Kleikamp in `7fab479bebb96b`
(2005-05-02). Uses `list_del` since the beginning.
- `txInit()`: TxBlock init loop from `1da177e4c3f41` (original, 2005),
recently refactored by `300b072df72694` (2025) to fix waitqueue
initialization. The `synclist` was never initialized in the loop.
- `txUnlock()`: `list_del(&tblk->synclist)` from `1da177e4c3f41`
(original, 2005).
**The bug has existed since the initial kernel git history (v2.6.12,
2005).** Every stable tree is affected.
### Step 3.2: Fixes Tag
No Fixes: tag present (expected).
### Step 3.3: File History
- `300b072df72694` ("jfs: fix uninitialized waitqueue in transaction
manager") is a closely related fix in the same init loop, already in
this tree. This commit changes the for-loop context that the patch
under review modifies.
### Step 3.4: Author
- Yun Zhou is from Wind River — a systems company with kernel expertise.
- Dave Kleikamp (JFS maintainer) signed off and committed it.
### Step 3.5: Dependencies
- The `jfs_txnmgr.c` hunk depends on `300b072df72694` being present for
clean context (the separate init loop). This commit is already in the
7.0 stable tree.
- The `jfs_metapage.c` hunks are independent and should apply cleanly to
all stable trees.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
### Step 4.1: Original Discussion
- b4 dig could not find a match (lore is protected by Anubis anti-bot).
- Syzbot page shows the patch went through two versions: v1 on
2025-11-07, v2 on 2025-11-09.
### Step 4.2: Reviewers
- Committed by Dave Kleikamp (JFS maintainer) — the appropriate person.
### Step 4.3: Bug Report
>From syzbot:
- **First crash:** 165 days ago, **last crash:** 2 days ago (still
actively crashing)
- **Reproducer:** C reproducer available
- **Affected stable trees:** linux-5.15 (0/3 patched), linux-6.1 (0/3
patched), linux-6.6 (0/2 patched), linux-4.19 (0/1 patched)
- **Fix confirmed:** `3c778ec88208` in mainline, patched on 21 upstream
CI configurations
### Step 4.5: Stable Discussion
The syzbot report explicitly lists this bug as unpatched across all
stable trees, confirming the backport is needed.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
### Step 5.1: Key Functions
- `alloc_metapage()` — allocates metapage structures from mempool
- `remove_from_logsync()` — removes a metapage from the logsync list
- `txInit()` — initializes transaction manager at mount time
- `txUnlock()` — unlocks transaction blocks during commit
### Step 5.2: Callers
- `alloc_metapage()` is called from `__get_metapage()` which is called
for every JFS metadata read
- `remove_from_logsync()` called from `last_write_complete()`,
`release_metapage()`, `__invalidate_metapages()` — all common JFS
operations
- `list_add(&mp->synclist, &tblk->synclist)` at line 577 of `jfs_dmap.c`
and line 2831 of `jfs_imap.c` — called during transaction commit for
allocation map updates
### Step 5.4: Reachability
The crash path is: `jfs_lazycommit (kthread) → txLazyCommit →
txUpdateMap → txAllocPMap → dbUpdatePMap → list_add`. This is triggered
during normal JFS transaction commits — any write operation on a JFS
filesystem can trigger it.
---
## PHASE 6: STABLE TREE ANALYSIS
### Step 6.1: Code in Stable Trees
The buggy code dates to the original kernel (2005). **Every stable
tree** that supports JFS is affected. Syzbot confirms active crashes on
5.15, 6.1, and 6.6 stable trees.
### Step 6.2: Backport Complications
- For the 7.0 tree: should apply cleanly — `300b072df72694` (dependency
for context) is already present.
- For older trees (6.6, 6.1, 5.15): the `txInit()` loop context differs
(single combined loop), requiring minor context adjustment. The
`jfs_metapage.c` changes (INIT_LIST_HEAD in alloc_metapage,
list_del_init in remove_from_logsync) and the `txUnlock()`
list_del_init change should apply cleanly.
### Step 6.3: Related Fixes Already in Stable
None. Syzbot explicitly shows 0/N patched for all stable trees.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
### Step 7.1: Subsystem
- **Subsystem:** JFS (Journaled File System) — `fs/jfs/`
- **Criticality:** IMPORTANT — filesystem, data corruption/loss risk,
used in real-world deployments
### Step 7.2: Activity
JFS is a mature/stable subsystem with infrequent changes. The bug has
been present since 2005.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
### Step 8.1: Who Is Affected
All users of the JFS filesystem on any kernel version.
### Step 8.2: Trigger Conditions
Any write operation on a JFS filesystem that triggers transaction
commit. The syzbot reproducer demonstrates it's reliably triggerable. No
special privileges needed beyond filesystem access.
### Step 8.3: Failure Mode
**CRITICAL** — `kernel BUG()` / `invalid opcode: 0000 [#1]` / kernel
oops. The system crashes.
### Step 8.4: Risk-Benefit
- **Benefit:** VERY HIGH — prevents kernel crash on every JFS
filesystem, actively exploitable by syzbot, unpatched across all
stable trees
- **Risk:** VERY LOW — 5 lines of changes using well-known, textbook-
correct patterns (`INIT_LIST_HEAD`, `list_del_init`)
- **Ratio:** Extremely favorable
---
## PHASE 9: FINAL SYNTHESIS
### Step 9.1: Evidence Summary
**FOR backporting:**
- Fixes an active kernel BUG/oops crash (CRITICAL severity)
- Reported and reproduced by syzbot with C reproducer
- Actively crashing on multiple stable trees (5.15, 6.1, 6.6) — all
listed as 0/N patched
- Bug has existed since 2005 (original kernel source)
- Tested by syzbot confirming the fix works
- Committed by JFS maintainer Dave Kleikamp
- Extremely small (5 effective line changes) and surgical
- Uses textbook-correct patterns (INIT_LIST_HEAD, list_del_init)
- Zero regression risk
**AGAINST backporting:**
- None identified
### Step 9.2: Stable Rules Checklist
1. **Obviously correct and tested?** YES — uses standard list API
patterns, syzbot-tested
2. **Fixes a real bug?** YES — kernel crash on JFS writes
3. **Important issue?** YES — kernel BUG/oops (crash)
4. **Small and contained?** YES — 5 lines across 2 files in same
subsystem
5. **No new features or APIs?** YES — pure bug fix
6. **Can apply to stable?** YES — cleanly for 7.0; minor context
adjustment for older trees
### Step 9.3: Exception Categories
Not applicable — this is a straightforward bug fix, not an exception
category.
### Step 9.4: Decision
This is an unambiguous YES. It fixes a kernel crash (BUG/oops) found by
syzbot, confirmed with a reproducer, actively crashing on all stable
trees, with a minimal 5-line fix using correct patterns, committed by
the subsystem maintainer.
---
## Verification
- [Phase 1] Parsed tags: Reported-by syzbot, Tested-by syzbot, Closes
syzkaller link, signed by JFS maintainer
- [Phase 2] Diff analysis: 3 lines added (2x INIT_LIST_HEAD), 2 lines
changed (list_del → list_del_init)
- [Phase 3] git blame: buggy code from `1da177e4c3f41` (v2.6.12, 2005)
and `7fab479bebb96b` (2005) — present in all stable trees
- [Phase 3] git show 300b072df72694: confirmed prerequisite commit (loop
refactor) is in 7.0 tree
- [Phase 3] Grep for INIT_LIST_HEAD of synclist: confirmed none existed
before this patch (only `log->synclist` was initialized)
- [Phase 3] Grep for list_add(&mp->synclist): confirmed 2 call sites
(jfs_dmap.c:577, jfs_imap.c:2831)
- [Phase 3] Grep for list_del(&mp->synclist) and
list_del(&tblk->synclist): confirmed both use `list_del` (not
`list_del_init`) pre-patch
- [Phase 4] Syzbot bug page: confirmed fix commit 3c778ec88208 in
mainline, 0/N patched on stable 5.15/6.1/6.6
- [Phase 4] Syzbot: first crash 165d ago, last 2d ago — still actively
crashing
- [Phase 4] b4 dig: could not find lore match (Anubis anti-bot
protection); syzbot page shows v1 and v2 patch submissions
- [Phase 5] Traced call chain: `jfs_lazycommit → txLazyCommit →
txUpdateMap → txAllocPMap → dbUpdatePMap → list_add` — triggered
during normal JFS writes
- [Phase 5] alloc_metapage() called from __get_metapage() — every JFS
metadata read
- [Phase 6] Bug exists in all stable trees (code from 2005); syzbot
confirms active crashes on 5.15, 6.1, 6.6
- [Phase 6] For 7.0: should apply cleanly (300b072df72694 present)
- [Phase 8] Failure mode: kernel BUG/oops (invalid opcode), severity
CRITICAL
- [Phase 8] Risk: VERY LOW — 5 lines, textbook patterns, zero regression
potential
**YES**
fs/jfs/jfs_metapage.c | 3 ++-
fs/jfs/jfs_txnmgr.c | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index 64c6eaa7f3f26..78dd8a1b29b7d 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -270,6 +270,7 @@ static inline struct metapage *alloc_metapage(gfp_t gfp_mask)
mp->clsn = 0;
mp->log = NULL;
init_waitqueue_head(&mp->wait);
+ INIT_LIST_HEAD(&mp->synclist);
}
return mp;
}
@@ -379,7 +380,7 @@ static void remove_from_logsync(struct metapage *mp)
mp->lsn = 0;
mp->clsn = 0;
log->count--;
- list_del(&mp->synclist);
+ list_del_init(&mp->synclist);
}
LOGSYNC_UNLOCK(log, flags);
}
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index c16578af3a77e..083dbbb0c3268 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -275,6 +275,7 @@ int txInit(void)
for (k = 0; k < nTxBlock; k++) {
init_waitqueue_head(&TxBlock[k].gcwait);
init_waitqueue_head(&TxBlock[k].waitor);
+ INIT_LIST_HEAD(&TxBlock[k].synclist);
}
for (k = 1; k < nTxBlock - 1; k++) {
@@ -974,7 +975,7 @@ static void txUnlock(struct tblock * tblk)
if (tblk->lsn) {
LOGSYNC_LOCK(log, flags);
log->count--;
- list_del(&tblk->synclist);
+ list_del_init(&tblk->synclist);
LOGSYNC_UNLOCK(log, flags);
}
}
--
2.53.0
|
|
From: Sasha L. <sa...@ke...> - 2026-04-20 13:16:57
|
From: Yun Zhou <yun...@wi...>
[ Upstream commit cce219b203c4b9cb445e910c7090d1f58af847c5 ]
Add check_dmapctl() to validate dmapctl structure integrity, focusing on
preventing invalid operations caused by on-disk corruption.
Key checks:
- nleafs bounded by [0, LPERCTL] (maximum leaf nodes per dmapctl).
- l2nleafs bounded by [0, L2LPERCTL] and consistent with nleafs
(nleafs must be 2^l2nleafs).
- leafidx must be exactly CTLLEAFIND (expected leaf index position).
- height bounded by [0, L2LPERCTL >> 1] (valid tree height range).
- budmin validity: NOFREE only if nleafs=0; otherwise >= BUDMIN.
- Leaf nodes fit within stree array (leafidx + nleafs <= CTLTREESIZE).
- Leaf node values are either non-negative or NOFREE.
Invoked in dbAllocAG(), dbFindCtl(), dbAdjCtl() and dbExtendFS() when
accessing dmapctl pages, catching corruption early before dmap operations
trigger invalid memory access or logic errors.
This fixes the following UBSAN warning.
[58245.668090][T14017] ------------[ cut here ]------------
[58245.668103][T14017] UBSAN: shift-out-of-bounds in fs/jfs/jfs_dmap.c:2641:11
[58245.668119][T14017] shift exponent 110 is too large for 32-bit type 'int'
[58245.668137][T14017] CPU: 0 UID: 0 PID: 14017 Comm: 4c1966e88c28fa9 Tainted: G E 6.18.0-rc4-00253-g21ce5d4ba045-dirty #124 PREEMPT_{RT,(full)}
[58245.668174][T14017] Tainted: [E]=UNSIGNED_MODULE
[58245.668176][T14017] Hardware name: QEMU Ubuntu 25.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[58245.668184][T14017] Call Trace:
[58245.668200][T14017] <TASK>
[58245.668208][T14017] dump_stack_lvl+0x189/0x250
[58245.668288][T14017] ? __pfx_dump_stack_lvl+0x10/0x10
[58245.668301][T14017] ? __pfx__printk+0x10/0x10
[58245.668315][T14017] ? lock_metapage+0x303/0x400 [jfs]
[58245.668406][T14017] ubsan_epilogue+0xa/0x40
[58245.668422][T14017] __ubsan_handle_shift_out_of_bounds+0x386/0x410
[58245.668462][T14017] dbSplit+0x1f8/0x200 [jfs]
[58245.668543][T14017] dbAdjCtl+0x34c/0xa20 [jfs]
[58245.668628][T14017] dbAllocNear+0x2ee/0x3d0 [jfs]
[58245.668710][T14017] dbAlloc+0x933/0xba0 [jfs]
[58245.668797][T14017] ea_write+0x374/0xdd0 [jfs]
[58245.668888][T14017] ? __pfx_ea_write+0x10/0x10 [jfs]
[58245.668966][T14017] ? __jfs_setxattr+0x76e/0x1120 [jfs]
[58245.669046][T14017] __jfs_setxattr+0xa01/0x1120 [jfs]
[58245.669135][T14017] ? __pfx___jfs_setxattr+0x10/0x10 [jfs]
[58245.669216][T14017] ? mutex_lock_nested+0x154/0x1d0
[58245.669252][T14017] ? __jfs_xattr_set+0xb9/0x170 [jfs]
[58245.669333][T14017] __jfs_xattr_set+0xda/0x170 [jfs]
[58245.669430][T14017] ? __pfx___jfs_xattr_set+0x10/0x10 [jfs]
[58245.669509][T14017] ? xattr_full_name+0x6f/0x90
[58245.669546][T14017] ? jfs_xattr_set+0x33/0x60 [jfs]
[58245.669636][T14017] ? __pfx_jfs_xattr_set+0x10/0x10 [jfs]
[58245.669726][T14017] __vfs_setxattr+0x43c/0x480
[58245.669743][T14017] __vfs_setxattr_noperm+0x12d/0x660
[58245.669756][T14017] vfs_setxattr+0x16b/0x2f0
[58245.669768][T14017] ? __pfx_vfs_setxattr+0x10/0x10
[58245.669782][T14017] filename_setxattr+0x274/0x600
[58245.669795][T14017] ? __pfx_filename_setxattr+0x10/0x10
[58245.669806][T14017] ? getname_flags+0x1e5/0x540
[58245.669829][T14017] path_setxattrat+0x364/0x3a0
[58245.669840][T14017] ? __pfx_path_setxattrat+0x10/0x10
[58245.669859][T14017] ? __se_sys_chdir+0x1b9/0x280
[58245.669876][T14017] __x64_sys_lsetxattr+0xbf/0xe0
[58245.669888][T14017] do_syscall_64+0xfa/0xfa0
[58245.669901][T14017] ? lockdep_hardirqs_on+0x9c/0x150
[58245.669913][T14017] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[58245.669927][T14017] ? exc_page_fault+0xab/0x100
[58245.669937][T14017] entry_SYSCALL_64_after_hwframe+0x77/0x7f
Reported-by: syz...@sy...
Closes: https://syzkaller.appspot.com/bug?extid=4c1966e88c28fa96e053
Signed-off-by: Yun Zhou <yun...@wi...>
Signed-off-by: Dave Kleikamp <dav...@or...>
Signed-off-by: Sasha Levin <sa...@ke...>
---
LLM Generated explanations, may be completely bogus:
Now let me compile the full analysis.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
### Step 1.1: PARSE THE SUBJECT LINE
Record: [jfs] [add integrity check] - Adds `check_dmapctl()` function to
validate dmapctl structure integrity to prevent invalid operations from
on-disk corruption.
### Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
- **Reported-by:** syz...@sy...
(fuzzer-found bug, strong signal)
- **Closes:**
https://syzkaller.appspot.com/bug?extid=4c1966e88c28fa96e053
- **Signed-off-by:** Yun Zhou <yun...@wi...> (author)
- **Signed-off-by:** Dave Kleikamp <dav...@or...> (JFS
maintainer applied it)
- No Fixes: tag (expected for autosel candidates)
- No Cc: stable (expected for autosel candidates)
### Step 1.3: ANALYZE THE COMMIT BODY TEXT
The commit describes a UBSAN shift-out-of-bounds at
`fs/jfs/jfs_dmap.c:2641:11` in `dbSplit()` where a corrupt `budmin`
value from an on-disk dmapctl structure leads to a shift exponent of 110
(far exceeding the 32-bit limit). The call chain is: `lsetxattr` syscall
-> `__jfs_setxattr` -> `ea_write` -> `dbAlloc` -> `dbAllocNear` ->
`dbAdjCtl` -> `dbSplit`.
Record: Bug is a UBSAN shift-out-of-bounds triggered by corrupt dmapctl
on-disk data. Failure mode is undefined behavior from invalid shift.
Root cause is lack of comprehensive validation of dmapctl fields read
from disk.
### Step 1.4: DETECT HIDDEN BUG FIXES
This is not a hidden bug fix - it's an explicitly declared fix for a
syzbot-reported UBSAN issue. The commit directly fixes undefined
behavior caused by corrupt on-disk data.
---
## PHASE 2: DIFF ANALYSIS - LINE BY LINE
### Step 2.1: INVENTORY THE CHANGES
- **File:** `fs/jfs/jfs_dmap.c` only (single file)
- **New function:** `check_dmapctl()` (~87 lines added)
- **3 replacement sites:** `dbAllocAG()`, `dbFindCtl()`, `dbAdjCtl()` -
each replaces `if (dcp->leafidx != cpu_to_le32(CTLLEAFIND))` with `if
(unlikely(!check_dmapctl(dcp)))`
- **5 new check sites in dbExtendFS():** Adds `check_dmapctl()` calls
after reading L2, L1, and L0 dmapctl pages where no checks existed
before
- Total: ~87 lines for the new function + 8 call sites (3 replacements +
5 additions) = ~110 lines net change
### Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
**Before:** Only `leafidx == CTLLEAFIND` was validated when reading
dmapctl pages in 3 functions. `budmin`, `nleafs`, `l2nleafs`, `height`,
and leaf values were NOT validated. In `dbExtendFS()`, no dmapctl
validation at all.
**After:** All 7 key fields of dmapctl are validated before any
arithmetic is performed on them. This catches corrupt values (like the
`budmin` that led to shift exponent 110) before they flow into
`BUDSIZE()`, `BLKTOCTLLEAF()`, or array index operations.
### Step 2.3: IDENTIFY THE BUG MECHANISM
Category: **Input validation / sanitizer-reported undefined behavior**
The `BUDSIZE(s,m)` macro at line 275 of `jfs_dmap.h` is `(1 << ((s) -
(m)))`. When `leaf[leafno]` is corrupt (e.g., 115) and `budmin` is 5
(BUDMIN), `cursz = 115 - 1 = 114`, and `BUDSIZE(114, 5) = (1 << 109)`
which is shift-out-of-bounds for a 32-bit int. The `check_dmapctl()`
validates `budmin >= BUDMIN` and leaf values are within `[NOFREE, 31]`,
preventing such overflows.
### Step 2.4: ASSESS THE FIX QUALITY
- The fix is well-structured with clear boundary checks against known
constants
- It replaces weaker checks with comprehensive validation
- Each check maps to a specific invariant of the dmapctl structure
- Error paths return -EIO which is the existing pattern
- The leaf value range check `val > 31` matches the fact that
`BUDSIZE(31, 5) = (1 << 26)` is the maximum valid shift
- Risk of regression is low - it only makes validation stricter, all
valid dmapctl pages will pass
---
## PHASE 3: GIT HISTORY INVESTIGATION
### Step 3.1: BLAME THE CHANGED LINES
The 3 existing `leafidx != cpu_to_le32(CTLLEAFIND)` checks being
replaced were all introduced in the initial Linux git import (v2.6.12,
commit 1da177e4c3f41). This is ancient code present in ALL stable trees.
### Step 3.2: FOLLOW THE FIXES: TAG
No Fixes: tag present. The bug is in the fundamental lack of validation
of on-disk dmapctl data, which dates back to the initial JFS
implementation.
### Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
JFS has seen a steady stream of similar corruption-defense fixes, all
syzbot-driven:
- `a5f5e4698f8ab` - fix shift-out-of-bounds in dbSplit (similar bug,
budmin < 0)
- `d64ff0d230671` - check if leafidx greater than num leaves per dmap
tree
- `a174706ba4dad` - check to prevent array-index-out-of-bounds in
dbAdjTree
- Many others addressing the same class of "corrupt on-disk data →
UBSAN/crash"
This commit is standalone - it does not require other patches in a
series.
### Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Yun Zhou (Wind River) is not a regular JFS contributor. However, the
patch was reviewed by Li Lingfeng and applied by Dave Kleikamp
(sh...@ke...), who is the JFS maintainer.
### Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
The patch replaces `dcp->leafidx != cpu_to_le32(CTLLEAFIND)` checks that
have existed since v2.6.12. The code context (function signatures, data
structures) is stable and unchanged. No dependencies on recent commits.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
### Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
- b4 dig found the match: https://patch.msgid.link/20251128155150.149398
6-1...@wi...
- This is v2 of the patch (v1 was submitted 2025/11/20, v2 on
2025/11/28)
- Dave Kleikamp (JFS maintainer) replied: "This is finally tested and
applied." (2026/03/16)
- Li Lingfeng reviewed and pinged twice for application
### Step 4.2: CHECK WHO REVIEWED THE PATCH
The JFS maintainer (Dave Kleikamp/shaggy) directly tested and applied.
Li Lingfeng reviewed.
### Step 4.3: SEARCH FOR THE BUG REPORT
The syzbot page confirms:
- Bug is reproducible with a C reproducer
- First crash: 354 days ago, last: 36 days ago (persistent bug)
- The bug has **similar bugs** on linux-6.1 and linux-5.15 that are
**NOT patched** (0/3 patched on 6.1, 0/3 on 5.15)
- This confirms the bug exists and is unpatched in stable trees
### Step 4.4-4.5: Related patches
This is a standalone patch. No explicit stable discussion found, but the
syzbot similar bugs table shows the same class of issue exists in stable
trees (linux-6.1, linux-5.15) and is NOT yet fixed there.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
### Step 5.1-5.2: KEY FUNCTIONS AND CALLERS
The `check_dmapctl()` is called in:
1. **`dbAllocAG()`** - called from `dbAlloc()`, the primary block
allocation path
2. **`dbFindCtl()`** - called from `dbAllocCtl()` and `dbAllocAG()`
3. **`dbAdjCtl()`** - called from `dbFreeBits()`, `dbAllocDmap()`,
`dbAllocDmapBU()`, and recursively
4. **`dbExtendFS()`** - called during filesystem extension
### Step 5.3-5.4: CALL CHAIN (reachability)
The syzbot crash trace confirms reachability from userspace:
`lsetxattr` (syscall) → `__jfs_setxattr` → `ea_write` → `dbAlloc` →
`dbAllocNear` → `dbAdjCtl` → `dbSplit`
This is triggered by a standard `setxattr` syscall on a JFS filesystem.
Any unprivileged user who can write to a JFS filesystem can trigger
this.
---
## PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS
### Step 6.1: DOES THE BUGGY CODE EXIST IN STABLE?
Yes. The code being modified (the leafidx checks in dbAllocAG,
dbFindCtl, dbAdjCtl) has existed since v2.6.12. The bug exists in ALL
stable trees. Syzbot confirms it reproduces on linux-6.1 and linux-5.15.
### Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
The patch should apply cleanly to most stable trees. The three
replacement sites have had the same `dcp->leafidx !=
cpu_to_le32(CTLLEAFIND)` check since the initial git import. The
`dbExtendFS()` additions are to code that is also largely unchanged.
There may be minor context conflicts depending on surrounding changes in
each stable tree (the many JFS syzbot fixes), but the core code
structure is the same.
### Step 6.3: RELATED FIXES IN STABLE
The earlier fix `a5f5e4698f8ab` ("jfs: fix shift-out-of-bounds in
dbSplit") addressed the same syzbot bug family (#2) by checking
`dp->tree.budmin < 0` in `dbAllocCtl`. This commit provides a more
comprehensive fix at the dmapctl level, catching corruption earlier and
more broadly.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
### Step 7.1: SUBSYSTEM CRITICALITY
- **Subsystem:** fs/jfs (JFS filesystem)
- **Criticality:** IMPORTANT - JFS is a mature filesystem used in
enterprise environments, particularly on older IBM/AIX-heritage
systems and some embedded environments
- The fix is in the block allocation map code, which is core to
filesystem operations
### Step 7.2: SUBSYSTEM ACTIVITY
JFS is in maintenance mode - no new features, but receives a steady
stream of corruption-defense fixes (mostly syzbot-driven). This is
exactly the kind of fix that goes to stable.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
### Step 8.1: WHO IS AFFECTED
All users of JFS filesystems. Trigger requires a corrupt JFS filesystem
image (could be from disk corruption, or crafted by an attacker with
local access).
### Step 8.2: TRIGGER CONDITIONS
- Requires mounting or using a JFS filesystem with corrupt dmapctl pages
- Syzbot reproduces this via `lsetxattr` syscall on a crafted image
- Can be triggered by unprivileged user operations on a mounted JFS
filesystem
- Corrupt filesystem images can come from disk errors, USB drives, or
malicious crafting
### Step 8.3: FAILURE MODE SEVERITY
- **UBSAN shift-out-of-bounds** = undefined behavior
- Without UBSAN, this could lead to incorrect memory access patterns,
potential memory corruption
- The corrupt budmin/leaf values flow into array indexing and shift
operations
- Severity: **HIGH** (undefined behavior, potential for memory
corruption or crash)
### Step 8.4: RISK-BENEFIT RATIO
- **BENEFIT:** High - fixes a reproducible syzbot bug, prevents
undefined behavior from corrupt on-disk data, protects against a class
of bugs (not just one instance)
- **RISK:** Low-Medium - the fix is ~87 lines of new validation code
(moderate size), but all checks are straightforward bounds checks
against well-defined constants. The existing error return pattern is
preserved. The fix only adds stricter validation - it cannot break
valid filesystems.
- **Ratio:** Favorable for backport
---
## PHASE 9: FINAL SYNTHESIS
### Step 9.1: COMPILE THE EVIDENCE
**Evidence FOR backporting:**
- Fixes a real, reproducible syzbot bug (UBSAN shift-out-of-bounds in
dbSplit)
- Syzbot confirms the same bug exists on linux-6.1 and linux-5.15
(unpatched)
- Protects against undefined behavior from corrupt on-disk data
- Applied and tested by the JFS maintainer (Dave Kleikamp)
- Standalone patch, no dependencies
- Code being modified has existed since v2.6.12 (present in all stable
trees)
- Bug is triggerable from unprivileged userspace syscalls
- Pattern is consistent with dozens of similar JFS fixes that go to
stable
**Evidence AGAINST backporting:**
- Somewhat large (~110 lines of net change) for a stable fix
- Adds a new function rather than a minimal surgical fix
- Could theoretically reject valid dmapctl pages if the checks are too
strict (but checks match documented invariants)
- The `dbExtendFS()` additions are new check sites (not replacements),
adding code to paths that had none before
### Step 9.2: STABLE RULES CHECKLIST
1. **Obviously correct and tested?** YES - maintainer tested and
applied; checks are against documented constants
2. **Fixes a real bug?** YES - syzbot-reported UBSAN, reproducible with
C reproducer
3. **Important issue?** YES - undefined behavior (shift-out-of-bounds),
potential memory corruption
4. **Small and contained?** BORDERLINE - ~110 lines but all in one file,
one new function with clear purpose
5. **No new features or APIs?** YES - purely defensive validation
6. **Can apply to stable?** YES - code structure is stable across all
trees
### Step 9.3: EXCEPTION CATEGORIES
Not an exception category, but fits the standard "fixes real bug found
by fuzzer" pattern.
### Step 9.4: DECISION
The fix addresses a real, reproducible syzbot bug that exists in all
stable trees. While the patch is somewhat larger than ideal for stable
(adding a complete validation function), it is well-contained within a
single file and function, all checks are obviously correct bounds
checks, and it was tested by the subsystem maintainer. The undefined
behavior it prevents could lead to memory corruption or crashes on
corrupt JFS images. The syzbot data shows similar bugs are actively
reproducing on stable kernels (linux-6.1, linux-5.15). The benefit
clearly outweighs the moderate risk.
---
## Verification
- [Phase 1] Parsed tags: Reported-by syzbot+4c1966e88c28fa96e053,
Closes: syzkaller link, SOBs from author + JFS maintainer
- [Phase 2] Diff analysis: ~87 lines new validation function, 3
replacement sites, 5 new check sites, all in `fs/jfs/jfs_dmap.c`
- [Phase 2] Root cause: `BUDSIZE(cursz, budmin)` macro does `1 << (s -
m)`, corrupt leaf value (e.g. 115) → shift exponent 109
- [Phase 3] git blame: All 3 existing leafidx checks date to
`1da177e4c3f41` (v2.6.12, 2005), present in all stable trees
- [Phase 3] git log: File has extensive syzbot-driven fix history (20+
commits addressing similar corruption issues)
- [Phase 3] Author: Yun Zhou (Wind River), not regular JFS contributor
but patch applied by maintainer Dave Kleikamp
- [Phase 4] b4 dig: Found match at https://patch.msgid.link/202511281551
50....@wi...
- [Phase 4] b4 dig -a: v1 (2025/11/20), v2 (2025/11/28) - committed
version is v2 (latest)
- [Phase 4] b4 dig -w: JFS maintainer (sh...@ke...) was included,
tested and applied
- [Phase 4] syzbot page: Bug first crash 354d ago, last 36d ago;
reproducer available; similar bugs on linux-6.1 (0/3 patched) and
linux-5.15 (0/3 patched)
- [Phase 4] Maintainer comment: "This is finally tested and applied"
(Dave Kleikamp, 2026/03/16)
- [Phase 5] Call chain verified from syzbot trace: lsetxattr →
__jfs_setxattr → ea_write → dbAlloc → dbAllocNear → dbAdjCtl → dbSplit
(userspace reachable)
- [Phase 6] Buggy code present in all stable trees (since v2.6.12)
- [Phase 6] Backport: should apply cleanly, no significant structural
changes in the 3 replacement sites
- [Phase 8] Severity: HIGH (undefined behavior from shift-out-of-bounds,
potential memory corruption)
- [Phase 8] Trigger: unprivileged user setxattr on corrupt JFS
filesystem
**YES**
fs/jfs/jfs_dmap.c | 114 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 111 insertions(+), 3 deletions(-)
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 2abe8cc02ee6f..a841cf21da7de 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -133,6 +133,93 @@ static const s8 budtab[256] = {
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};
+/*
+ * check_dmapctl - Validate integrity of a dmapctl structure
+ * @dcp: Pointer to the dmapctl structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmapctl(struct dmapctl *dcp)
+{
+ s8 budmin = dcp->budmin;
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dcp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERCTL)) {
+ jfs_err("dmapctl: invalid nleafs %u (max %u)",
+ nleafs, LPERCTL);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dcp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERCTL)) {
+ jfs_err("dmapctl: invalid l2nleafs %u (max %u)",
+ l2nleafs, L2LPERCTL);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmapctl: nleafs %u != 2^%u",
+ nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dcp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != CTLLEAFIND)) {
+ jfs_err("dmapctl: invalid leafidx %u (expected %u)",
+ leafidx, CTLLEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dcp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > (L2LPERCTL >> 1))) {
+ jfs_err("dmapctl: invalid height %u (max %u)",
+ height, L2LPERCTL >> 1);
+ return false;
+ }
+
+ /* Check budmin is valid (cannot be NOFREE for non-empty tree) */
+ if (budmin == NOFREE) {
+ if (unlikely(nleafs > 0)) {
+ jfs_err("dmapctl: budmin is NOFREE but nleafs %u",
+ nleafs);
+ return false;
+ }
+ } else if (unlikely(budmin < BUDMIN)) {
+ jfs_err("dmapctl: invalid budmin %d (min %d)",
+ budmin, BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > CTLTREESIZE)) {
+ jfs_err("dmapctl: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, CTLTREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dcp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmapctl: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmapctl: leaf value %d too large at index %d", val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1372,7 +1459,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -1702,7 +1789,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb,
"Corrupt dmapctl page\n");
release_metapage(mp);
@@ -2485,7 +2572,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
return -EIO;
dcp = (struct dmapctl *) mp->data;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -3454,6 +3541,11 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
return -EIO;
}
l2dcp = (struct dmapctl *) l2mp->data;
+ if (unlikely(!check_dmapctl(l2dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ release_metapage(l2mp);
+ return -EIO;
+ }
/* compute start L1 */
k = blkno >> L2MAXL1SIZE;
@@ -3471,6 +3563,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l1mp == NULL)
goto errout;
l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start L0 */
j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE;
@@ -3484,6 +3580,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;
l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start L0 */
j = 0;
@@ -3503,6 +3603,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l0mp == NULL)
goto errout;
l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start dmap */
i = (blkno & (MAXL0SIZE - 1)) >>
@@ -3518,6 +3622,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;
l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start dmap */
i = 0;
--
2.53.0
|
|
From: Sasha L. <sa...@ke...> - 2026-04-20 13:16:39
|
From: Edward Adam Davis <ea...@qq...> [ Upstream commit b15e4310633f90072d66cc9b6692acbf6b4d7d00 ] In lbmRead(), the I/O event waited for by wait_event() finishes before it goes to sleep, and the lbmIODone() prematurely sets the flag to lbmDONE, thus ending the wait. This causes wait_event() to return before lbmREAD is cleared (because lbmDONE was set first), the premature return of wait_event() leads to the release of lbuf before lbmIODone() returns, thus triggering the use-after-free vulnerability reported in [1]. Moving the operation of setting the lbmDONE flag to after clearing lbmREAD in lbmIODone() avoids the use-after-free vulnerability reported in [1]. [1] BUG: KASAN: slab-use-after-free in rt_spin_lock+0x88/0x3e0 kernel/locking/spinlock_rt.c:56 Call Trace: blk_update_request+0x57e/0xe60 block/blk-mq.c:1007 blk_mq_end_request+0x3e/0x70 block/blk-mq.c:1169 blk_complete_reqs block/blk-mq.c:1244 [inline] blk_done_softirq+0x10a/0x160 block/blk-mq.c:1249 Allocated by task 6101: lbmLogInit fs/jfs/jfs_logmgr.c:1821 [inline] lmLogInit+0x3d0/0x19e0 fs/jfs/jfs_logmgr.c:1269 open_inline_log fs/jfs/jfs_logmgr.c:1175 [inline] lmLogOpen+0x4e1/0xfa0 fs/jfs/jfs_logmgr.c:1069 jfs_mount_rw+0xe9/0x670 fs/jfs/jfs_mount.c:257 jfs_fill_super+0x754/0xd80 fs/jfs/super.c:532 Freed by task 6101: kfree+0x1bd/0x900 mm/slub.c:6876 lbmLogShutdown fs/jfs/jfs_logmgr.c:1864 [inline] lmLogInit+0x1137/0x19e0 fs/jfs/jfs_logmgr.c:1415 open_inline_log fs/jfs/jfs_logmgr.c:1175 [inline] lmLogOpen+0x4e1/0xfa0 fs/jfs/jfs_logmgr.c:1069 jfs_mount_rw+0xe9/0x670 fs/jfs/jfs_mount.c:257 jfs_fill_super+0x754/0xd80 fs/jfs/super.c:532 Reported-by: syz...@sy... Closes: https://syzkaller.appspot.com/bug?extid=1d38eedcb25a3b5686a7 Signed-off-by: Edward Adam Davis <ea...@qq...> Signed-off-by: Dave Kleikamp <dav...@or...> Signed-off-by: Sasha Levin <sa...@ke...> --- LLM Generated explanations, may be completely bogus: Now I have all the information needed to complete the full analysis. Let me compile the results. --- ## PHASE 1: COMMIT MESSAGE FORENSICS **Step 1.1: Subject Line** Record: [jfs] [Set/fix] Move the lbmDONE flag assignment to the end of lbmIODone to prevent a use-after-free race. **Step 1.2: Tags** - Reported-by: syz...@sy... (syzbot fuzzer-found bug) - Closes: https://syzkaller.appspot.com/bug?extid=1d38eedcb25a3b5686a7 (syzbot bug tracker) - Signed-off-by: Edward Adam Davis <ea...@qq...> (author) - Signed-off-by: Dave Kleikamp <dav...@or...> (JFS maintainer) - No Fixes: tag, no Cc: stable (expected for candidates under review) Record: Syzbot-reported, maintainer-signed. Strong quality signals. **Step 1.3: Commit Body** The commit describes a race in `lbmRead()`: the `wait_event()` condition `bp->l_flag != lbmREAD` becomes true prematurely when `lbmIODone()` sets `lbmDONE` BEFORE clearing `lbmREAD`. This allows `wait_event()` to return before `lbmIODone()` finishes processing the buffer, causing the buffer to be freed while `lbmIODone()` is still accessing it. Full KASAN stack trace included showing slab-use-after-free. Record: Bug = race condition causing UAF. Symptom = KASAN slab-use- after-free in `blk_update_request`. Trigger = JFS mount path during log initialization. **Step 1.4: Hidden Bug Fix?** No disguise needed - this is an explicit use-after-free fix with KASAN evidence. ## PHASE 2: DIFF ANALYSIS **Step 2.1: Inventory** - Single file: `fs/jfs/jfs_logmgr.c` - Net change: ~7 lines removed, ~6 lines added (very surgical) - Functions modified: `lbmIODone()` only **Step 2.2: Code Flow Changes** The key changes are: 1. **Removed** `bp->l_flag |= lbmDONE` from the beginning of `lbmIODone()` 2. **READ path**: Removed early `LCACHE_UNLOCK` and `return`, replaced with `LCACHE_WAKEUP` while still holding the lock, then `goto out` 3. **DIRECT path**: Changed `LCACHE_UNLOCK; return` to `goto out` 4. **SYNC path**: Removed `LCACHE_UNLOCK` before `LCACHE_WAKEUP` 5. **GC path**: Added `LCACHE_LOCK(flags)` after `lmPostGC(bp)` to re- acquire lock (avoiding the deadlock v1 had) 6. **ASYNC path**: Removed explicit `LCACHE_UNLOCK` 7. **Added** `out:` label at end: `bp->l_flag |= lbmDONE; LCACHE_UNLOCK(flags);` Record: Single function refactored to consolidate exit paths through single `out:` label. The `lbmDONE` flag is now set as the very last operation before releasing the lock. **Step 2.3: Bug Mechanism** Category: **Race condition + Use-after-free** The race: 1. `lbmRead()` submits bio, then calls `wait_event(bp->l_ioevent, (bp->l_flag != lbmREAD))` 2. `lbmIODone()` (bio completion handler) sets `lbmDONE` early → `l_flag` becomes `lbmREAD | lbmDONE` which `!= lbmREAD` 3. `wait_event()` on another CPU sees condition true (lockless check), returns 4. Caller continues, eventually frees the buffer on error paths (`lbmLogShutdown` → `kfree`) 5. `lbmIODone()` is still executing, accesses freed buffer → UAF **Step 2.4: Fix Quality** - Obviously correct: centralizes exit and delays the "done" signal to after all processing - Minimal and surgical: single function, ~13 lines changed - The v2 correctly handles the GC path deadlock (drops lock for `lmPostGC`, re-acquires after) - Low regression risk: the lock ordering is preserved, exit paths are consolidated ## PHASE 3: GIT HISTORY INVESTIGATION **Step 3.1: Blame** The buggy code (`bp->l_flag |= lbmDONE` at the start of `lbmIODone`) was introduced in the initial Linux commit `1da177e4c3f41` (Linus Torvalds, 2005-04-16). This bug has been present since the very beginning of the git history - ALL kernel versions are affected. **Step 3.2: Fixes tag** No Fixes: tag present. The bug predates all stable branch points. **Step 3.3: File History** No changes to `fs/jfs/jfs_logmgr.c` since v7.0. The file matches the expected pre-patch state exactly. The fix applies cleanly. **Step 3.4: Author** Edward Adam Davis is a frequent contributor of syzbot-triggered fixes. Dave Kleikamp (JFS maintainer) tested and applied the patch. **Step 3.5: Dependencies** No dependencies. This is a self-contained fix to a single function. The code in the 7.0 tree at line 2183 matches the old code exactly. ## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH **Step 4.1: Patch Discussion** - b4 dig found the original submission: https://patch.msgid.link/tencent _48...@qq... - Two revisions: v1 introduced a deadlock (caught by syzbot CI), v2 fixed the deadlock by keeping the `LCACHE_UNLOCK/lmPostGC/LCACHE_LOCK` pattern for the GC path - Dave Kleikamp (JFS maintainer "Shaggy") replied: "Finally tested and applied to jfs-next" **Step 4.2: Reviewers** - CC'd: jfs-discussion, linux-block, linux-kernel, Jens Axboe, Dave Kleikamp - Applied by the JFS maintainer after testing **Step 4.3: Bug Report** - Syzbot first reported this bug on Nov 1, 2024 (6.12-rc5) - C reproducer available since Dec 7, 2025 - Bug has been active for 708+ days - Multiple syzbot reproducers show it's reliably triggerable - Also found on linux-6.1 stable tree (separate syzbot entry) - Jens Axboe reassigned the subsystem from "block?" to "jfs" **Step 4.4: Series Context** Standalone single patch (not part of a series). The v2 is the final applied version. **Step 4.5: Stable Discussion** No explicit stable nomination found, but the syzbot bug page shows it was also found on linux-6.1 stable tree. ## PHASE 5: CODE SEMANTIC ANALYSIS **Step 5.1: Functions Modified** Only `lbmIODone()` - the bio completion handler for JFS log buffers. **Step 5.2: Callers** `lbmIODone` is called: 1. As `bio->bi_end_io` callback from block layer (softirq context) - async I/O completion 2. Directly from `lbmRead()` when `log->no_integrity` is true (synchronous) **Step 5.3: Callees** `lbmIODone` calls: `bio_put`, `LCACHE_WAKEUP` (wake_up), `lbmRedrive`, `lmPostGC`, `lbmfree` **Step 5.4: Call Chain** Trigger path: `mount()` → `jfs_fill_super` → `jfs_mount_rw` → `lmLogOpen` → `lmLogInit` → `lbmRead` → `submit_bio` → (bio completion on another CPU) → `lbmIODone` → UAF This is reachable from the `mount()` syscall - unprivileged users can trigger it (with a crafted JFS filesystem image). **Step 5.5: Similar Patterns** `lbmIOWait()` uses `LCACHE_SLEEP_COND` which holds the lock during condition checks, making it immune to this race. Only `lbmRead()` uses the lockless `wait_event` and is affected. ## PHASE 6: STABLE TREE ANALYSIS **Step 6.1: Code Existence** The buggy code has been present since 2005. It exists in ALL stable trees. The syzbot dashboard confirms it was also found on linux-6.1. **Step 6.2: Backport Complications** The file has NO changes since v7.0 in this tree. The patch should apply cleanly. **Step 6.3: Related Fixes** No related fixes found in the tree. This is the first fix for this bug. ## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT **Step 7.1: Subsystem** JFS filesystem (`fs/jfs/`). Criticality: IMPORTANT - filesystem bugs can lead to data corruption or system crashes. While JFS is not the most widely used filesystem, it has real users especially in enterprise environments. **Step 7.2: Activity** JFS is a mature, low-activity subsystem. The bug has been present for ~20 years, affecting all kernel versions. ## PHASE 8: IMPACT AND RISK ASSESSMENT **Step 8.1: Affected Users** All users who mount JFS filesystems. Also security-relevant as it can be triggered with a crafted filesystem image. **Step 8.2: Trigger Conditions** - Triggered during JFS mount (`lmLogInit` → `lbmRead`) - Requires multi-CPU system and specific timing (I/O completion racing with `wait_event`) - Syzbot has a C reproducer - reliably triggerable - Can be triggered by unprivileged users mounting a filesystem (depending on policy) **Step 8.3: Failure Mode** KASAN: slab-use-after-free → potential kernel crash, memory corruption, or exploitable vulnerability. Severity: **CRITICAL** **Step 8.4: Risk-Benefit Ratio** - BENEFIT: Very high - fixes a KASAN-detected UAF with reproducer, present in all kernels - RISK: Low - single file, well-contained, tested by maintainer, v2 addressed deadlock issue from v1 - The change consolidates exit paths which is structurally cleaner ## PHASE 9: FINAL SYNTHESIS **Step 9.1: Evidence Compilation** FOR backporting: - Fixes slab-use-after-free (KASAN-verified, syzbot-reported with C reproducer) - Bug present since initial Linux git commit (2005) - affects ALL stable trees - Triggered from mount() syscall - reachable from userspace - Single file, single function, ~13 lines changed - JFS maintainer tested and applied - v2 iteration fixed a deadlock found by syzbot CI in v1 - Clean apply expected (no file changes since v7.0) - Bug active 708+ days, also found on linux-6.1 stable AGAINST backporting: - (None significant) **Step 9.2: Stable Rules Checklist** 1. Obviously correct and tested? **YES** - maintainer tested, v2 addresses v1's deadlock 2. Fixes a real bug? **YES** - KASAN UAF with syzbot reproducer 3. Important issue? **YES** - UAF in filesystem mount path (crash/security) 4. Small and contained? **YES** - single function in single file 5. No new features/APIs? **YES** - pure bug fix 6. Can apply to stable? **YES** - no changes to file since v7.0 **Step 9.3: Exception Categories** Not an exception category - this is a standard high-priority bug fix. **Step 9.4: Decision** Clear YES. This fixes a critical use-after-free vulnerability in JFS that has been present since 2005, is syzbot-verified with a reproducer, and is a small, well-tested, maintainer-approved fix. --- ## Verification - [Phase 1] Parsed tags: Reported-by: syzbot, Closes: syzkaller link, SOB from author and JFS maintainer - [Phase 2] Diff analysis: `lbmDONE` flag move from start to end of `lbmIODone()`, consolidated exit paths via `goto out` - [Phase 3] git blame: buggy code (`bp->l_flag |= lbmDONE` at line 2183) from commit 1da177e4c3f41 (2005), present in ALL kernels - [Phase 3] git log v7.0..: confirmed no changes to file since v7.0 branch point - [Phase 4] b4 dig -c b15e4310633f: found original at https://patch.msgi d.link/ten...@qq... - [Phase 4] b4 dig -a: two revisions (v1 had deadlock, v2 fixed it) - [Phase 4] b4 dig -w: JFS maintainer (Dave Kleikamp/sh...@ke...), Jens Axboe, mailing lists were CC'd - [Phase 4] mbox thread: Dave Kleikamp tested and applied to jfs-next; syzbot CI found deadlock in v1 confirming proper testing - [Phase 4] syzbot dashboard: bug first reported Nov 2024, C reproducer available, 708+ days active, also on linux-6.1 - [Phase 5] Traced call chain: mount() → jfs_fill_super → jfs_mount_rw → lmLogOpen → lmLogInit → lbmRead → submit_bio → lbmIODone (race) - [Phase 5] Verified `lbmRead` uses lockless `wait_event` (line 1989) vs `lbmIOWait` using locked `LCACHE_SLEEP_COND` (line 2151) - [Phase 5] Verified `__SLEEP_COND` macro in jfs_lock.h - confirmed lockless condition check in wait_event is the root cause - [Phase 6] Code exists in ALL stable trees (bug since 2005) - [Phase 6] Clean apply expected - no file changes since v7.0 - [Phase 8] Failure mode: slab-use-after-free → potential kernel crash/exploit, severity CRITICAL **YES** fs/jfs/jfs_logmgr.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index d8266220776e8..306165e61438c 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -2178,8 +2178,6 @@ static void lbmIODone(struct bio *bio) LCACHE_LOCK(flags); /* disable+lock */ - bp->l_flag |= lbmDONE; - if (bio->bi_status) { bp->l_flag |= lbmERROR; @@ -2194,12 +2192,10 @@ static void lbmIODone(struct bio *bio) if (bp->l_flag & lbmREAD) { bp->l_flag &= ~lbmREAD; - LCACHE_UNLOCK(flags); /* unlock+enable */ - /* wakeup I/O initiator */ LCACHE_WAKEUP(&bp->l_ioevent); - return; + goto out; } /* @@ -2223,8 +2219,7 @@ static void lbmIODone(struct bio *bio) if (bp->l_flag & lbmDIRECT) { LCACHE_WAKEUP(&bp->l_ioevent); - LCACHE_UNLOCK(flags); - return; + goto out; } tail = log->wqueue; @@ -2276,8 +2271,6 @@ static void lbmIODone(struct bio *bio) * leave buffer for i/o initiator to dispose */ if (bp->l_flag & lbmSYNC) { - LCACHE_UNLOCK(flags); /* unlock+enable */ - /* wakeup I/O initiator */ LCACHE_WAKEUP(&bp->l_ioevent); } @@ -2288,6 +2281,7 @@ static void lbmIODone(struct bio *bio) else if (bp->l_flag & lbmGC) { LCACHE_UNLOCK(flags); lmPostGC(bp); + LCACHE_LOCK(flags); /* disable+lock */ } /* @@ -2300,9 +2294,11 @@ static void lbmIODone(struct bio *bio) assert(bp->l_flag & lbmRELEASE); assert(bp->l_flag & lbmFREE); lbmfree(bp); - - LCACHE_UNLOCK(flags); /* unlock+enable */ } + +out: + bp->l_flag |= lbmDONE; + LCACHE_UNLOCK(flags); } int jfsIOWait(void *arg) -- 2.53.0 |
|
From: Tristan M. <tr...@gm...> - 2026-04-18 13:10:26
|
From: Tristan Madani <tr...@ta...>
diMount() reads l2nbperpage from the disk inode aggregate without
validation. A corrupted filesystem image can set this field to a value
exceeding the bit width of the shift operand in jfs_statfs(), causing
UBSAN shift-out-of-bounds.
Add validation of l2nbperpage against reasonable bounds before using it
in arithmetic operations.
Reported-by: syz...@sy...
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: st...@vg...
Signed-off-by: Tristan Madani <tr...@ta...>
---
fs/jfs/jfs_imap.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
index b84ba4d7dfb44..eafbd2b55df75 100644
--- a/fs/jfs/jfs_imap.c
+++ b/fs/jfs/jfs_imap.c
@@ -124,6 +124,18 @@ int diMount(struct inode *ipimap)
atomic_set(&imap->im_numfree, le32_to_cpu(dinom_le->in_numfree));
imap->im_nbperiext = le32_to_cpu(dinom_le->in_nbperiext);
imap->im_l2nbperiext = le32_to_cpu(dinom_le->in_l2nbperiext);
+
+ if (imap->im_l2nbperiext < 0 ||
+ imap->im_l2nbperiext > 30 ||
+ imap->im_nbperiext != (1 << imap->im_l2nbperiext)) {
+ jfs_err("diMount: invalid imap parameters: "
+ "nbperiext(%d) l2nbperiext(%d)",
+ imap->im_nbperiext, imap->im_l2nbperiext);
+ release_metapage(mp);
+ kfree(imap);
+ return -EINVAL;
+ }
+
for (index = 0; index < MAXAG; index++) {
imap->im_agctl[index].inofree =
le32_to_cpu(dinom_le->in_agctl[index].inofree);
--
2.47.3
|
|
From: Tristan M. <tr...@gm...> - 2026-04-18 13:10:22
|
From: Tristan Madani <tr...@ta...>
BT_STACK_DUMP() iterates over MAXTREEHEIGHT entries in the btstack
regardless of how many entries were actually pushed. This reads
uninitialized stack entries beyond the current depth.
Fix by computing the actual depth from btstack->top and limiting the
loop to only initialized entries.
Reported-by: syz...@sy...
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: st...@vg...
Signed-off-by: Tristan Madani <tr...@ta...>
---
fs/jfs/jfs_btree.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/jfs/jfs_btree.h b/fs/jfs/jfs_btree.h
index ce055ef50cd35..26dd5acddcfeb 100644
--- a/fs/jfs/jfs_btree.h
+++ b/fs/jfs/jfs_btree.h
@@ -131,8 +131,10 @@ struct btstack {
static inline void BT_STACK_DUMP(struct btstack *btstack)
{
int i;
+ int depth = btstack->top - btstack->stack;
+
printk("btstack dump:\n");
- for (i = 0; i < MAXTREEHEIGHT; i++)
+ for (i = 0; i < depth; i++)
printk(KERN_ERR "bn = %Lx, index = %d\n",
(long long)btstack->stack[i].bn,
btstack->stack[i].index);
--
2.47.3
|
|
From: Tristan M. <tr...@gm...> - 2026-04-18 13:07:50
|
From: Tristan Madani <tr...@ta...>
lbmIODone() wakes up the I/O initiator in multiple paths before it has
finished accessing the lbuf structure. The initiator can then free the
lbuf while lbmIODone() is still running, leading to a use-after-free.
Fix by deferring all wakeups to the common exit path after the lbmDONE
flag is set. Change the wait condition to check for lbmDONE rather than
!lbmREAD, so the waiter only proceeds after lbmIODone() has completely
finished with the buffer.
Reported-by: syz...@sy...
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: st...@vg...
Signed-off-by: Tristan Madani <tr...@ta...>
---
fs/jfs/jfs_logmgr.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
index 306165e61438c..cbe3878ff8867 100644
--- a/fs/jfs/jfs_logmgr.c
+++ b/fs/jfs/jfs_logmgr.c
@@ -1984,7 +1984,7 @@ static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp)
submit_bio(bio);
}
- wait_event(bp->l_ioevent, (bp->l_flag != lbmREAD));
+ wait_event(bp->l_ioevent, (bp->l_flag & lbmDONE));
return 0;
}
@@ -2192,9 +2192,6 @@ static void lbmIODone(struct bio *bio)
if (bp->l_flag & lbmREAD) {
bp->l_flag &= ~lbmREAD;
- /* wakeup I/O initiator */
- LCACHE_WAKEUP(&bp->l_ioevent);
-
goto out;
}
@@ -2218,7 +2215,6 @@ static void lbmIODone(struct bio *bio)
log->clsn = (bp->l_pn << L2LOGPSIZE) + bp->l_ceor;
if (bp->l_flag & lbmDIRECT) {
- LCACHE_WAKEUP(&bp->l_ioevent);
goto out;
}
@@ -2271,8 +2267,7 @@ static void lbmIODone(struct bio *bio)
* leave buffer for i/o initiator to dispose
*/
if (bp->l_flag & lbmSYNC) {
- /* wakeup I/O initiator */
- LCACHE_WAKEUP(&bp->l_ioevent);
+ goto out;
}
/*
@@ -2298,6 +2293,8 @@ static void lbmIODone(struct bio *bio)
out:
bp->l_flag |= lbmDONE;
+ /* wakeup I/O initiator */
+ LCACHE_WAKEUP(&bp->l_ioevent);
LCACHE_UNLOCK(flags);
}
--
2.47.3
|
|
From: Edward A. D. <ea...@qq...> - 2026-04-18 09:05:32
|
Fixed the sequencing of setting the DONE flag and waking up the ioevent.
The ioevent wakeup must occur after the DONE flag has been set, and while
under the protection of the jfsLCacheLock. This ensures that when the
thread associated with wait_event() resumes execution (e.g., in lbmRead/
Write/IOWait, etc.), it will strictly avoid accessing any content related
to the bio, simultaneously, this guarantees the stable and proper shutdown
of subsequent log I/O operations.
Fixes: b15e4310633f ("jfs: Set the lbmDone flag at the end of lbmIODone")
Reported-by: syz...@sy...
Closes: https://syzkaller.appspot.com/bug?extid=ecf51a7ccb6b1394e90c
Tested-by: syz...@sy...
Signed-off-by: Edward Adam Davis <ea...@qq...>
---
fs/jfs/jfs_logmgr.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
index 306165e61438..f795f19d24bb 100644
--- a/fs/jfs/jfs_logmgr.c
+++ b/fs/jfs/jfs_logmgr.c
@@ -1984,7 +1984,7 @@ static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp)
submit_bio(bio);
}
- wait_event(bp->l_ioevent, (bp->l_flag != lbmREAD));
+ wait_event(bp->l_ioevent, (bp->l_flag & lbmDONE));
return 0;
}
@@ -2192,9 +2192,6 @@ static void lbmIODone(struct bio *bio)
if (bp->l_flag & lbmREAD) {
bp->l_flag &= ~lbmREAD;
- /* wakeup I/O initiator */
- LCACHE_WAKEUP(&bp->l_ioevent);
-
goto out;
}
@@ -2217,10 +2214,8 @@ static void lbmIODone(struct bio *bio)
log = bp->l_log;
log->clsn = (bp->l_pn << L2LOGPSIZE) + bp->l_ceor;
- if (bp->l_flag & lbmDIRECT) {
- LCACHE_WAKEUP(&bp->l_ioevent);
+ if (bp->l_flag & lbmDIRECT)
goto out;
- }
tail = log->wqueue;
@@ -2271,8 +2266,7 @@ static void lbmIODone(struct bio *bio)
* leave buffer for i/o initiator to dispose
*/
if (bp->l_flag & lbmSYNC) {
- /* wakeup I/O initiator */
- LCACHE_WAKEUP(&bp->l_ioevent);
+ goto out;
}
/*
@@ -2298,6 +2292,8 @@ static void lbmIODone(struct bio *bio)
out:
bp->l_flag |= lbmDONE;
+ /* wakeup I/O initiator */
+ LCACHE_WAKEUP(&bp->l_ioevent);
LCACHE_UNLOCK(flags);
}
--
2.43.0
|
|
From: Jori K. <jko...@xs...> - 2026-04-16 10:15:33
|
> Op 16-04-2026 12:01 CEST schreef Jori Koolstra <jko...@xs...>:
> > ---
> > fs/jfs/xattr.c | 8 +++++++-
> > 1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c
> > index 11d7f74d2..320659bb8 100644
> > --- a/fs/jfs/xattr.c
> > +++ b/fs/jfs/xattr.c
> > @@ -688,8 +688,14 @@ int __jfs_setxattr(tid_t tid, struct inode *inode, const char *name,
> > new_size = sizeof (struct jfs_ea_list);
> >
> > if (xattr_size) {
> > - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist);
> > + struct jfs_ea *ealist_end = END_EALIST(ealist);
> > + for (ea = FIRST_EA(ealist); ea < ealist_end;
> > ea = NEXT_EA(ea)) {
> > + if (unlikely(ea + 1 > ealist_end) ||
>
> What use is this check? How can ea + 1 > ealist_end immediately after
> ea < ealist_end?
>
Maybe I should rephrase this a bit more clearly: does the
unlikely(NEXT_EA(ea) > ealist_end))
already take care of that potential scenario?
|
|
From: Jori K. <jko...@xs...> - 2026-04-16 10:01:53
|
On Mon, Apr 13, 2026 at 08:03:28AM -0600, Sebastian Alba Vives wrote:
> The loop in __jfs_setxattr() iterates over EA entries without validating
> that NEXT_EA(ea) stays within the ealist buffer. A crafted filesystem
> image with a manipulated EA valuelen field can cause NEXT_EA() to land
> inside the buffer but past the last valid entry, leading to out-of-bounds
> reads in the subsequent memcmp() call.
>
> Fix this by adding the same bounds check already present in
> __jfs_getxattr() and jfs_listxattr().
>
> Reported-by: Sebastian Alba Vives <seb...@gm...>
> Signed-off-by: Sebastian Alba Vives <seb...@gm...>
> ---
> fs/jfs/xattr.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c
> index 11d7f74d2..320659bb8 100644
> --- a/fs/jfs/xattr.c
> +++ b/fs/jfs/xattr.c
> @@ -688,8 +688,14 @@ int __jfs_setxattr(tid_t tid, struct inode *inode, const char *name,
> new_size = sizeof (struct jfs_ea_list);
>
> if (xattr_size) {
> - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist);
> + struct jfs_ea *ealist_end = END_EALIST(ealist);
> + for (ea = FIRST_EA(ealist); ea < ealist_end;
> ea = NEXT_EA(ea)) {
> + if (unlikely(ea + 1 > ealist_end) ||
What use is this check? How can ea + 1 > ealist_end immediately after
ea < ealist_end?
> + unlikely(NEXT_EA(ea) > ealist_end)) {
> + rc = -EUCLEAN;
> + goto release;
> + }
> if ((namelen == ea->namelen) &&
> (memcmp(name, ea->name, namelen) == 0)) {
> found = 1;
> --
> 2.43.0
>
You should also know that jfs is not maintained, and there are MANY
of these issues (see syzbot for instance). To my knowledge the community
has decided to deprioritize these kinds of issues, and perhaps
eventually move to FUSE based implementations for legacy filesystems.
Also, you should cc the general list lin...@vg... and
the jfs maintainer Dave Kleikamp <sh...@ke...>. Use the
get_maintainer script.
That being said these fixes are still accepted. Thanks for your (first?)
contribution :)
Thanks,
Jori.
|
|
From: <pr-...@ke...> - 2026-04-16 02:54:40
|
The pull request you sent on Wed, 15 Apr 2026 08:39:52 -0500: > gi...@gi...:kleikamp/linux-shaggy.git tags/jfs-7.1 has been merged into torvalds/linux.git: https://git.kernel.org/torvalds/c/1d51b370a0f8f642f4fc84c795fbedac0fcdbbd2 Thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/prtracker.html |
|
From: Dave K. <dav...@or...> - 2026-04-15 13:40:07
|
The following changes since commit 1f318b96cc84d7c2ab792fcc0bfd42a7ca890681:
Linux 7.0-rc3 (2026-03-08 16:56:54 -0700)
are available in the Git repository at:
gi...@gi...:kleikamp/linux-shaggy.git tags/jfs-7.1
for you to fetch changes up to dad98c5b2a05ef744af4c884c97066a3c8cdad61:
jfs: avoid -Wtautological-constant-out-of-range-compare warning again
(2026-03-16 10:20:47 -0500)
----------------------------------------------------------------
More robust data integrity checking and some fixes.
----------------------------------------------------------------
Arnd Bergmann (1):
jfs: avoid -Wtautological-constant-out-of-range-compare warning again
Edward Adam Davis (1):
jfs: Set the lbmDone flag at the end of lbmIODone
Helen Koike (1):
jfs: hold LOG_LOCK on umount to avoid null-ptr-deref
João Paredes (1):
JFS: always load filesystem UUID during mount
Yun Zhou (4):
jfs: add dtroot integrity check to prevent index out-of-bounds
jfs: add dtpage integrity check to prevent index/pointer overflows
jfs: add dmapctl integrity check to prevent invalid operations
jfs: fix corrupted list in dbUpdatePMap
fs/jfs/jfs_dmap.c | 114 +++++++++++++++++++++++++++++-
fs/jfs/jfs_dtree.c | 192
++++++++++++++++++++++++++++++++++++++++++++++++--
fs/jfs/jfs_dtree.h | 4 ++
fs/jfs/jfs_imap.c | 4 ++
fs/jfs/jfs_logmgr.c | 34 ++++-----
fs/jfs/jfs_logmgr.h | 7 ++
fs/jfs/jfs_metapage.c | 3 +-
fs/jfs/jfs_mount.c | 3 +-
fs/jfs/jfs_txnmgr.c | 3 +-
fs/jfs/jfs_umount.c | 10 +++
10 files changed, 344 insertions(+), 30 deletions(-)
|
|
From: syzbot <syz...@sy...> - 2026-04-15 09:06:42
|
syzbot has found a reproducer for the following issue on: HEAD commit: e6efabc0afca Add linux-next specific files for 20260414 git tree: linux-next console output: https://syzkaller.appspot.com/x/log.txt?x=17ee18ce580000 kernel config: https://syzkaller.appspot.com/x/.config?x=56c2b36de3316f1b dashboard link: https://syzkaller.appspot.com/bug?extid=ecf51a7ccb6b1394e90c compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 syz repro: https://syzkaller.appspot.com/x/repro.syz?x=11dc01ba580000 C reproducer: https://syzkaller.appspot.com/x/repro.c?x=173bfb02580000 Downloadable assets: disk image: https://storage.googleapis.com/syzbot-assets/e7099cbf73e4/disk-e6efabc0.raw.xz vmlinux: https://storage.googleapis.com/syzbot-assets/439c402df1b9/vmlinux-e6efabc0.xz kernel image: https://storage.googleapis.com/syzbot-assets/fc0c0175fc76/bzImage-e6efabc0.xz mounted in repro: https://storage.googleapis.com/syzbot-assets/81df22d6836d/mount_0.gz fsck result: failed (log: https://syzkaller.appspot.com/x/fsck.log?x=107dfcd2580000) IMPORTANT: if you fix the issue, please add the following tag to the commit: Reported-by: syz...@sy... ================================================================== BUG: KASAN: slab-use-after-free in lbmIODone+0x1312/0x16c0 fs/jfs/jfs_logmgr.c:2192 Read of size 4 at addr ffff88802a45a608 by task ksoftirqd/1/31 CPU: 1 UID: 0 PID: 31 Comm: ksoftirqd/1 Not tainted syzkaller #0 PREEMPT_{RT,(full)} Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/18/2026 Call Trace: <TASK> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 lbmIODone+0x1312/0x16c0 fs/jfs/jfs_logmgr.c:2192 blk_update_request+0x57e/0xe60 block/blk-mq.c:1016 blk_mq_end_request+0x3e/0x70 block/blk-mq.c:1178 blk_complete_reqs block/blk-mq.c:1253 [inline] blk_done_softirq+0x10a/0x160 block/blk-mq.c:1258 handle_softirqs+0x1de/0x6d0 kernel/softirq.c:622 run_ksoftirqd+0x52/0x180 kernel/softirq.c:1076 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK> Allocated by task 6106: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x3a6/0x690 mm/slub.c:5415 kmalloc_noprof include/linux/slab.h:950 [inline] lbmLogInit fs/jfs/jfs_logmgr.c:1819 [inline] lmLogInit+0x3e5/0x1a00 fs/jfs/jfs_logmgr.c:1267 open_inline_log fs/jfs/jfs_logmgr.c:1173 [inline] lmLogOpen+0x4e1/0xfa0 fs/jfs/jfs_logmgr.c:1067 jfs_mount_rw+0xee/0x670 fs/jfs/jfs_mount.c:257 jfs_fill_super+0x754/0xd80 fs/jfs/super.c:532 get_tree_bdev_flags+0x431/0x4f0 fs/super.c:1694 vfs_get_tree+0x92/0x2a0 fs/super.c:1754 fc_mount fs/namespace.c:1193 [inline] do_new_mount_fc fs/namespace.c:3758 [inline] do_new_mount+0x341/0xd30 fs/namespace.c:3834 do_mount fs/namespace.c:4167 [inline] __do_sys_mount fs/namespace.c:4399 [inline] __se_sys_mount+0x31d/0x420 fs/namespace.c:4376 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 5969: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2689 [inline] slab_free mm/slub.c:6246 [inline] kfree+0x1c5/0x6c0 mm/slub.c:6561 lbmLogShutdown fs/jfs/jfs_logmgr.c:1862 [inline] lmLogShutdown+0x456/0x850 fs/jfs/jfs_logmgr.c:1681 lmLogClose+0x28a/0x520 fs/jfs/jfs_logmgr.c:1457 jfs_umount+0x2fb/0x3d0 fs/jfs/jfs_umount.c:124 jfs_put_super+0x8c/0x190 fs/jfs/super.c:194 generic_shutdown_super+0x13d/0x2d0 fs/super.c:646 kill_block_super+0x44/0x90 fs/super.c:1725 deactivate_locked_super+0xbc/0x130 fs/super.c:476 cleanup_mnt+0x437/0x4d0 fs/namespace.c:1312 task_work_run+0x1d9/0x270 kernel/task_work.c:233 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] __exit_to_user_mode_loop kernel/entry/common.c:67 [inline] exit_to_user_mode_loop+0xed/0x480 kernel/entry/common.c:98 __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline] syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:238 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:328 [inline] do_syscall_64+0x33e/0xf80 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff88802a45a600 which belongs to the cache kmalloc-256 of size 256 The buggy address is located 8 bytes inside of freed 256-byte region [ffff88802a45a600, ffff88802a45a700) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x2a45a head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x80000000000040(head|node=0|zone=1) page_type: f5(slab) raw: 0080000000000040 ffff88801a02cb40 dead000000000100 dead000000000122 raw: 0000000000000000 0000000800100010 00000000f5000000 0000000000000000 head: 0080000000000040 ffff88801a02cb40 dead000000000100 dead000000000122 head: 0000000000000000 0000000800100010 00000000f5000000 0000000000000000 head: 0080000000000001 ffffffffffffff81 00000000ffffffff 00000000ffffffff head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000002 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 1, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 155, tgid 155 (kworker/u8:6), ts 12504727555, free_ts 0 set_page_owner include/linux/page_owner.h:32 [inline] post_alloc_hook+0x231/0x280 mm/page_alloc.c:1858 prep_new_page mm/page_alloc.c:1866 [inline] get_page_from_freelist+0x27d6/0x2850 mm/page_alloc.c:3946 __alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5226 alloc_slab_page mm/slub.c:3278 [inline] allocate_slab+0x77/0x660 mm/slub.c:3467 new_slab mm/slub.c:3525 [inline] refill_objects+0x33c/0x3d0 mm/slub.c:7251 refill_sheaf mm/slub.c:2816 [inline] __pcs_replace_empty_main+0x373/0x720 mm/slub.c:4651 alloc_from_pcs mm/slub.c:4749 [inline] slab_alloc_node mm/slub.c:4883 [inline] __kmalloc_cache_noprof+0x44e/0x690 mm/slub.c:5410 kmalloc_noprof include/linux/slab.h:950 [inline] scsi_probe_and_add_lun+0x2f8/0x48e0 drivers/scsi/scsi_scan.c:1225 __scsi_scan_target+0x1f0/0xe10 drivers/scsi/scsi_scan.c:1786 scsi_scan_channel drivers/scsi/scsi_scan.c:1874 [inline] scsi_scan_host_selected+0x3d3/0x780 drivers/scsi/scsi_scan.c:1903 do_scsi_scan_host drivers/scsi/scsi_scan.c:2036 [inline] do_scan_async+0x124/0x6f0 drivers/scsi/scsi_scan.c:2046 async_run_entry_fn+0xa8/0x440 kernel/async.c:129 process_one_work kernel/workqueue.c:3308 [inline] process_scheduled_works+0xb68/0x1910 kernel/workqueue.c:3399 worker_thread+0xa90/0x1040 kernel/workqueue.c:3485 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 page_owner free stack trace missing Memory state around the buggy address: ffff88802a45a500: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88802a45a580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff88802a45a600: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff88802a45a680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88802a45a700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== --- If you want syzbot to run the reproducer, reply with: #syz test: git://repo/address.git branch-or-commit-hash If you attach or paste a git patch, syzbot will apply it before testing. |
|
From: syzbot <syz...@sy...> - 2026-04-15 08:32:49
|
syzbot has found a reproducer for the following issue on: HEAD commit: e6efabc0afca Add linux-next specific files for 20260414 git tree: linux-next console output: https://syzkaller.appspot.com/x/log.txt?x=161a18ce580000 kernel config: https://syzkaller.appspot.com/x/.config?x=56c2b36de3316f1b dashboard link: https://syzkaller.appspot.com/bug?extid=ecf51a7ccb6b1394e90c compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 syz repro: https://syzkaller.appspot.com/x/repro.syz?x=107784ce580000 Downloadable assets: disk image: https://storage.googleapis.com/syzbot-assets/e7099cbf73e4/disk-e6efabc0.raw.xz vmlinux: https://storage.googleapis.com/syzbot-assets/439c402df1b9/vmlinux-e6efabc0.xz kernel image: https://storage.googleapis.com/syzbot-assets/fc0c0175fc76/bzImage-e6efabc0.xz mounted in repro: https://storage.googleapis.com/syzbot-assets/01761e564f3f/mount_0.gz fsck result: failed (log: https://syzkaller.appspot.com/x/fsck.log?x=168401ba580000) IMPORTANT: if you fix the issue, please add the following tag to the commit: Reported-by: syz...@sy... ================================================================== BUG: KASAN: slab-use-after-free in lbmIODone+0x1312/0x16c0 fs/jfs/jfs_logmgr.c:2192 Read of size 4 at addr ffff888032de1208 by task ksoftirqd/1/31 CPU: 1 UID: 0 PID: 31 Comm: ksoftirqd/1 Not tainted syzkaller #0 PREEMPT_{RT,(full)} Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/18/2026 Call Trace: <TASK> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 lbmIODone+0x1312/0x16c0 fs/jfs/jfs_logmgr.c:2192 blk_update_request+0x57e/0xe60 block/blk-mq.c:1016 blk_mq_end_request+0x3e/0x70 block/blk-mq.c:1178 blk_complete_reqs block/blk-mq.c:1253 [inline] blk_done_softirq+0x10a/0x160 block/blk-mq.c:1258 handle_softirqs+0x1de/0x6d0 kernel/softirq.c:622 run_ksoftirqd+0x52/0x180 kernel/softirq.c:1076 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK> Allocated by task 6248: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x3a6/0x690 mm/slub.c:5415 kmalloc_noprof include/linux/slab.h:950 [inline] lbmLogInit fs/jfs/jfs_logmgr.c:1819 [inline] lmLogInit+0x3e5/0x1a00 fs/jfs/jfs_logmgr.c:1267 open_inline_log fs/jfs/jfs_logmgr.c:1173 [inline] lmLogOpen+0x4e1/0xfa0 fs/jfs/jfs_logmgr.c:1067 jfs_mount_rw+0xee/0x670 fs/jfs/jfs_mount.c:257 jfs_fill_super+0x754/0xd80 fs/jfs/super.c:532 get_tree_bdev_flags+0x431/0x4f0 fs/super.c:1694 vfs_get_tree+0x92/0x2a0 fs/super.c:1754 fc_mount fs/namespace.c:1193 [inline] do_new_mount_fc fs/namespace.c:3758 [inline] do_new_mount+0x341/0xd30 fs/namespace.c:3834 do_mount fs/namespace.c:4167 [inline] __do_sys_mount fs/namespace.c:4399 [inline] __se_sys_mount+0x31d/0x420 fs/namespace.c:4376 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 5959: kasan_save_stack mm/kasan/common.c:57 [inline] kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2689 [inline] slab_free mm/slub.c:6246 [inline] kfree+0x1c5/0x6c0 mm/slub.c:6561 lbmLogShutdown fs/jfs/jfs_logmgr.c:1862 [inline] lmLogShutdown+0x456/0x850 fs/jfs/jfs_logmgr.c:1681 lmLogClose+0x28a/0x520 fs/jfs/jfs_logmgr.c:1457 jfs_umount+0x2fb/0x3d0 fs/jfs/jfs_umount.c:124 jfs_put_super+0x8c/0x190 fs/jfs/super.c:194 generic_shutdown_super+0x13d/0x2d0 fs/super.c:646 kill_block_super+0x44/0x90 fs/super.c:1725 deactivate_locked_super+0xbc/0x130 fs/super.c:476 cleanup_mnt+0x437/0x4d0 fs/namespace.c:1312 task_work_run+0x1d9/0x270 kernel/task_work.c:233 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] __exit_to_user_mode_loop kernel/entry/common.c:67 [inline] exit_to_user_mode_loop+0xed/0x480 kernel/entry/common.c:98 __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline] syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:238 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:328 [inline] do_syscall_64+0x33e/0xf80 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff888032de1200 which belongs to the cache kmalloc-256 of size 256 The buggy address is located 8 bytes inside of freed 256-byte region [ffff888032de1200, ffff888032de1300) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x32de0 head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x80000000000040(head|node=0|zone=1) page_type: f5(slab) raw: 0080000000000040 ffff88801a02cb40 dead000000000100 dead000000000122 raw: 0000000000000000 0000000800100010 00000000f5000000 0000000000000000 head: 0080000000000040 ffff88801a02cb40 dead000000000100 dead000000000122 head: 0000000000000000 0000000800100010 00000000f5000000 0000000000000000 head: 0080000000000001 ffffffffffffff81 00000000ffffffff 00000000ffffffff head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000002 page dumped because: kasan: bad access detected page_owner tracks the page as allocated page last allocated via order 1, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5948, tgid 5948 (syz-executor), ts 136932809553, free_ts 131716582127 set_page_owner include/linux/page_owner.h:32 [inline] post_alloc_hook+0x231/0x280 mm/page_alloc.c:1858 prep_new_page mm/page_alloc.c:1866 [inline] get_page_from_freelist+0x27d6/0x2850 mm/page_alloc.c:3946 __alloc_frozen_pages_noprof+0x18d/0x380 mm/page_alloc.c:5226 alloc_slab_page mm/slub.c:3278 [inline] allocate_slab+0x77/0x660 mm/slub.c:3467 new_slab mm/slub.c:3525 [inline] refill_objects+0x33c/0x3d0 mm/slub.c:7251 refill_sheaf mm/slub.c:2816 [inline] __pcs_replace_empty_main+0x373/0x720 mm/slub.c:4651 alloc_from_pcs mm/slub.c:4749 [inline] slab_alloc_node mm/slub.c:4883 [inline] __do_kmalloc_node mm/slub.c:5294 [inline] __kmalloc_noprof+0x530/0x7b0 mm/slub.c:5307 kmalloc_noprof include/linux/slab.h:954 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] fib_create_info+0x1723/0x31f0 net/ipv4/fib_semantics.c:1400 fib_table_insert+0xc8/0x1b50 net/ipv4/fib_trie.c:1212 fib_magic+0x434/0x510 net/ipv4/fib_frontend.c:1134 fib_add_ifaddr+0x38d/0x5f0 net/ipv4/fib_frontend.c:1171 fib_netdev_event+0x382/0x490 net/ipv4/fib_frontend.c:1516 notifier_call_chain+0x1ad/0x3d0 kernel/notifier.c:85 call_netdevice_notifiers_extack net/core/dev.c:2287 [inline] call_netdevice_notifiers net/core/dev.c:2301 [inline] __dev_notify_flags+0x1a9/0x310 net/core/dev.c:9849 netif_change_flags+0xe8/0x1a0 net/core/dev.c:9878 do_setlink+0xf82/0x4590 net/core/rtnetlink.c:3180 page last free pid 5958 tgid 5958 stack trace: reset_page_owner include/linux/page_owner.h:25 [inline] __free_pages_prepare mm/page_alloc.c:1402 [inline] __free_frozen_pages+0xf9b/0x10f0 mm/page_alloc.c:2943 __slab_free+0x252/0x2a0 mm/slub.c:5608 qlink_free mm/kasan/quarantine.c:163 [inline] qlist_free_all+0x99/0x100 mm/kasan/quarantine.c:179 kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286 __kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350 kasan_slab_alloc include/linux/kasan.h:253 [inline] slab_post_alloc_hook mm/slub.c:4569 [inline] slab_alloc_node mm/slub.c:4898 [inline] kmem_cache_alloc_node_noprof+0x22a/0x6e0 mm/slub.c:4950 __alloc_skb+0x1d0/0x7d0 net/core/skbuff.c:702 alloc_skb include/linux/skbuff.h:1383 [inline] nlmsg_new include/net/netlink.h:1055 [inline] netlink_ack+0x146/0xa50 net/netlink/af_netlink.c:2487 netlink_rcv_skb+0x2b6/0x4b0 net/netlink/af_netlink.c:2556 netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline] netlink_unicast+0x780/0x920 net/netlink/af_netlink.c:1344 netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1894 sock_sendmsg_nosec net/socket.c:787 [inline] __sock_sendmsg net/socket.c:802 [inline] __sys_sendto+0x67f/0x710 net/socket.c:2265 __do_sys_sendto net/socket.c:2272 [inline] __se_sys_sendto net/socket.c:2268 [inline] __x64_sys_sendto+0xde/0x100 net/socket.c:2268 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Memory state around the buggy address: ffff888032de1100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888032de1180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff888032de1200: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff888032de1280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff888032de1300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== --- If you want syzbot to run the reproducer, reply with: #syz test: git://repo/address.git branch-or-commit-hash If you attach or paste a git patch, syzbot will apply it before testing. |
|
From: 许东洁 <xud...@ma...> - 2026-04-15 04:11:25
|
Hi, While running fuzzing tests on 6.19.0-rc5, we encountered a slab-use-after-free bug in the JFS filesystem, triggered within the jfsCommit background thread. The crash is actually a chain reaction. According to the trace, the jfsCommit thread executes txLazyCommit and eventually reaches xtTruncate. When calling release_metapage() and unlock_metapage(), it hits a "spinlock bad magic" BUG because the wait queue state is corrupted. When the kernel attempts to print the corrupted lock info via spin_dump() -> printk(), it triggers a KASAN slab-use-after-free read. The KASAN report reveals the root cause: the memory being accessed belongs to a jfs_ip (JFS inode) cache object that has already been freed via RCU (i_callback). It appears there is a race condition between the asynchronous jfsCommit thread processing delayed transactions and the destruction of the inode (e.g., during a concurrent umount via evict_inodes). The background thread retains and uses an inode pointer that lacks a valid reference count. A possible fix would be to ensure that transactions added to the lazy commit list hold a robust reference (e.g., via igrab()) to the inode to prevent it from being freed prematurely. Alternatively, JFS could ensure that all pending lazy commits are properly flushed and drained before inodes are forcefully evicted during unmount. We currently do not have a patch ready, but we are happy to help test any proposed fixes. Crash log, call trace, and machine info are as follows: [Machine Info] QEMU emulator version 6.2.0 CPU: Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz (4 cores) Kernel Version: 6.19.0-rc5-00042-g944aacb68baf [Crash Report & Call Trace] BUG: spinlock bad magic on CPU#1, jfsCommit/143 BUG: KASAN: slab-use-after-free in string_nocheck lib/vsprintf.c:654 [inline] BUG: KASAN: slab-use-after-free in string+0x4a9/0x4f0 lib/vsprintf.c:736 Read of size 1 at addr ffff88812564b780 by task jfsCommit/143 CPU: 1 UID: 0 PID: 143 Comm: jfsCommit Tainted: G L 6.19.0-rc5-00042-g944aacb68baf #1 PREEMPT(full) Tainted: [L]=SOFTLOCKUP Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1b0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xca/0x5f0 mm/kasan/report.c:482 kasan_report+0xca/0x100 mm/kasan/report.c:595 string_nocheck lib/vsprintf.c:654 [inline] string+0x4a9/0x4f0 lib/vsprintf.c:736 vsnprintf+0x624/0x11a0 lib/vsprintf.c:2947 vprintk_store+0x340/0xb60 kernel/printk/printk.c:2255 vprintk_emit+0x12f/0x720 kernel/printk/printk.c:2402 _printk+0xbe/0xf0 kernel/printk/printk.c:2451 spin_dump kernel/locking/spinlock_debug.c:64 [inline] spin_bug+0x156/0x1d0 kernel/locking/spinlock_debug.c:78 debug_spin_lock_before kernel/locking/spinlock_debug.c:86 [inline] do_raw_spin_lock+0x222/0x2b0 kernel/locking/spinlock_debug.c:115 __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:111 [inline] _raw_spin_lock_irqsave+0x45/0x60 kernel/locking/spinlock.c:162 __wake_up_common_lock kernel/sched/wait.c:124 [inline] __wake_up+0x1c/0x60 kernel/sched/wait.c:146 unlock_metapage fs/jfs/jfs_metapage.c:40 [inline] release_metapage+0x105/0x850 fs/jfs/jfs_metapage.c:871 discard_metapage fs/jfs/jfs_metapage.h:88 [inline] xtTruncate+0x2099/0x28c0 fs/jfs/jfs_xtree.c:2502 jfs_free_zero_link+0x357/0x4b0 fs/jfs/namei.c:759 jfs_evict_inode+0x421/0x4b0 fs/jfs/inode.c:159 evict+0x3a1/0xa80 fs/inode.c:837 iput_final fs/inode.c:1954 [inline] iput fs/inode.c:2006 [inline] iput+0x7a8/0xf20 fs/inode.c:1966 txUpdateMap+0x97a/0xb50 fs/jfs/jfs_txnmgr.c:2368 txLazyCommit fs/jfs/jfs_txnmgr.c:2665 [inline] jfs_lazycommit+0x5d8/0xb20 fs/jfs/jfs_txnmgr.c:2734 kthread+0x446/0x890 kernel/kthread.c:463 ret_from_fork+0x95b/0xae0 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 </TASK> Freed by task 28: kasan_save_stack+0x24/0x50 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x61/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2540 [inline] slab_free mm/slub.c:6670 [inline] kmem_cache_free+0x15f/0x780 mm/slub.c:6781 i_callback+0x46/0x70 fs/inode.c:325 rcu_do_batch kernel/rcu/tree.c:2605 [inline] rcu_core+0x7b1/0x1790 kernel/rcu/tree.c:2857 handle_softirqs+0x1d4/0x8d0 kernel/softirq.c:622 run_ksoftirqd kernel/softirq.c:1063 [inline] run_ksoftirqd+0x3a/0x60 kernel/softirq.c:1055 smpboot_thread_fn+0x3d4/0xaa0 kernel/smpboot.c:160 kthread+0x446/0x890 kernel/kthread.c:463 ret_from_fork+0x95b/0xae0 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 Xu Dongjie University of Chinese Academy of Sciences |