Skip to content

Commit b892772

Browse files
committed
Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 fixes from Ted Ts'o: "Fix bugs that could cause kernel deadlocks or file system corruption while moving xattrs to expand the extended inode. Also add some sanity checks to the block group descriptors to make sure we don't end up overwriting the superblock" * tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: avoid deadlock when expanding inode size ext4: properly align shifted xattrs when expanding inodes ext4: fix xattr shifting when expanding inodes part 2 ext4: fix xattr shifting when expanding inodes ext4: validate that metadata blocks do not overlap superblock ext4: reserve xattr index for the Hurd
2 parents 1f6a563 + 2e81a4e commit b892772

File tree

4 files changed

+49
-25
lines changed

4 files changed

+49
-25
lines changed

fs/ext4/inode.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5466,8 +5466,6 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
54665466
sbi->s_want_extra_isize,
54675467
iloc, handle);
54685468
if (ret) {
5469-
ext4_set_inode_state(inode,
5470-
EXT4_STATE_NO_EXPAND);
54715469
if (mnt_count !=
54725470
le16_to_cpu(sbi->s_es->s_mnt_count)) {
54735471
ext4_warning(inode->i_sb,

fs/ext4/super.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,7 @@ void ext4_group_desc_csum_set(struct super_block *sb, __u32 block_group,
22112211

22122212
/* Called at mount-time, super-block is locked */
22132213
static int ext4_check_descriptors(struct super_block *sb,
2214+
ext4_fsblk_t sb_block,
22142215
ext4_group_t *first_not_zeroed)
22152216
{
22162217
struct ext4_sb_info *sbi = EXT4_SB(sb);
@@ -2241,20 +2242,35 @@ static int ext4_check_descriptors(struct super_block *sb,
22412242
grp = i;
22422243

22432244
block_bitmap = ext4_block_bitmap(sb, gdp);
2245+
if (block_bitmap == sb_block) {
2246+
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
2247+
"Block bitmap for group %u overlaps "
2248+
"superblock", i);
2249+
}
22442250
if (block_bitmap < first_block || block_bitmap > last_block) {
22452251
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
22462252
"Block bitmap for group %u not in group "
22472253
"(block %llu)!", i, block_bitmap);
22482254
return 0;
22492255
}
22502256
inode_bitmap = ext4_inode_bitmap(sb, gdp);
2257+
if (inode_bitmap == sb_block) {
2258+
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
2259+
"Inode bitmap for group %u overlaps "
2260+
"superblock", i);
2261+
}
22512262
if (inode_bitmap < first_block || inode_bitmap > last_block) {
22522263
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
22532264
"Inode bitmap for group %u not in group "
22542265
"(block %llu)!", i, inode_bitmap);
22552266
return 0;
22562267
}
22572268
inode_table = ext4_inode_table(sb, gdp);
2269+
if (inode_table == sb_block) {
2270+
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
2271+
"Inode table for group %u overlaps "
2272+
"superblock", i);
2273+
}
22582274
if (inode_table < first_block ||
22592275
inode_table + sbi->s_itb_per_group - 1 > last_block) {
22602276
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
@@ -3757,7 +3773,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
37573773
goto failed_mount2;
37583774
}
37593775
}
3760-
if (!ext4_check_descriptors(sb, &first_not_zeroed)) {
3776+
if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) {
37613777
ext4_msg(sb, KERN_ERR, "group descriptors corrupted!");
37623778
ret = -EFSCORRUPTED;
37633779
goto failed_mount2;

fs/ext4/xattr.c

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,15 +1353,19 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
13531353
size_t min_offs, free;
13541354
int total_ino;
13551355
void *base, *start, *end;
1356-
int extra_isize = 0, error = 0, tried_min_extra_isize = 0;
1356+
int error = 0, tried_min_extra_isize = 0;
13571357
int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);
1358+
int isize_diff; /* How much do we need to grow i_extra_isize */
13581359

13591360
down_write(&EXT4_I(inode)->xattr_sem);
1361+
/*
1362+
* Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty
1363+
*/
1364+
ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
13601365
retry:
1361-
if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
1362-
up_write(&EXT4_I(inode)->xattr_sem);
1363-
return 0;
1364-
}
1366+
isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
1367+
if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
1368+
goto out;
13651369

13661370
header = IHDR(inode, raw_inode);
13671371
entry = IFIRST(header);
@@ -1382,16 +1386,15 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
13821386
goto cleanup;
13831387

13841388
free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
1385-
if (free >= new_extra_isize) {
1389+
if (free >= isize_diff) {
13861390
entry = IFIRST(header);
13871391
ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
13881392
- new_extra_isize, (void *)raw_inode +
13891393
EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
13901394
(void *)header, total_ino,
13911395
inode->i_sb->s_blocksize);
13921396
EXT4_I(inode)->i_extra_isize = new_extra_isize;
1393-
error = 0;
1394-
goto cleanup;
1397+
goto out;
13951398
}
13961399

13971400
/*
@@ -1414,7 +1417,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
14141417
end = bh->b_data + bh->b_size;
14151418
min_offs = end - base;
14161419
free = ext4_xattr_free_space(first, &min_offs, base, NULL);
1417-
if (free < new_extra_isize) {
1420+
if (free < isize_diff) {
14181421
if (!tried_min_extra_isize && s_min_extra_isize) {
14191422
tried_min_extra_isize++;
14201423
new_extra_isize = s_min_extra_isize;
@@ -1428,7 +1431,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
14281431
free = inode->i_sb->s_blocksize;
14291432
}
14301433

1431-
while (new_extra_isize > 0) {
1434+
while (isize_diff > 0) {
14321435
size_t offs, size, entry_size;
14331436
struct ext4_xattr_entry *small_entry = NULL;
14341437
struct ext4_xattr_info i = {
@@ -1459,7 +1462,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
14591462
EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +
14601463
EXT4_XATTR_LEN(last->e_name_len);
14611464
if (total_size <= free && total_size < min_total_size) {
1462-
if (total_size < new_extra_isize) {
1465+
if (total_size < isize_diff) {
14631466
small_entry = last;
14641467
} else {
14651468
entry = last;
@@ -1514,22 +1517,22 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
15141517
error = ext4_xattr_ibody_set(handle, inode, &i, is);
15151518
if (error)
15161519
goto cleanup;
1520+
total_ino -= entry_size;
15171521

15181522
entry = IFIRST(header);
1519-
if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize)
1520-
shift_bytes = new_extra_isize;
1523+
if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
1524+
shift_bytes = isize_diff;
15211525
else
1522-
shift_bytes = entry_size + size;
1526+
shift_bytes = entry_size + EXT4_XATTR_SIZE(size);
15231527
/* Adjust the offsets and shift the remaining entries ahead */
1524-
ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
1525-
shift_bytes, (void *)raw_inode +
1526-
EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes,
1527-
(void *)header, total_ino - entry_size,
1528-
inode->i_sb->s_blocksize);
1528+
ext4_xattr_shift_entries(entry, -shift_bytes,
1529+
(void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
1530+
EXT4_I(inode)->i_extra_isize + shift_bytes,
1531+
(void *)header, total_ino, inode->i_sb->s_blocksize);
15291532

1530-
extra_isize += shift_bytes;
1531-
new_extra_isize -= shift_bytes;
1532-
EXT4_I(inode)->i_extra_isize = extra_isize;
1533+
isize_diff -= shift_bytes;
1534+
EXT4_I(inode)->i_extra_isize += shift_bytes;
1535+
header = IHDR(inode, raw_inode);
15331536

15341537
i.name = b_entry_name;
15351538
i.value = buffer;
@@ -1551,6 +1554,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
15511554
kfree(bs);
15521555
}
15531556
brelse(bh);
1557+
out:
1558+
ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
15541559
up_write(&EXT4_I(inode)->xattr_sem);
15551560
return 0;
15561561

@@ -1562,6 +1567,10 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
15621567
kfree(is);
15631568
kfree(bs);
15641569
brelse(bh);
1570+
/*
1571+
* We deliberately leave EXT4_STATE_NO_EXPAND set here since inode
1572+
* size expansion failed.
1573+
*/
15651574
up_write(&EXT4_I(inode)->xattr_sem);
15661575
return error;
15671576
}

fs/ext4/xattr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#define EXT4_XATTR_INDEX_SYSTEM 7
2525
#define EXT4_XATTR_INDEX_RICHACL 8
2626
#define EXT4_XATTR_INDEX_ENCRYPTION 9
27+
#define EXT4_XATTR_INDEX_HURD 10 /* Reserved for Hurd */
2728

2829
struct ext4_xattr_header {
2930
__le32 h_magic; /* magic number for identification */

0 commit comments

Comments
 (0)