Re: [PATCH] ext4: stop retrying saturated xattr cache entries
Jan Kara <[email protected]> Mon, 3 Aug 2026 11:08:54 +0200
| Newsgroups | org.kernel.vger.linux-ext4,org.kernel.vger.stable |
|---|---|
| Message-ID | <qrkmgvn7wszo75li5a46zpbsi7hw6evw36zx6es2vllapovuxy@vukdtamq3t7k> |
On Sun 02-08-26 14:59:41, Matthias Goergens wrote: > ext4_xattr_block_set() retries when a cache entry selected for reuse > has a saturated reference count after taking the buffer lock. The retry > returns to the mbcache lookup without making that entry ineligible, so > it can select the same unusable entry indefinitely. A task spinning > there can hold the parent directory's i_rwsem and leave concurrent > rmdir callers blocked. > > Normally a reusable entry has a reference count below > EXT4_XATTR_REFCOUNT_MAX because the count and MBE_REUSABLE_B are > updated under the same buffer lock. A corrupted filesystem can violate > that invariant. The syzbot reproducer reports allocator and xattr > corruption before triggering this retry loop. > > Check the untrusted on-disk count before incrementing it, avoiding > overflow, and clear MBE_REUSABLE_B when it is already saturated. The > next lookup then skips the entry that was just proven unusable. This > mirrors the normal transition at EXT4_XATTR_REFCOUNT_MAX; the release > path marks the entry reusable again on the exact 1024-to-1023 > transition. > > Using the same QEMU harness and guest parameters, current unpatched > Linux hung in 6 of 8 420-second trials with the do_rmdir signature; > representative NMI backtraces caught the owner spinning in > ext4_xattr_block_set(). The patched kernel completed 28 of 28 trials > without a hung-task report; the final twelve trials exercised the > reviewed overflow-safe form of the change. syzbot's patch testing also > completed without reproducing the hang. > > Reported-and-tested-by: [email protected] > Closes: https://syzkaller.appspot.com/bug?extid=e68dbebd9617a9250e8d > Fixes: 65f8b80053a1 ("ext4: fix race when reusing xattr blocks") > Cc: [email protected] > Signed-off-by: Matthias Goergens <[email protected]> Looks good. Feel free to add: Reviewed-by: Jan Kara <[email protected]> Honza > --- > fs/ext4/xattr.c | 6 ++++-- > 1 file changed, 4 insertions(+), 2 deletions(-) > > diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c > index 982a1f831e228..3c1bb547b2f54 100644 > --- a/fs/ext4/xattr.c > +++ b/fs/ext4/xattr.c > @@ -2075,12 +2075,13 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, > * stable so we can check the additional > * reference fits. > */ > - ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1; > - if (ref > EXT4_XATTR_REFCOUNT_MAX) { > + ref = le32_to_cpu(BHDR(new_bh)->h_refcount); > + if (ref >= EXT4_XATTR_REFCOUNT_MAX) { > /* > * Undo everything and check mbcache > * again. > */ > + clear_bit(MBE_REUSABLE_B, &ce->e_flags); > unlock_buffer(new_bh); > dquot_free_block(inode, > EXT4_C2B(EXT4_SB(sb), > @@ -2091,6 +2092,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, > new_bh = NULL; > goto inserted; > } > + ref++; > BHDR(new_bh)->h_refcount = cpu_to_le32(ref); > if (ref == EXT4_XATTR_REFCOUNT_MAX) > clear_bit(MBE_REUSABLE_B, &ce->e_flags); > -- > 2.55.0 > -- Jan Kara <[email protected]> SUSE Labs, CR