2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

A few more miscellaneous ext4 bug fixes and cleanups including some

syzbot failures and fixing a stale file handing refeencing an inode
 previously used as a regular file, but which has been deleted and
 reused as an ea_inode would result in ext4 erroneously consider this a
 case of fs corrupotion.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAmf7r3YACgkQ8vlZVpUN
 gaPl9QgApwE5BAQdO6miW0sDMPj5b4sMc25aG4OPlfKhFqiIJB0Ub4zC2n0OFnaf
 HXk8P5oVeepH9ciTnYFF30X20Ythzjwmd9j5eyq2wsfYASQUjfcvmR9WovbqZtGQ
 3Zerd9QFp7SvZa+K4sADBhEb/7HAnxDGfiqSQptY6WQTwD+it1bnuhmzG0m6AH4m
 R1ItREDx7D2QrudDToFBd8XQ+FgRETZ8Qrs7PqIznw/dBNMdHRnAiw2eiyuoPU/S
 T8cmCxii3Z9sJ6LtohKYuWOmOmdxg951V5ZcekVRuaFSljSUsRsIplO7OlaMvQDs
 9vGVKiiZLdU2B0Wd90IeQUdJmP4xPg==
 =I8qx
 -----END PGP SIGNATURE-----

Merge tag 'ext4_for_linus-6.15-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 fixes from Ted Ts'o:
 "A few more miscellaneous ext4 bug fixes and cleanups including some
  syzbot failures and fixing a stale file handing refeencing an inode
  previously used as a regular file, but which has been deleted and
  reused as an ea_inode would result in ext4 erroneously considering
  this a case of fs corruption"

* tag 'ext4_for_linus-6.15-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: fix off-by-one error in do_split
  ext4: make block validity check resistent to sb bh corruption
  ext4: avoid -Wflex-array-member-not-at-end warning
  Documentation: ext4: Add fields to ext4_super_block documentation
  ext4: don't treat fhandle lookup of ea_inode as FS corruption
This commit is contained in:
Linus Torvalds 2025-04-13 07:15:50 -07:00
commit 5aaaedb0cb
5 changed files with 77 additions and 43 deletions

View File

@ -328,9 +328,13 @@ The ext4 superblock is laid out as follows in
- s_checksum_type - s_checksum_type
- Metadata checksum algorithm type. The only valid value is 1 (crc32c). - Metadata checksum algorithm type. The only valid value is 1 (crc32c).
* - 0x176 * - 0x176
- __le16 - \_\_u8
- s_reserved_pad - s\_encryption\_level
- - Versioning level for encryption.
* - 0x177
- \_\_u8
- s\_reserved\_pad
- Padding to next 32bits.
* - 0x178 * - 0x178
- __le64 - __le64
- s_kbytes_written - s_kbytes_written
@ -466,9 +470,13 @@ The ext4 superblock is laid out as follows in
- s_last_error_time_hi - s_last_error_time_hi
- Upper 8 bits of the s_last_error_time field. - Upper 8 bits of the s_last_error_time field.
* - 0x27A * - 0x27A
- __u8 - \_\_u8
- s_pad[2] - s\_first\_error\_errcode
- Zero padding. -
* - 0x27B
- \_\_u8
- s\_last\_error\_errcode
-
* - 0x27C * - 0x27C
- __le16 - __le16
- s_encoding - s_encoding

View File

@ -351,10 +351,9 @@ int ext4_check_blockref(const char *function, unsigned int line,
{ {
__le32 *bref = p; __le32 *bref = p;
unsigned int blk; unsigned int blk;
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
if (ext4_has_feature_journal(inode->i_sb) && if (journal && inode == journal->j_inode)
(inode->i_ino ==
le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum)))
return 0; return 0;
while (bref < p+max) { while (bref < p+max) {

View File

@ -386,10 +386,11 @@ static int __check_block_validity(struct inode *inode, const char *func,
unsigned int line, unsigned int line,
struct ext4_map_blocks *map) struct ext4_map_blocks *map)
{ {
if (ext4_has_feature_journal(inode->i_sb) && journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
(inode->i_ino ==
le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) if (journal && inode == journal->j_inode)
return 0; return 0;
if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) { if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) {
ext4_error_inode(inode, func, line, map->m_pblk, ext4_error_inode(inode, func, line, map->m_pblk,
"lblock %lu mapped to illegal pblock %llu " "lblock %lu mapped to illegal pblock %llu "
@ -4724,22 +4725,43 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val)
inode_set_iversion_queried(inode, val); inode_set_iversion_queried(inode, val);
} }
static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags) static int check_igot_inode(struct inode *inode, ext4_iget_flags flags,
const char *function, unsigned int line)
{ {
const char *err_str;
if (flags & EXT4_IGET_EA_INODE) { if (flags & EXT4_IGET_EA_INODE) {
if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
return "missing EA_INODE flag"; err_str = "missing EA_INODE flag";
goto error;
}
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
EXT4_I(inode)->i_file_acl) EXT4_I(inode)->i_file_acl) {
return "ea_inode with extended attributes"; err_str = "ea_inode with extended attributes";
goto error;
}
} else { } else {
if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
return "unexpected EA_INODE flag"; /*
* open_by_handle_at() could provide an old inode number
* that has since been reused for an ea_inode; this does
* not indicate filesystem corruption
*/
if (flags & EXT4_IGET_HANDLE)
return -ESTALE;
err_str = "unexpected EA_INODE flag";
goto error;
}
} }
if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) {
return "unexpected bad inode w/o EXT4_IGET_BAD"; err_str = "unexpected bad inode w/o EXT4_IGET_BAD";
return NULL; goto error;
}
return 0;
error:
ext4_error_inode(inode, function, line, 0, err_str);
return -EFSCORRUPTED;
} }
struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
@ -4751,7 +4773,6 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
struct ext4_inode_info *ei; struct ext4_inode_info *ei;
struct ext4_super_block *es = EXT4_SB(sb)->s_es; struct ext4_super_block *es = EXT4_SB(sb)->s_es;
struct inode *inode; struct inode *inode;
const char *err_str;
journal_t *journal = EXT4_SB(sb)->s_journal; journal_t *journal = EXT4_SB(sb)->s_journal;
long ret; long ret;
loff_t size; loff_t size;
@ -4780,10 +4801,10 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW)) { if (!(inode->i_state & I_NEW)) {
if ((err_str = check_igot_inode(inode, flags)) != NULL) { ret = check_igot_inode(inode, flags, function, line);
ext4_error_inode(inode, function, line, 0, err_str); if (ret) {
iput(inode); iput(inode);
return ERR_PTR(-EFSCORRUPTED); return ERR_PTR(ret);
} }
return inode; return inode;
} }
@ -5065,13 +5086,21 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
ret = -EFSCORRUPTED; ret = -EFSCORRUPTED;
goto bad_inode; goto bad_inode;
} }
if ((err_str = check_igot_inode(inode, flags)) != NULL) { ret = check_igot_inode(inode, flags, function, line);
ext4_error_inode(inode, function, line, 0, err_str); /*
ret = -EFSCORRUPTED; * -ESTALE here means there is nothing inherently wrong with the inode,
goto bad_inode; * it's just not an inode we can return for an fhandle lookup.
*/
if (ret == -ESTALE) {
brelse(iloc.bh);
unlock_new_inode(inode);
iput(inode);
return ERR_PTR(-ESTALE);
} }
if (ret)
goto bad_inode;
brelse(iloc.bh); brelse(iloc.bh);
unlock_new_inode(inode); unlock_new_inode(inode);
return inode; return inode;

View File

@ -3037,10 +3037,8 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
unsigned char blocksize_bits = min_t(unsigned char, unsigned char blocksize_bits = min_t(unsigned char,
sb->s_blocksize_bits, sb->s_blocksize_bits,
EXT4_MAX_BLOCK_LOG_SIZE); EXT4_MAX_BLOCK_LOG_SIZE);
struct sg { DEFINE_RAW_FLEX(struct ext4_group_info, sg, bb_counters,
struct ext4_group_info info; EXT4_MAX_BLOCK_LOG_SIZE + 2);
ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2];
} sg;
group--; group--;
if (group == 0) if (group == 0)
@ -3048,7 +3046,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
" 2^0 2^1 2^2 2^3 2^4 2^5 2^6 " " 2^0 2^1 2^2 2^3 2^4 2^5 2^6 "
" 2^7 2^8 2^9 2^10 2^11 2^12 2^13 ]\n"); " 2^7 2^8 2^9 2^10 2^11 2^12 2^13 ]\n");
i = (blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) + i = (blocksize_bits + 2) * sizeof(sg->bb_counters[0]) +
sizeof(struct ext4_group_info); sizeof(struct ext4_group_info);
grinfo = ext4_get_group_info(sb, group); grinfo = ext4_get_group_info(sb, group);
@ -3068,14 +3066,14 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
* We care only about free space counters in the group info and * We care only about free space counters in the group info and
* these are safe to access even after the buddy has been unloaded * these are safe to access even after the buddy has been unloaded
*/ */
memcpy(&sg, grinfo, i); memcpy(sg, grinfo, i);
seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free, seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg->bb_free,
sg.info.bb_fragments, sg.info.bb_first_free); sg->bb_fragments, sg->bb_first_free);
for (i = 0; i <= 13; i++) for (i = 0; i <= 13; i++)
seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ? seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ?
sg.info.bb_counters[i] : 0); sg->bb_counters[i] : 0);
seq_puts(seq, " ]"); seq_puts(seq, " ]");
if (EXT4_MB_GRP_BBITMAP_CORRUPT(&sg.info)) if (EXT4_MB_GRP_BBITMAP_CORRUPT(sg))
seq_puts(seq, " Block bitmap corrupted!"); seq_puts(seq, " Block bitmap corrupted!");
seq_putc(seq, '\n'); seq_putc(seq, '\n');
return 0; return 0;

View File

@ -1971,7 +1971,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
* split it in half by count; each resulting block will have at least * split it in half by count; each resulting block will have at least
* half the space free. * half the space free.
*/ */
if (i > 0) if (i >= 0)
split = count - move; split = count - move;
else else
split = count/2; split = count/2;