mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Alan <3164729207@qq.com>
To: tytso@mit.edu
Cc: Alan <3164729207@qq.com>,
	Andreas Dilger <adilger.kernel@dilger.ca>,
	Baokun Li <libaokun@linux.alibaba.com>, Jan Kara <jack@suse.cz>,
	Ojaswin Mujoo <ojaswin@linux.ibm.com>,
	"Ritesh Harjani (IBM)" <ritesh.list@gmail.com>,
	Zhang Yi <yi.zhang@huawei.com>,
	linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: [PATCH v2] ext4: free the new extent block on failure to grow the tree
Date: Wed, 11 Nov 2026 15:34:44 +0800	[thread overview]
Message-ID: <tencent_DF25FDEF46DA68C4E18D6DB40F08E30BB705@qq.com> (raw)
In-Reply-To: <tencent_9EB8EBE910378E46C80E006C6A7DA918BA08@qq.com>

ext4_ext_grow_indepth() allocates and accounts a metadata block before
getting its buffer, obtaining journal create access and dirtying the
new extent block. If any of these operations fails, the new block has
not been linked into the extent tree. However, the error paths return
without freeing it, leaking the block and leaving i_blocks larger
than the number of blocks reachable from the inode.

Commit 8a83ac54940d ("ext4: call bdev_getblk() from sb_getblk_gfp()")
removed the implicit __GFP_NOFAIL from the buffer allocation, making
the allocation failure path reachable. The two journal error paths
already lacked cleanup before that commit and are fixed here as well.

Route all three failures through a common cleanup label to free the
unpublished metadata block and undo its inode accounting. Keep the
existing buffer unlock and release handling. Once the new block has
been linked by ext4_idx_store_pblock(), an ext4_mark_inode_dirty()
failure must not free it because the extent tree already references it.

Tested on Linux 6.12.1 by injecting NULL for buffer acquisition and
-EIO for each of the two metadata helper calls at their caller
boundaries. Each unfixed case leaves seven unreachable blocks and
i_blocks of 40 instead of 32 on a filesystem with 4 KiB blocks. With
the fix, all three cases retain i_blocks of 32, preserve the original
data and pass e2fsck -fn after a normal unmount. Retry, truncate and
uninjected growth also pass for each case.

Fixes: 8a83ac54940d ("ext4: call bdev_getblk() from sb_getblk_gfp()")
Cc: stable@vger.kernel.org
Signed-off-by: Alan <3164729207@qq.com>
---
 fs/ext4/extents.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ad0d80e33f6090..f0b2852f8a9342 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1333,15 +1333,17 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
 		return err;
 
 	bh = sb_getblk_gfp(inode->i_sb, newblock, __GFP_MOVABLE | GFP_NOFS);
-	if (unlikely(!bh))
-		return -ENOMEM;
+	if (unlikely(!bh)) {
+		err = -ENOMEM;
+		goto out_free;
+	}
 	lock_buffer(bh);
 
 	err = ext4_journal_get_create_access(handle, inode->i_sb, bh,
 					     EXT4_JTR_NONE);
 	if (err) {
 		unlock_buffer(bh);
-		goto out;
+		goto out_free;
 	}
 
 	ext_size = sizeof(EXT4_I(inode)->i_data);
@@ -1366,7 +1368,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
 
 	err = ext4_handle_dirty_metadata(handle, inode, bh);
 	if (err)
-		goto out;
+		goto out_free;
 
 	/* Update top-level index: num,max,pointer */
 	neh = ext_inode_hdr(inode);
@@ -1389,6 +1391,12 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
 	brelse(bh);
 
 	return err;
+
+out_free:
+	/* The new block has not been linked into the extent tree yet. */
+	ext4_free_blocks(handle, inode, NULL, newblock, 1,
+			 EXT4_FREE_BLOCKS_METADATA);
+	goto out;
 }
 
 /*

base-commit: d135ab453a7400a34930bdda1ef4bdc8acfac80e
-- 
2.53.0


      parent reply	other threads:[~2026-11-11  7:35 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10  7:27 [PATCH] ext4: free the new extent block when getting its buffer fails Alan
2026-11-10 12:31 ` Jan Kara
2026-11-11  7:34 ` Alan [this message]

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=tencent_DF25FDEF46DA68C4E18D6DB40F08E30BB705@qq.com \
    --to=3164729207@qq.com \
    --cc=adilger.kernel@dilger.ca \
    --cc=jack@suse.cz \
    --cc=libaokun@linux.alibaba.com \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojaswin@linux.ibm.com \
    --cc=ritesh.list@gmail.com \
    --cc=stable@vger.kernel.org \
    --cc=tytso@mit.edu \
    --cc=yi.zhang@huawei.com \
    /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

all inboxes | Powered by JetHome®