Re: [PATCH 6/6] xfs: don't livelock in scrub on a circular unlinked list

"Darrick J. Wong" <[email protected]>
Newsgroups org.kernel.vger.linux-xfs,org.kernel.vger.stable
Message-ID <20260716214720.GR7380@frogsfrogsfrogs>
On Thu, Jul 16, 2026 at 10:24:21AM +0200, Christoph Hellwig wrote:
> On Wed, Jul 15, 2026 at 11:07:40PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <[email protected]>
> > 
> > LOLLM points out that online fsck can livelock if an unlinked inode list
> > contains a loop.  Use a bitmap to detect cycles.
> 
> Didn't we recently have some AI generated patch for the same thing?

Yes.

> Either way, this does actually look reasonable.  But also complex
> enough that I really want a test case that creates an image with
> such a corruption to test this case before we merge the kernel code
> as this is something that basically requires a maliciously crafted
> image, and we trade new otherwise untested code for a theoretical
> bug.

I'll try to work on that one today.

> > +static int
> > +xchk_iunlink_bucket(
> > +	struct xfs_scrub	*sc,
> > +	unsigned int		bucket,
> > +	xfs_agino_t		agino)
> 
> Maybe add a comment what this function tests?

/*
 * Walk the incore unlinked list for a particular AGI bucket to construct
 * the unlinked inode bitmap for later reconstruction of the unlinked list.
 * Returns 1 if we should keep checking, 0 to stop checking, or a negative
 * errno.
 */

> 
> > +{
> > +	struct xagino_bitmap	seen;
> > +	struct xfs_inode	*ip;
> > +	int			ret = 0;
> > +
> > +	xagino_bitmap_init(&seen);
> > +
> > +	while (agino != NULLAGINO) {
> > +		unsigned int	len = 1;
> > +
> > +		if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) {
> > +			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
> > +			goto bad;
> > +		}
> 
> Handle entries that should not be here, makes sense.
> 
> > +
> > +		if (xagino_bitmap_test(&seen, agino, &len)) {
> > +			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
> > +			goto bad;
> > +		}
> 
> Check that we don't have duplicates, makes sense.
> 
> > +		ip = xfs_iunlink_lookup(sc->sa.pag, agino);
> > +		if (!ip) {
> > +			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
> > +			goto bad;
> > +		}
> > +
> > +		if (!xfs_inode_on_unlinked_list(ip)) {
> > +			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
> > +			goto bad;
> > +		}
> 
> The that that the inode actually is on the unlinked list, makes
> sense.
> 
> >  	for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
> > -		xfs_agino_t	agino = be32_to_cpu(agi->agi_unlinked[i]);
> > +		int		ret;
> >  
> > -		while (agino != NULLAGINO) {
> > -			if (agino % XFS_AGI_UNLINKED_BUCKETS != i) {
> > -				xchk_block_set_corrupt(sc, sc->sa.agi_bp);
> > -				return;
> > -			}
> 
> Ahh, and this is mostly existing code...
> 
> Maybe split the reactoring into a separate helper into a prep patch?

Ok will do.

--D
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.