Root Cause Analysis
1. Crash Site and Mechanism
The crash occurs in jfs_statfs() (fs/jfs/super.c:140):
static int jfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct jfs_sb_info *sbi = JFS_SBI(dentry->d_sb);
s64 maxinodes;
struct inomap *imap = JFS_IP(sbi->ipimap)->i_imap;
...
maxinodes = min((s64) atomic_read(&imap->im_numinos) +
((sbi->bmap->db_nfree >> imap->im_l2nbperiext)
<< L2INOSPEREXT), (s64) 0xffffffffLL);
In struct inomap, the field im_numinos is located at byte offset 20672 (0x50c0). When imap is NULL, atomic_read(&imap->im_numinos) reads from memory address 0x00000000000050c0. Because address 0x50c0 falls within the user-space address range (below PAGE_OFFSET), KASAN classifies this access as user-memory-access. On systems without KASAN, this triggers an unhandled kernel page fault (NULL pointer dereference with struct offset 0x50c0).
2. Root Cause in Remount Path
The condition JFS_IP(sbi->ipimap)->i_imap == NULL is produced when a JFS filesystem is remounted from read-only to read-write (or when JFS_NOINTEGRITY is modified) and the remount operation fails.
In jfs_remount() (fs/jfs/super.c:446-461):
if (sb_rdonly(sb) && !(*flags & SB_RDONLY)) {
truncate_inode_pages(JFS_SBI(sb)->direct_inode->i_mapping, 0);
JFS_SBI(sb)->flag = flag;
ret = jfs_mount_rw(sb, 1);
/* mark the fs r/w for quota activity */
sb->s_flags &= ~SB_RDONLY;
dquot_resume(sb, -1);
return ret;
}
jfs_remount() delegates the read-write transition to jfs_mount_rw(sb, 1) (fs/jfs/jfs_mount.c:221):
if (remount) {
if (chkSuper(sb) || (sbi->state != FM_CLEAN))
return -EINVAL;
truncate_inode_pages(sbi->ipimap->i_mapping, 0);
truncate_inode_pages(sbi->ipbmap->i_mapping, 0);
IWRITE_LOCK(sbi->ipimap, RDWRLOCK_IMAP);
diUnmount(sbi->ipimap, 1);
if ((rc = diMount(sbi->ipimap))) {
IWRITE_UNLOCK(sbi->ipimap);
jfs_err("jfs_mount_rw: diMount failed!");
return rc;
}
IWRITE_UNLOCK(sbi->ipimap);
dbUnmount(sbi->ipbmap, 1);
if ((rc = dbMount(sbi->ipbmap))) {
jfs_err("jfs_mount_rw: dbMount failed!");
return rc;
}
}
In diUnmount(sbi->ipimap, 1) (fs/jfs/jfs_imap.c:176-197):
kfree(imap);
JFS_IP(ipimap)->i_imap = NULL;
diUnmount() frees the existing struct inomap and clears JFS_IP(ipimap)->i_imap. Next, diMount(sbi->ipimap) attempts to allocate a new struct inomap and read the on-disk inode map control structure via read_metapage().
If diMount() fails (e.g., if reading the metapage fails with -EIO due to backing device errors/disconnection, or kmalloc fails with -ENOMEM):
diMount() returns an error without setting JFS_IP(ipimap)->i_imap.
jfs_mount_rw() returns rc immediately.
jfs_remount() returns ret (and inappropriately clears SB_RDONLY even on failure).
- In VFS,
reconfigure_super() (fs/super.c:1073) detects that reconfigure returned an error and aborts the remount. The superblock is not unmounted and remains active in the VFS mount table.
A parallel issue exists for sbi->bmap: if dbMount(sbi->ipbmap) fails after dbUnmount(sbi->ipbmap, 1), sbi->bmap is left as NULL.
3. Failure Manifestation
Following the failed remount, the filesystem remains mounted, but JFS_IP(sbi->ipimap)->i_imap is left as NULL. Any subsequent call to statfs() invokes jfs_statfs(), which attempts to read atomic_read(&imap->im_numinos) without checking whether imap is non-NULL, immediately triggering a NULL pointer dereference at offset 0x50c0.