Linux EXT4 FS development
 help / color / mirror / Atom feed
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

  reply	other threads:[~2026-11-14 12:03 UTC|newest]

Thread overview: 70+ 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-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 04/33] ecryptfs: fall back to a read-only " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 07/33] jfs: " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 08/33] nilfs2: " Christian Brauner
2026-11-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 15/33] hfsplus: don't set SB_RDONLY when creating the attributes tree Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 16/33] affs: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 21/33] ocfs2: keep the current mount options on remount Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 22/33] ocfs2: don't set SB_RDONLY after filesystem errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 23/33] zonefs: don't set SB_RDONLY after I/O errors Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 24/33] zonefs: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 29/33] efivarfs: stop using SB_RDONLY for read-only efivar ops Christian Brauner
2026-11-14 12:03   ` sashiko-bot
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-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 32/33] ext4: use the generic emergency read-only state Christian Brauner
2026-11-14 12:03   ` sashiko-bot
2026-11-13 11:59 ` [PATCH v3 33/33] xfs: allow remounting read-only after a shutdown Christian Brauner
2026-11-14 12:03   ` sashiko-bot

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