CVE-2025-68168: Add .vulnerable file
Root Cause Commit: 95e2b352c03b0a86c5717ba1d24ea20969abcacc
Title: "FS: JFS: Check for read-only mounted filesystem in txBegin"
Author: Immad Mir
Date: June 23, 2023
Technical Details:
The Fix Commit (300b072df72694ea330c4c673c035253e07827b8)
The fix modifies txInit() in fs/jfs/jfs_txnmgr.c to ensure all
TxBlock[] entries have their waitqueues initialized, including
TxBlock[0]:
Before:
for (k = 1; k < nTxBlock - 1; k++) {
TxBlock[k].next = k + 1;
init_waitqueue_head(&TxBlock[k].gcwait);
init_waitqueue_head(&TxBlock[k].waitor);
}
TxBlock[k].next = 0;
init_waitqueue_head(&TxBlock[k].gcwait);
init_waitqueue_head(&TxBlock[k].waitor);
After:
for (k = 0; k < nTxBlock; k++) {
init_waitqueue_head(&TxBlock[k].gcwait);
init_waitqueue_head(&TxBlock[k].waitor);
}
for (k = 1; k < nTxBlock - 1; k++) {
TxBlock[k].next = k + 1;
}
The Bug Chain:
1. Original Design (since Linux 2.6.12-rc2, commit 1da177e4c3f4):
- TxBlock[0] was intentionally not initialized because tid=0 was
"reserved" and never used
- The code comment at fs/jfs/jfs_txnmgr.c:289 states: "tid = 0 is
reserved."
- txBegin() would NEVER return 0 - it would block and retry via
TXN_SLEEP() if no tids were available
- TxAnchor.freetid was initialized to 1, not 0
2. Root Cause (commit 95e2b352c03b, June 2023):
This commit added a read-only filesystem check at the beginning of
txBegin():
if (!log) {
jfs_error(sb, "read-only filesystem\n");
return 0; // <-- BUG INTRODUCED HERE
}
This creates a NEW code path where txBegin() returns tid=0.
3. Exploitation Path:
- When a JFS filesystem is mounted read-only, JFS_SBI(sb)->log is
NULL
- txBegin() now returns tid=0
- Callers like jfs_write_inode() don't check for tid=0 and proceed to
call txEnd(tid)
- txEnd(0) calls tid_to_tblock(0) which expands to &TxBlock[0]
(defined
in fs/jfs/jfs_txnmgr.h:13)
- txEnd() then calls TXN_WAKEUP(&tblk->waitor) at line 504
- TxBlock[0].waitor was NEVER initialized, causing a lockdep warning
and crash:
INFO: trying to register non-static key in txEnd
Why Commit 95e2b352c03b is the Root Cause:
Before this commit:
- txBegin() would always return a valid tid >= 1 or block waiting
- The loop "if ((t = TxAnchor.freetid) == 0) { TXN_SLEEP(...); goto
retry; }" ensured no 0 return
- TxBlock[0] not being initialized was irrelevant since it was never
accessed
After this commit:
- txBegin() can return 0 immediately on read-only filesystems
- Callers proceed to use this tid=0, eventually calling txEnd(0)
- txEnd(0) accesses the uninitialized TxBlock[0].waitor, triggering the
crash
The commit intended to fix a NULL pointer dereference on log access,
but inadvertently broke the invariant that tid=0 is never used, exposing
the latent uninitialized memory in TxBlock[0].
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 file changed