* Forwarded: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
2025-12-06 8:38 [syzbot] [jfs?] kernel BUG in dtSplitRoot syzbot
@ 2025-12-07 4:57 ` syzbot
2025-12-07 5:21 ` syzbot
2025-12-07 6:05 ` syzbot
2 siblings, 0 replies; 4+ messages in thread
From: syzbot @ 2025-12-07 4:57 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
When inserting directory entries with long filenames that trigger tree
splits, the kernel crashes with "BUG at fs/jfs/jfs_dtree.c:1942" on the
assertion ASSERT(dtlck->index == 0).
The issue occurs due to improper lock reuse in txLock(). When a
transaction lock is reused (either from the same transaction or
transferred from an anonymous transaction), the dtlck->index field is
not reset, retaining its stale value from previous operations.
dtSplitRoot() expects dtlck->index to be 0 when creating a new root
node, as it needs to log directory entries starting from position 0. A
non-zero index causes entries to be written at incorrect positions,
corrupting the new root node's structure.
Lock allocation paths in txLock():
1. allocateLock path: Correctly initializes linelock->index = 0
2. Lock reuse paths: Jump to grantLock, skipping initialization
This leaves reused locks with stale index values (e.g., index=3 from
a previous operation where 3 entries were logged), causing the
assertion to fail.
Example sequence showing the bug:
- First file creation: fresh lock, index=0 -> success
- Second file creation: reused lock, index=3 -> assertion fails
Fix by unconditionally resetting dtlck->index to 0 in the grantLock
path for all DTREE lock types. This ensures clean state regardless of
whether the lock is freshly allocated or reused, preventing the stale
index from corrupting directory tree operations.
Reproducer triggers the issue with a corrupted JFS filesystem image
followed by multiple file creations with long names (~250 characters)
that force directory tree splits.
Reported-by: syzbot+a099d674daa27a9272db@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=a099d674daa27a9272db
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
fs/jfs/jfs_txnmgr.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index c16578af3a77..2c0678b03f66 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -811,6 +811,11 @@ struct tlock *txLock(tid_t tid, struct inode *ip, struct metapage * mp,
* update tlock vector
*/
grantLock:
+ if (type & tlckDTREE) {
+ struct dt_lock *dtlck = (struct dt_lock *) &tlck->lock;
+
+ dtlck->index = 0;
+ }
tlck->type |= type;
return tlck;
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* Forwarded: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
2025-12-06 8:38 [syzbot] [jfs?] kernel BUG in dtSplitRoot syzbot
2025-12-07 4:57 ` Forwarded: [PATCH] jfs: fix directory tree corruption in dtSplitRoot() syzbot
@ 2025-12-07 5:21 ` syzbot
2025-12-07 6:05 ` syzbot
2 siblings, 0 replies; 4+ messages in thread
From: syzbot @ 2025-12-07 5:21 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
When inserting directory entries with long filenames that trigger tree
splits, the kernel crashes with "BUG at fs/jfs/jfs_dtree.c:1942" on the
assertion ASSERT(dtlck->index == 0).
The issue occurs due to improper lock reuse in txLock(). When a
transaction lock is reused (either from the same transaction or
transferred from an anonymous transaction), the dtlck->index field is
not reset, retaining its stale value from previous operations.
dtSplitRoot() expects dtlck->index to be 0 when creating a new root
node, as it needs to log directory entries starting from position 0. A
non-zero index causes entries to be written at incorrect positions,
corrupting the new root node's structure.
Lock allocation paths in txLock():
1. allocateLock path: Correctly initializes linelock->index = 0
2. Lock reuse paths: Jump to grantLock, skipping initialization
This leaves reused locks with stale index values (e.g., index=3 from
a previous operation where 3 entries were logged), causing the
assertion to fail.
Example sequence showing the bug:
- First file creation: fresh lock, index=0 -> success
- Second file creation: reused lock, index=3 -> assertion fails
Fix by unconditionally resetting dtlck->index to 0 in the grantLock
path for all DTREE lock types. This ensures clean state regardless of
whether the lock is freshly allocated or reused, preventing the stale
index from corrupting directory tree operations.
Reproducer triggers the issue with a corrupted JFS filesystem image
followed by multiple file creations with long names (~250 characters)
that force directory tree splits.
Reported-by: syzbot+a099d674daa27a9272db@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=a099d674daa27a9272db
Tested-by: Deepanshu Kartikey <kartikey406@gmail.com>
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
fs/jfs/jfs_txnmgr.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index c16578af3a77..0bb8cf6eb1c0 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -811,6 +811,10 @@ 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;
+ dtlck->index = 0;
+ }
tlck->type |= type;
return tlck;
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* Forwarded: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
2025-12-06 8:38 [syzbot] [jfs?] kernel BUG in dtSplitRoot syzbot
2025-12-07 4:57 ` Forwarded: [PATCH] jfs: fix directory tree corruption in dtSplitRoot() syzbot
2025-12-07 5:21 ` syzbot
@ 2025-12-07 6:05 ` syzbot
2 siblings, 0 replies; 4+ messages in thread
From: syzbot @ 2025-12-07 6:05 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] jfs: fix directory tree corruption in dtSplitRoot()
Author: kartikey406@gmail.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@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=a099d674daa27a9272db
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.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
^ permalink raw reply [flat|nested] 4+ messages in thread