From: sashiko-bot@kernel.org
To: "Christian Brauner" <brauner@kernel.org>, "Jan Kara" <jack@suse.com>
Cc: "Al Viro" <viro@zeniv.linux.org.uk>,
linux-ext4@vger.kernel.org, linux-mm@kvack.org,
linux-efi@vger.kernel.org, linux-mtd@lists.infradead.org,
dsterba@suse.cz, tytso@mit.edu, "Mark Fasheh" <mark@fasheh.com>,
"Joel Becker" <jlbec@evilplan.org>,
"Joseph Qi" <joseph.qi@linux.alibaba.com>
Subject: Re: [PATCH v3 01/33] jfs: free uncommitted anonymous tlocks when evicting an inode
Date: Sat, 14 Nov 2026 12:03:20 +0000 [thread overview]
Message-ID: <sashiko-outbox-214452@kernel.org> (raw)
In-Reply-To: <20261113-work-fs-sb_rdonly-v3-1-0be92bb73709@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] jfs: false WARN_ON_ONCE() in jfs_evict_inode() on read-write
mounts
- [Medium] jfs: txAbortAnon() after jfs_free_zero_link() leaks metapages
and tlocks
- [Medium] jfs: race between jfs_evict_inode(), txAbortAnon(), and
txQuiesce()
--
commit a15655dff32c48b09c331d3e03e198e2173008c1
Author: Christian Brauner <brauner@kernel.org>
jfs: free uncommitted anonymous tlocks when evicting an inode
This commit adds txAbortAnon() and calls it from jfs_evict_inode() to
release uncommitted anonymous transaction locks and metapage pins when an
inode is evicted before writeback commits them.
> diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
> index c7914dbc91ed9..6390d915c7f11 100644
> --- a/fs/jfs/inode.c
> +++ b/fs/jfs/inode.c
> @@ -172,6 +172,15 @@ void jfs_evict_inode(struct inode *inode)
> clear_inode(inode);
> dquot_drop(inode);
>
> + /*
> + * Writeback commits the anonymous transactions of allocations. If the
> + * filesystem went read-only without a sync, e.g. after an error,
> + * nothing did.
> + */
> + if (ji->atlhead) {
> + WARN_ON_ONCE(!sb_rdonly(inode->i_sb));
[Severity: Medium]
Can WARN_ON_ONCE(!sb_rdonly(inode->i_sb)) fire on a read-write mount when
ji->atlhead is left populated without setting COMMIT_Dirty?
In extAlloc(), xtExtend() and xtInsert() acquire anonymous tlocks on
non-root xtree metapages via txLock() before splitting pages in
xtSplitUp(). When xtSplitUp() fails with -ENOSPC or -EDQUOT, extAlloc()
frees the data blocks and returns before calling mark_inode_dirty(ip):
fs/jfs/jfs_extent.c:extAlloc() {
...
if (xaddr && xaddr == nxaddr)
rc = xtExtend(0, ip, xoff, (int) nxlen, 0);
else
rc = xtInsert(0, ip, xflag, xoff, (int) nxlen, &nxaddr, 0);
/* if the extend or insert failed,
* free the newly allocated blocks and return the error.
*/
if (rc) {
dbFree(ip, nxaddr, nxlen);
dquot_free_block(ip, nxlen);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
return (rc);
}
...
mark_inode_dirty(ip);
}
Because COMMIT_Dirty is never set on the inode, jfs_write_inode() does not
commit the anonymous tlocks, and evicting the inode triggers the warning on
a read-write filesystem. The warning also triggers after a filesystem
error when mounted with errors=continue, where jfs_handle_error() marks the
superblock dirty without setting SB_RDONLY, or via extRecord() ->
xtUpdate(), which is fixed in a later patch in this series (commit "jfs:
don't set SB_RDONLY after filesystem errors").
> + txAbortAnon(inode);
> + }
[Severity: Medium]
Does calling txAbortAnon() after jfs_free_zero_link() leak metapages and
linelocks when an unlinked inode with COMMIT_Freewmap is evicted after an
error?
Earlier in jfs_evict_inode(), an unlinked inode with COMMIT_Freewmap calls
jfs_free_zero_link() -> xtTruncate(..., COMMIT_WMAP). For each non-root
xtree metapage, xtTruncate() marks mp->lid with tlckFREELOCK, calls
discard_metapage(mp), and finishes with txFreelock(ip):
fs/jfs/jfs_txnmgr.c:txFreelock() {
...
while ((lid = xtlck->next) != 0) {
tlck = lid_to_tlock(lid);
if (tlck->flag & tlckFREELOCK) {
xtlck->next = tlck->next;
txLockFree(lid);
} else {
...
}
If the transaction in jfs_unlink() or jfs_rename() aborted after
commitZeroLink() set COMMIT_Freewmap, ji->atlhead still holds anonymous
tlckPAGELOCK tlocks when jfs_evict_inode() runs. txFreelock() unlinks
those tlocks from ji->atlhead and frees lid before txAbortAnon() runs,
without dropping mp->nohomeok, clearing mp->lid, or freeing chained
linelocks. Calling txAbortAnon() before jfs_free_zero_link() would release
the metapage pins and linelocks before xtTruncate() discards the pages.
> diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
> index 083dbbb0c3268..85192dd4461a4 100644
> --- a/fs/jfs/jfs_txnmgr.c
> +++ b/fs/jfs/jfs_txnmgr.c
> @@ -2581,6 +2581,74 @@ void txFreelock(struct inode *ip)
> TXN_UNLOCK();
> }
>
> +/*
> + * txAbortAnon()
> + *
> + * function: free the anonymous tlocks of an inode that is evicted
> + * before writeback committed them, e.g. because an error made
> + * the filesystem read-only.
> + *
> + * A page that carries nothing but uncommitted changes isn't written home.
> + * A page that is also waiting to go home with committed changes (mp->lsn)
> + * goes home with both, after an error the superblock is marked dirty.
> + */
> +void txAbortAnon(struct inode *ip)
> +{
> + struct jfs_inode_info *jfs_ip = JFS_IP(ip);
> + struct linelock *linelock;
> + struct metapage *mp;
> + struct tlock *tlck;
> + lid_t lid, next, llid, k;
> +
> + /* txQuiesce() and jfs_sync() commit under the commit_mutex */
> + mutex_lock(&jfs_ip->commit_mutex);
> +
> + TXN_LOCK();
> + lid = jfs_ip->atlhead;
> + jfs_ip->atlhead = jfs_ip->atltail = 0;
> + list_del_init(&jfs_ip->anon_inode_list);
> + TXN_UNLOCK();
[Severity: Medium]
Can jfs_evict_inode() and txAbortAnon() race with txQuiesce() walking the
global TxAnchor.anon_list?
txQuiesce() walks TxAnchor.anon_list (shared across all JFS mounts) under
TXN_LOCK() and drops TXN_LOCK() without pinning the inode before taking
commit_mutex:
CPU0 CPU1
txQuiesce() jfs_evict_inode()
TXN_LOCK() txAbortAnon()
jfs_ip = list_entry(...) mutex_lock(&jfs_ip->commit_mutex)
TXN_UNLOCK() TXN_LOCK()
list_del_init(&jfs_ip->anon_inode_list)
TXN_UNLOCK()
...
mutex_unlock(&jfs_ip->commit_mutex)
...
destroy_inode(ip)
txBegin(ip->i_sb, ...)
mutex_lock(&jfs_ip->commit_mutex) <-- use-after-free
In the opposite interleaving, txCommit() called from txQuiesce() clears
jfs_ip->atlhead = 0 before taking TXN_LOCK() to remove
jfs_ip->anon_inode_list. Because jfs_evict_inode() checks if (ji->atlhead)
without commit_mutex or TXN_LOCK(), it can see ji->atlhead == 0, skip
txAbortAnon(), and hit BUG_ON(!list_empty(&ji->anon_inode_list)) or free
the inode while txCommit() is still running.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-work-fs-sb_rdonly-v3-0-0be92bb73709@kernel.org?part=1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
next prev parent reply other threads:[~2026-11-14 12:03 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 11:58 [PATCH v3 00/33] fs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-13 11:58 ` [PATCH v3 01/33] jfs: free uncommitted anonymous tlocks when evicting an inode Christian Brauner
2026-11-14 12:03 ` sashiko-bot [this message]
2026-11-13 11:58 ` [PATCH v3 02/33] jfs: use jfs_symlink_inode_operations in jfs_iget() Christian Brauner
2026-11-13 11:58 ` [PATCH v3 03/33] ecryptfs: don't leak the inode when a lookup can't open the lower file Christian Brauner
2026-11-13 11:59 ` [PATCH v3 04/33] ecryptfs: fall back to a read-only " Christian Brauner
2026-11-13 11:59 ` [PATCH v3 05/33] fs: add a generic emergency read-only state Christian Brauner
2026-11-13 14:53 ` Jan Kara
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 06/33] ext2: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-13 14:54 ` Jan Kara
2026-11-13 11:59 ` [PATCH v3 07/33] jfs: " Christian Brauner
2026-11-13 11:59 ` [PATCH v3 08/33] nilfs2: " Christian Brauner
2026-11-13 11:59 ` [PATCH v3 09/33] fat: " Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 10/33] exfat: keep the current mount options on remount Christian Brauner
2026-11-13 11:59 ` [PATCH v3 11/33] exfat: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 12/33] exfat: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 13/33] hfs: don't set SB_RDONLY after MDB write errors Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 14/33] hfs: use the generic emergency read-only state Christian Brauner
2026-11-13 11:59 ` [PATCH v3 15/33] hfsplus: don't set SB_RDONLY when creating the attributes tree Christian Brauner
2026-11-13 11:59 ` [PATCH v3 16/33] affs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-13 11:59 ` [PATCH v3 17/33] ufs: " Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 18/33] hpfs: " Christian Brauner
2026-11-13 11:59 ` [PATCH v3 19/33] ntfs: " Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 20/33] ntfs: use the generic emergency read-only state Christian Brauner
2026-11-13 11:59 ` [PATCH v3 21/33] ocfs2: keep the current mount options on remount Christian Brauner
2026-11-13 11:59 ` [PATCH v3 22/33] ocfs2: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-13 11:59 ` [PATCH v3 23/33] zonefs: don't set SB_RDONLY after I/O errors Christian Brauner
2026-11-13 11:59 ` [PATCH v3 24/33] zonefs: use the generic emergency read-only state Christian Brauner
2026-11-13 11:59 ` [PATCH v3 25/33] btrfs: check BTRFS_FS_STATE_RO instead of SB_RDONLY at runtime Christian Brauner
2026-11-13 11:59 ` [PATCH v3 26/33] btrfs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 27/33] gfs2: wait for a pending withdraw before remounting read-only Christian Brauner
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 28/33] gfs2: don't set SB_RDONLY on withdraw Christian Brauner
2026-11-13 11:59 ` [PATCH v3 29/33] efivarfs: stop using SB_RDONLY for read-only efivar ops Christian Brauner
2026-11-13 11:59 ` [PATCH v3 30/33] ubifs: don't set SB_RDONLY after errors Christian Brauner
2026-11-14 2:17 ` Zhihao Cheng
2026-11-14 12:03 ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 31/33] ext4: allow remounting read-only in the emergency state Christian Brauner
2026-11-13 11:59 ` [PATCH v3 32/33] ext4: use the generic emergency read-only state Christian Brauner
2026-11-13 11:59 ` [PATCH v3 33/33] xfs: allow remounting read-only after a shutdown Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-214452@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=brauner@kernel.org \
--cc=dsterba@suse.cz \
--cc=jack@suse.com \
--cc=jlbec@evilplan.org \
--cc=joseph.qi@linux.alibaba.com \
--cc=linux-efi@vger.kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-mtd@lists.infradead.org \
--cc=mark@fasheh.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tytso@mit.edu \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox