All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] ext4: drop extra ea_inode ref in cached-block reuse path
@ 2026-09-16  7:10 Matthias Goergens
  2026-09-16  7:24 ` sashiko-bot
  2026-09-24 16:56 ` Jan Kara
  0 siblings, 2 replies; 4+ messages in thread
From: Matthias Goergens @ 2026-09-16  7:10 UTC (permalink / raw)
  To: linux-ext4; +Cc: jack, tytso, Matthias Goergens, stable

ext4_xattr_block_set() takes a reference on the ea_inode via
ext4_xattr_inode_lookup_create() before it builds the new xattr block.
The new-block path drops that reference once
ext4_xattr_inode_inc_ref_all() has counted every entry in the block.
When a separately built block is discarded in favour of an identical
cached block, the cached block already counts its entries and the
lookup reference is left unbalanced.

Each affected reuse leaks one on-disk reference, and the ea_inode and
its data blocks stay allocated after the last xattr referring to them
is gone.  Setting the same xattr on two files is enough when the value
needs an EA inode and the entry does not fit in the inode body: the
second set reuses the first file's block and leaves the ea_inode at
refcount 2, and removing the xattr from both files leaves it at 1.

Drop the reference when the entry was not written into the old block in
place.  new_bh != bs->bh is not the right test: the in-place path can
reach 'inserted' and match a different cached block, and there the
reference belongs to the old block and ext4_xattr_release_block() drops
it, so dropping it here too would under-count.  Conversely, new_bh ==
bs->bh with a cloned s->base (re-setting an unchanged external value on
a shared block) leaks the reference as well, so the check follows both
branches.

If the decrement fails the filesystem is already broken: report the
error and return it without rolling back, putting and clearing ea_inode
first so that cleanup does not retry the decrement.  The reused block
keeps its incremented refcount and its block quota charge; e2fsck
corrects the refcount.

Found by the Sashiko AI review bot while reviewing commit 54b6bd40898d
("ext4: stop retrying saturated xattr cache entries"):
https://sashiko.dev/#/patchset/20260802065941.1726052-1-matthias.goergens@gmail.com?part=1

Fixes: 0a46ef234756 ("ext4: do not create EA inode under buffer lock")
Cc: stable@vger.kernel.org
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
---
v3:
- Error path per Jan: report with ext4_error_inode() and return, no
  unwinding.  Put and clear ea_inode there so cleanup does not retry the
  decrement.  After a successful decrement the branch reaches cleanup
  with error == 0, which only puts the inode, so nothing is needed there.
  The new-block path is unchanged.
- Keep the in-place condition instead of new_bh != bs->bh; reasons and
  measurements in the reply to v2.
- Changelog: the normal-path leak does not retain quota charges; state
  the preconditions of the example.
- Shorter comment, EXT4_ERROR_INODE(); rebased onto ext4 dev; Cc stable.
v2: fail the operation on a failed decrement instead of masking it.
 fs/ext4/xattr.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 5c310747b9650..d9e60b5952d2d 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -2113,6 +2113,24 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
 			mb_cache_entry_touch(ea_block_cache, ce);
 			mb_cache_entry_put(ea_block_cache, ce);
 			ce = NULL;
+			if (ea_inode && !(bs->bh && s->base == bs->bh->b_data)) {
+				/*
+				 * The cached block already owns its EA inode
+				 * references.  For an in-place update the lookup
+				 * reference belongs to the old block and is
+				 * dropped when that block is released.
+				 */
+				error = ext4_xattr_inode_dec_ref(handle,
+								 ea_inode);
+				if (error) {
+					EXT4_ERROR_INODE(inode,
+							 "dec ref error=%d",
+							 error);
+					ext4_put_ea_inode(ea_inode);
+					ea_inode = NULL;
+					goto cleanup;
+				}
+			}
 		} else if (bs->bh && s->base == bs->bh->b_data) {
 			/* We were modifying this block in-place. */
 			ea_bdebug(bs->bh, "keeping this block");
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-24 16:56 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16  7:10 [PATCH v3] ext4: drop extra ea_inode ref in cached-block reuse path Matthias Goergens
2026-09-16  7:24 ` sashiko-bot
2026-09-23 10:18   ` Matthias Goergens
2026-09-24 16:56 ` Jan Kara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.