From: shuo chen <1289151713@qq.com>
To: tytso@mit.edu
Cc: adilger.kernel@dilger.ca, libaokun@linux.alibaba.com,
jack@suse.cz, ojaswin@linux.ibm.com, yi.zhang@huawei.com,
linux-ext4@vger.kernel.org, shuo chen <1289151713@qq.com>
Subject: [PATCH v6] ext4: rewrite ext4_convert_inline_data_nolock to make it safer
Date: Fri, 4 Sep 2026 09:30:27 +0800 [thread overview]
Message-ID: <tencent_3656CB4AA45EA01A8DC2B9865242BA18A709@qq.com> (raw)
In-Reply-To: <tencent_93CC3232AC6A9418BCE4372286770F91850A@qq.com>
Fix issues raised by sashiko-bot.
---
v5 -> v6:
> - [High] Zero-initialization of not_found causes out-of-bounds memory
> operations on uninitialized xattr space.
Use -ENODATA as the initial value.
> - [Critical] Silent data loss when ext4_ext_insert_extent() fails
> with errors like -ENOMEM or -EIO.
Revert the inode to inline data if the error is neither EDQUOT nor ENOSPC.
---
Signed-off-by: shuo chen <1289151713@qq.com>
---
fs/ext4/inline.c | 211 +++++++++++++++++++++++++++++++++++------------
1 file changed, 158 insertions(+), 53 deletions(-)
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 8045e4ff270c..fef98acee688 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -14,6 +14,7 @@
#include "ext4.h"
#include "xattr.h"
#include "truncate.h"
+#include "ext4_extents.h"
#define EXT4_XATTR_SYSTEM_DATA "data"
#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -1070,21 +1071,123 @@ static int ext4_update_inline_dir(handle_t *handle, struct inode *dir,
return 0;
}
-static void ext4_restore_inline_data(handle_t *handle, struct inode *inode,
- struct ext4_iloc *iloc,
- void *buf, int inline_size)
+static int ext4_set_inline_data_block(handle_t *handle, struct inode *inode, ext4_fsblk_t block,
+ unsigned int len, struct buffer_head *bh)
{
- int ret;
+ struct ext4_inode_info *ei = EXT4_I(inode);
+ struct ext4_xattr_ibody_find is = {
+ .s = { .not_found = -ENODATA, },
+ };
+ struct ext4_xattr_info i = {
+ .name_index = EXT4_XATTR_INDEX_SYSTEM,
+ .name = EXT4_XATTR_SYSTEM_DATA,
+ .value = NULL,
+ .value_len = 0,
+ };
+ int error;
+ void *i_block_buf = NULL;
+ void *xattr_buf = NULL;
+ void *header = NULL;
+ void *tail = NULL;
+ int xattr_size;
+ struct ext4_inode *raw_inode;
- ret = ext4_create_inline_data(handle, inode, inline_size);
- if (ret) {
- ext4_msg(inode->i_sb, KERN_EMERG,
- "error restoring inline_data for inode -- potential data loss! (inode %llu, error %d)",
- inode->i_ino, ret);
- return;
+ i_block_buf = kmalloc(EXT4_MIN_INLINE_DATA_SIZE, GFP_NOFS);
+ if (!i_block_buf) {
+ error = -ENOMEM;
+ down_write(&ei->i_data_sem);
+ goto error;
}
- ext4_write_inline_data(inode, iloc, buf, 0, inline_size);
- ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+ down_write(&ei->i_data_sem);
+ error = ext4_get_inode_loc(inode, &is.iloc);
+ if (error)
+ goto error;
+ raw_inode = ext4_raw_inode(&is.iloc);
+ header = IHDR(inode, raw_inode);
+ tail = ITAIL(inode, raw_inode);
+ xattr_size = tail - header;
+ xattr_buf = kmalloc(xattr_size, GFP_NOFS);
+ if (!xattr_buf) {
+ error = -ENOMEM;
+ goto error;
+ }
+ BUFFER_TRACE(is.iloc.bh, "get_write_access");
+ error = ext4_journal_get_write_access(handle, inode->i_sb, is.iloc.bh, EXT4_JTR_NONE);
+ if (error)
+ goto error;
+ memcpy(i_block_buf, (void *)raw_inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ memset((void *)raw_inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ memset(ei->i_data, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ memcpy(xattr_buf, header, xattr_size);
+ if (ext4_has_feature_extents(inode->i_sb) &&
+ (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode))) {
+ ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS);
+ ext4_ext_tree_init(handle, inode);
+ struct ext4_ext_path *path = ext4_find_extent(inode, 0, NULL, 0);
+
+ if (IS_ERR(path)) {
+ error = PTR_ERR(path);
+ goto recovery;
+ }
+ struct ext4_extent newex;
+
+ newex.ee_block = cpu_to_le32(0);
+ newex.ee_len = cpu_to_le16(1);
+ ext4_ext_store_pblock(&newex, block);
+ path = ext4_ext_insert_extent(handle, inode, path, &newex, 0);
+ if (IS_ERR(path)) {
+ error = PTR_ERR(path);
+ if (error == -EDQUOT || error == -ENOSPC)
+ goto recovery;
+ else
+ goto no_free;
+ } else {
+ ext4_free_ext_path(path);
+ }
+ } else {
+ EXT4_I(inode)->i_data[0] = cpu_to_le32(block);
+ }
+ error = ext4_xattr_ibody_find(inode, &i, &is);
+ if (error)
+ goto recovery;
+ if (!is.s.not_found)
+ error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+recovery:
+ if (error) {
+ ext4_discard_preallocations(inode);
+ ext4_free_blocks(handle, inode, NULL, block, len, 0);
+ memcpy(header, xattr_buf, xattr_size);
+no_free:
+ ext4_forget(handle, 0, inode, bh, block);
+ memcpy((void *)raw_inode->i_block, i_block_buf, EXT4_MIN_INLINE_DATA_SIZE);
+ memcpy(ei->i_data, raw_inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
+ get_bh(is.iloc.bh);
+ ext4_mark_iloc_dirty(handle, inode, &is.iloc);
+ } else {
+ ext4_clear_inode_flag(inode, EXT4_INODE_INLINE_DATA);
+ if (S_ISDIR(inode->i_mode)) {
+ i_size_write(inode, inode->i_sb->s_blocksize);
+ EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize;
+ }
+ get_bh(is.iloc.bh);
+ error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
+ EXT4_I(inode)->i_inline_off = 0;
+ EXT4_I(inode)->i_inline_size = 0;
+ ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+ brelse(bh);
+ }
+ goto out;
+error:
+ ext4_forget(handle, 0, inode, bh, block);
+ ext4_discard_preallocations(inode);
+ ext4_free_blocks(handle, inode, NULL, block, len, 0);
+out:
+ brelse(is.iloc.bh);
+ up_write(&ei->i_data_sem);
+ kfree(i_block_buf);
+ kfree(xattr_buf);
+ return error;
}
static int ext4_convert_inline_data_nolock(handle_t *handle,
@@ -1093,9 +1196,12 @@ static int ext4_convert_inline_data_nolock(handle_t *handle,
{
int error;
void *buf = NULL;
- struct buffer_head *data_bh = NULL;
- struct ext4_map_blocks map;
+ struct buffer_head *bh = NULL;
int inline_size;
+ ext4_fsblk_t newblock = 0;
+ struct ext4_allocation_request ar;
+ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+ unsigned int allocated_block;
inline_size = ext4_get_inline_size(inode);
buf = kmalloc(inline_size, GFP_NOFS);
@@ -1120,63 +1226,62 @@ static int ext4_convert_inline_data_nolock(handle_t *handle,
goto out;
}
- error = ext4_destroy_inline_data_nolock(handle, inode);
- if (error)
- goto out;
-
- map.m_lblk = 0;
- map.m_len = 1;
- map.m_flags = 0;
- error = ext4_map_blocks(handle, inode, &map, EXT4_GET_BLOCKS_CREATE);
+ memset(&ar, 0, sizeof(ar));
+ ar.inode = inode;
+ ar.logical = 0;
+ ar.len = 1;
+ if (S_ISREG(inode->i_mode))
+ ar.flags = EXT4_MB_HINT_DATA;
+ else
+ ar.flags = 0;
+ newblock = ext4_mb_new_blocks(handle, &ar, &error);
if (error < 0)
- goto out_restore;
- if (!(map.m_flags & EXT4_MAP_MAPPED)) {
- error = -EIO;
- goto out_restore;
- }
-
- data_bh = sb_getblk(inode->i_sb, map.m_pblk);
- if (!data_bh) {
+ goto out;
+ allocated_block = EXT4_C2B(sbi, ar.len);
+ bh = sb_getblk(inode->i_sb, newblock);
+ if (!bh) {
error = -ENOMEM;
- goto out_restore;
+ goto out_bh;
}
- lock_buffer(data_bh);
- error = ext4_journal_get_create_access(handle, inode->i_sb, data_bh,
+ lock_buffer(bh);
+ error = ext4_journal_get_create_access(handle, inode->i_sb, bh,
EXT4_JTR_NONE);
if (error) {
- unlock_buffer(data_bh);
+ unlock_buffer(bh);
error = -EIO;
- goto out_restore;
+ goto out_bh;
}
- memset(data_bh->b_data, 0, inode->i_sb->s_blocksize);
+ memset(bh->b_data, 0, inode->i_sb->s_blocksize);
if (!S_ISDIR(inode->i_mode)) {
- memcpy(data_bh->b_data, buf, inline_size);
- set_buffer_uptodate(data_bh);
- unlock_buffer(data_bh);
+ memcpy(bh->b_data, buf, inline_size);
+ set_buffer_uptodate(bh);
+ unlock_buffer(bh);
error = ext4_handle_dirty_metadata(handle,
- inode, data_bh);
+ inode, bh);
} else {
- unlock_buffer(data_bh);
- inode->i_size = inode->i_sb->s_blocksize;
- i_size_write(inode, inode->i_sb->s_blocksize);
- EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize;
-
- error = ext4_init_dirblock(handle, inode, data_bh,
+ unlock_buffer(bh);
+ error = ext4_init_dirblock(handle, inode, bh,
le32_to_cpu(((struct ext4_dir_entry_2 *)buf)->inode),
buf + EXT4_INLINE_DOTDOT_SIZE,
inline_size - EXT4_INLINE_DOTDOT_SIZE);
- if (!error)
- error = ext4_mark_inode_dirty(handle, inode);
}
+out_bh:
+ if (error) {
+ if (bh)
+ ext4_forget(handle, 0, inode, bh, newblock);
+ struct ext4_inode_info *ei = EXT4_I(inode);
-out_restore:
- if (error)
- ext4_restore_inline_data(handle, inode, iloc, buf, inline_size);
-
+ down_write(&ei->i_data_sem);
+ ext4_discard_preallocations(inode);
+ ext4_free_blocks(handle, inode, NULL, newblock, allocated_block, 0);
+ up_write(&ei->i_data_sem);
+ } else {
+ error = ext4_set_inline_data_block(handle, inode,
+ newblock, allocated_block, bh);
+ }
out:
- brelse(data_bh);
kfree(buf);
return error;
}
--
2.55.0
next prev parent reply other threads:[~2026-09-04 1:31 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-13 8:33 [PATCH] Add EXT4_STATE_MAY_INLINE_DATA check in ext4_readdir pipishuo
2026-08-13 8:49 ` sashiko-bot
2026-08-14 1:27 ` shuo chen
2026-08-14 3:31 ` Theodore Tso
2026-08-14 8:40 ` shuo chen
2026-08-14 13:57 ` Theodore Tso
2026-08-15 1:43 ` shuo chen
2026-08-16 15:02 ` [PATCH v2] " shuo chen
2026-08-16 15:16 ` sashiko-bot
2026-08-16 20:05 ` [syzbot ci] " syzbot ci
2026-08-17 3:12 ` [PATCH v2] " Theodore Tso
2026-08-17 8:24 ` shuo chen
2026-08-22 16:26 ` [PATCH] " shuo chen
2026-08-22 16:43 ` sashiko-bot
2026-08-22 16:28 ` [PATCH V3] " shuo chen
2026-08-22 16:45 ` sashiko-bot
2026-08-27 8:50 ` shuo chen
2026-08-31 2:20 ` [PATCH v4] ext4: rewrite ext4_convert_inline_data_nolock to make it safer shuo chen
2026-08-31 2:34 ` sashiko-bot
2026-08-31 6:34 ` shuo chen
2026-09-02 10:10 ` [PATCH v5] " shuo chen
2026-09-02 10:24 ` sashiko-bot
2026-09-02 12:24 ` shuo chen
2026-09-04 1:30 ` shuo chen [this message]
2026-09-04 1:46 ` [PATCH v6] " sashiko-bot
2026-09-04 2:06 ` shuo chen
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_3656CB4AA45EA01A8DC2B9865242BA18A709@qq.com \
--to=1289151713@qq.com \
--cc=adilger.kernel@dilger.ca \
--cc=jack@suse.cz \
--cc=libaokun@linux.alibaba.com \
--cc=linux-ext4@vger.kernel.org \
--cc=ojaswin@linux.ibm.com \
--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