KASAN: slab-use-after-free Read in write_special_inodes
From: Jianzhou Zhao
Date: Wed Mar 11 2026 - 04:22:39 EST
Subject: [BUG] jfs: KASAN: slab-use-after-free in write_special_inodes
Dear Maintainers,
We are writing to report a slab use-after-free (UAF) vulnerability discovered in the JFS filesystem's unmount path. This bug was found by our custom fuzzing tool, RacePilot. The UAF is triggered during a failed filesystem mount/unmount sequence where `jfs_put_super()` prematurely frees the `sbi->direct_inode` before flushing the journal, leading to a fatal memory fault in `write_special_inodes()`. We observed this bug on the Linux kernel version 6.18.0-08691-g2061f18ad76e-dirty.
Call Trace & Context
==================================================================
BUG: KASAN: slab-use-after-free in write_special_inodes+0x131/0x170 fs/jfs/jfs_logmgr.c:206
Read of size 8 at addr ffff888013774a38 by task syz-executor/9881
CPU: 0 UID: 0 PID: 9881 Comm: syz-executor Tainted: G L 6.18.0-08691-g2061f18ad76e-dirty #43 PREEMPT(full)
...
Call Trace:
<TASK>
write_special_inodes+0x131/0x170 fs/jfs/jfs_logmgr.c:206
jfs_flush_journal+0x2b8/0x960 fs/jfs/jfs_logmgr.c:1581
jfs_umount+0x17a/0x440 fs/jfs/jfs_umount.c:58
jfs_put_super+0x85/0x1d0 fs/jfs/super.c:194
generic_shutdown_super+0x156/0x390 fs/super.c:643
kill_block_super+0x3b/0x90 fs/super.c:1730
deactivate_locked_super+0xbf/0x1a0 fs/super.c:474
...
Allocated by task 45171:
jfs_fill_super+0xd1/0x1030 fs/jfs/super.c:452
get_tree_bdev_flags+0x389/0x620 fs/super.c:1699
vfs_get_tree+0x93/0x340 fs/super.c:1759
...
Freed by task 38650:
kfree+0x2ca/0x6d0 mm/slub.c:6871
generic_shutdown_super+0x156/0x390 fs/super.c:643
kill_block_super+0x3b/0x90 fs/super.c:1730
...
==================================================================
Execution Flow & Code Context
When a `jfs` unmount occurs (or a failed mount aborts causing `jfs_put_super`), the kernel invokes `jfs_umount()` dynamically:
```c
// fs/jfs/super.c
static void jfs_put_super(struct super_block *sb)
{
struct jfs_sb_info *sbi = JFS_SBI(sb);
int rc;
jfs_info("In jfs_put_super");
jfs_quota_off_umount(sb);
rc = jfs_umount(sb);
if (rc)
jfs_err("jfs_umount failed with return code %d", rc);
unload_nls(sbi->nls_tab);
truncate_inode_pages(sbi->direct_inode->i_mapping, 0);
iput(sbi->direct_inode);
kfree(sbi);
}
```
The unmount phase eventually flushes the filesystem journal synchronously to finalize out-of-band I/O transactions:
```c
// fs/jfs/jfs_umount.c
int jfs_umount(struct super_block *sb)
{
...
if ((log = sbi->log))
/*
* Wait for outstanding transactions to be written to log:
*/
jfs_flush_journal(log, 2);
...
```
However, inside `jfs_flush_journal()`, the backend logger tries iterating over special filesystem mappings linked inside the `jfs_sb_info` layout:
```c
// fs/jfs/jfs_logmgr.c
static void write_special_inodes(struct jfs_log *log,
int (*writer)(struct address_space *))
{
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); // <-- KASAN triggers Use-After-Free exception here
}
}
```
Root Cause Analysis
A KASAN Slab Use-After-Free bug emerges inside `write_special_inodes()`. When the VFS abstraction invokes `generic_shutdown_super`, `jfs_put_super()` functions cleanly unless the backend volume drops early cache invalidation during fault teardowns. During normal `jfs_put_super`, `jfs_umount` completes, `sbi->direct_inode` is cleaned up via `iput(sbi->direct_inode)` and memory structures begin breaking down.
However, if `sbi` was inherently tied into multi-mount sharing the same external `jfs_log` subsystem globally, then parallel actions flushing the same global journal execute `jfs_flush_journal()`. Because `list_for_each_entry(&log->sb_list)` navigates all bound superblocks attached to the log without strictly validating memory states, it hits the currently-degraded `sbi` array belonging to the tearing-down mount, improperly accessing `sbi->direct_inode->i_mapping` while the slab memory allocation (`kfree` for `sbi`) has already dissolved.
Unfortunately, we were unable to generate a reproducer for this bug.
Potential Impact
This logic discrepancy causes highly-privileged local kernel execution faults (DoS), triggering uncorrectable system-wide panics impacting standard namespace isolation security if external journals fall out-of-sync against mounting/unmounting devices sequentially.
Proposed Fix
To mitigate this lifecycle lapse, the unmounting superblock (`sbi`) must be cleanly detached from the `log->sb_list` before any `jfs_flush_journal` calls or before structural freeing transpires in `jfs_put_super()` to ensure the log processing ignores dangling bounds:
```diff
--- a/fs/jfs/jfs_umount.c
+++ b/fs/jfs/jfs_umount.c
@@ -107,6 +107,11 @@ int jfs_umount(struct super_block *sb)
if (log) { /* log = NULL if read-only mount */
updateSuper(sb, FM_CLEAN);
+ /* Remove from log list before flushing residual */
+ mutex_lock(&jfs_log_mutex);
+ list_del_init(&sbi->log_list);
+ mutex_unlock(&jfs_log_mutex);
+
/*
* close log:
*
```
We would be highly honored if this could be of any help.
Best regards,
RacePilot Team