[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <69351933.a70a0220.38f243.0047.GAE@google.com>
Date: Sat, 06 Dec 2025 22:05:39 -0800
From: syzbot <syzbot+a099d674daa27a9272db@...kaller.appspotmail.com>
To: linux-kernel@...r.kernel.org, syzkaller-bugs@...glegroups.com
Subject: Forwarded: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
For archival purposes, forwarding an incoming command email to
linux-kernel@...r.kernel.org, syzkaller-bugs@...glegroups.com.
***
Subject: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
Author: kartikey406@...il.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
When reusing transaction locks for DTREE operations, the index field
may contain stale values from previous operations, causing assertion
failures in dtSplitRoot():
ASSERT(dtlck->index == 0)
This results in kernel crashes like:
kernel BUG at fs/jfs/jfs_dtree.c:1942!
Call Trace:
dtSplitRoot+0x1694/0x16c0 fs/jfs/jfs_dtree.c:1942
dtSplitUp fs/jfs/jfs_dtree.c:1244 [inline]
dtInsert+0x2525/0x5f40 fs/jfs/jfs_dtree.c:871
jfs_create+0x6c8/0xa80 fs/jfs/namei.c:137
The bug occurs because txLock() has multiple code paths for lock
acquisition:
1. Fresh allocation (allocateLock) - correctly initializes index to 0
2. Lock reuse (same transaction) - skips initialization
3. Anonymous lock acquisition - skips initialization
Paths 2 and 3 jump directly to the grantLock label, bypassing the
index initialization. When dtSplitRoot() is called multiple times
within a batched transaction (which JFS uses for performance), it may
receive a reused lock with index=3 from a previous operation instead
of the expected index=0.
Example sequence:
Transaction tid=1:
- First dtSplitRoot: gets fresh lock, index=0 ✓
- Modifies entries, index becomes 3
- Lock returned to pool but not freed
Transaction tid=1 (continues):
- Second dtSplitRoot: reuses same lock
- index still = 3 (stale value) ✗
- ASSERT(index == 0) fails → crash
Fix by resetting dtlck->index to 0 at the grantLock label, but only
for operations with the tlckNEW flag set. This ensures:
- New pages (like dtSplitRoot) start with clean state (index=0)
- Existing pages preserve accumulated changes within a transaction
- No performance impact (only affects new page operations)
The tlckNEW flag is used by dtSplitRoot() when creating a new root
page, making this fix targeted to the exact scenario that requires
index=0.
Reported-by: syzbot+a099d674daa27a9272db@...kaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=a099d674daa27a9272db
Signed-off-by: Deepanshu Kartikey <kartikey406@...il.com>
---
fs/jfs/jfs_dtree.c | 8 +++++++-
fs/jfs/jfs_txnmgr.c | 17 +++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
index 0ab83bb7bbdf..6e5b4431f287 100644
--- a/fs/jfs/jfs_dtree.c
+++ b/fs/jfs/jfs_dtree.c
@@ -1926,7 +1926,13 @@ static int dtSplitRoot(tid_t tid,
*/
tlck = txLock(tid, ip, rmp, tlckDTREE | tlckNEW);
dtlck = (struct dt_lock *) & tlck->lock;
-
+ printk(KERN_ERR "JFS_DEBUG: dtSplitRoot before assertion\n");
+ printk(KERN_ERR " tid=%d, ip=%p, rmp=%p\n", tid, ip, rmp);
+ printk(KERN_ERR " tlck=%p, tlck->tid=%d, tlck->type=0x%x\n", tlck, tlck->tid, tlck->type);
+ printk(KERN_ERR " dtlck=%p, dtlck->index=%d\n", dtlck, dtlck->index);
+ if (dtlck->index != 0) {
+ printk(KERN_ERR " ERROR: index is %d, expected 0!\n", dtlck->index);
+ }
rp->header.flag =
(sp->header.flag & BT_LEAF) ? BT_LEAF : BT_INTERNAL;
rp->header.self = *pxd;
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index c16578af3a77..bb2fb9bc3440 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -811,6 +811,23 @@ struct tlock *txLock(tid_t tid, struct inode *ip, struct metapage * mp,
* update tlock vector
*/
grantLock:
+ if ((type & tlckDTREE) && (type & tlckNEW)) {
+ struct dt_lock *dtlck = (struct dt_lock *)&tlck->lock;
+ struct linelock *linelock = (struct linelock *)&tlck->lock;
+ printk(KERN_ERR "JFS_DEBUG: txLock grantLock (DTREE)\n");
+ printk(KERN_ERR " tid=%d, ip=%p, mp=%p\n", tid, ip, mp);
+ printk(KERN_ERR " type=0x%x (DTREE=%d, NEW=%d)\n", type, !!(type & tlckDTREE), !!(type & tlckNEW));
+ printk(KERN_ERR " tlck=%p, tlck->tid=%d\n", tlck, tlck->tid);
+ printk(KERN_ERR " BEFORE: linelock->index=%d\n", linelock->index);
+ printk(KERN_ERR " BEFORE: linelock->next=%d, flag=%d\n",linelock->next, linelock->flag);
+ if (type & tlckNEW) {
+ printk(KERN_ERR " ACTION: Resetting index to 0 (tlckNEW)\n");
+ dtlck->index = 0;
+ } else {
+ printk(KERN_ERR " ACTION: NOT resetting (no tlckNEW)\n");
+ }
+ printk(KERN_ERR " AFTER: linelock->index=%d\n", linelock->index);
+ }
tlck->type |= type;
return tlck;
--
2.43.0
Powered by blists - more mailing lists