[BUG] jfs: BUG_ON in jfs_evict_inode fires in practice (anon_inode_list assertion)
From: yangguikun
Date: Tue Oct 13 2026 - 14:58:10 EST
Hi Dave,
the BUG_ON in jfs_evict_inode() fires in practice, and it still does on current
mainline: our fuzzing fleet hit it on 7.2-rc5 on 2026-10-12, and fs/jfs is
unchanged between that tree and current mainline, so the same code is still
there. It is easy to hit - a corrupted image is enough, and the crash is a
panic, not a graceful error.
The assertion that fires is
BUG_ON(!list_empty(&ji->anon_inode_list)); /* fs/jfs/inode.c:175 */
We think the interesting part is that this assertion contradicts how the rest
of the file treats that same list:
- txQuiesce() (jfs_txnmgr.c:2827) walks TxAnchor.anon_list and dereferences
the inode directly - ip = &jfs_ip->vfs_inode; txBegin(ip->i_sb, ...) -
without igrab(), i.e. it assumes every inode on the list is alive;
- jfs_sync() (jfs_txnmgr.c:2895) walks the same list, does igrab(ip), and
when igrab() fails it does list_del_init(&jfs_ip->anon_inode_list) under
the comment "Inode is being freed" - so the code explicitly expects that a
dying inode can be on this list.
That is, the list can hold an inode that is being evicted, and the BUG_ON is
the side that fires in practice. Making the two consistent would mean dropping
the inode from anon_inode_list before it is freed - the same thing jfs_sync()
already does in its igrab() failure branch - which also keeps txQuiesce() safe,
because it would then never see a dead inode.
We have not attempted a fix. Happy to test a candidate patch against our
reproducer on our side if that helps.
Framing: the trigger is a crafted/corrupted image, so this is a panic/DoS on
such an image; we are not claiming a privilege-boundary crossing.
Thanks,
yangguikun <yangguikun19@xxxxxx>