A large number of ext4 bug fixes, mostly buffer and memory leaks on

error return cleanup paths.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAlvoFrEACgkQ8vlZVpUN
 gaMTSQf+Ogrvm7pfWtXf+RkmhhuyR26T+Hwxgl51m5bKetJBjEsh0qOaIfo7etwG
 aLc1x/pWng2VTCHk4z0Ij9KS8YwLK3sQCBYZoJFyT/R09yGgAhLm+xP5j38WLqrX
 h4GxVgekHSATkG95N/So7F7pQiz7gDowgbaYFW3PooXPoHJnCnTzcr7TGFAQBZAw
 iR+8+KtH5E8IcC7Jj40nemk7Wib45DgaeGpP5P9Ct/Jw7hW+Mwhf56NYOWkLdHyy
 4Kt7rm1Sbxam8k3nksNmIwx28bw+S0Ew1zZgkwgAcKcHaWdrv3TtGPkOA26AH+S3
 UVeORM7xH+zXslIOyFK+7sXUZr5LiQ==
 =BaBl
 -----END PGP SIGNATURE-----

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:
 "A large number of ext4 bug fixes, mostly buffer and memory leaks on
  error return cleanup paths"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: missing !bh check in ext4_xattr_inode_write()
  ext4: fix buffer leak in __ext4_read_dirblock() on error path
  ext4: fix buffer leak in ext4_expand_extra_isize_ea() on error path
  ext4: fix buffer leak in ext4_xattr_move_to_block() on error path
  ext4: release bs.bh before re-using in ext4_xattr_block_find()
  ext4: fix buffer leak in ext4_xattr_get_block() on error path
  ext4: fix possible leak of s_journal_flag_rwsem in error path
  ext4: fix possible leak of sbi->s_group_desc_leak in error path
  ext4: remove unneeded brelse call in ext4_xattr_inode_update_ref()
  ext4: avoid possible double brelse() in add_new_gdb() on error path
  ext4: avoid buffer leak in ext4_orphan_add() after prior errors
  ext4: avoid buffer leak on shutdown in ext4_mark_iloc_dirty()
  ext4: fix possible inode leak in the retry loop of ext4_resize_fs()
  ext4: fix missing cleanup if ext4_alloc_flex_bg_array() fails while resizing
  ext4: add missing brelse() update_backups()'s error path
  ext4: add missing brelse() add_new_gdb_meta_bg()'s error path
  ext4: add missing brelse() in set_flexbg_block_bitmap()'s error path
  ext4: avoid potential extra brelse in setup_new_flex_group_blocks()
This commit is contained in:
Linus Torvalds 2018-11-11 16:53:02 -06:00
commit c140f8b072
5 changed files with 51 additions and 31 deletions

View File

@ -5835,9 +5835,10 @@ int ext4_mark_iloc_dirty(handle_t *handle,
{ {
int err = 0; int err = 0;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
put_bh(iloc->bh);
return -EIO; return -EIO;
}
if (IS_I_VERSION(inode)) if (IS_I_VERSION(inode))
inode_inc_iversion(inode); inode_inc_iversion(inode);

View File

@ -126,6 +126,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
if (!is_dx_block && type == INDEX) { if (!is_dx_block && type == INDEX) {
ext4_error_inode(inode, func, line, block, ext4_error_inode(inode, func, line, block,
"directory leaf block found instead of index block"); "directory leaf block found instead of index block");
brelse(bh);
return ERR_PTR(-EFSCORRUPTED); return ERR_PTR(-EFSCORRUPTED);
} }
if (!ext4_has_metadata_csum(inode->i_sb) || if (!ext4_has_metadata_csum(inode->i_sb) ||
@ -2811,7 +2812,9 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
list_del_init(&EXT4_I(inode)->i_orphan); list_del_init(&EXT4_I(inode)->i_orphan);
mutex_unlock(&sbi->s_orphan_lock); mutex_unlock(&sbi->s_orphan_lock);
} }
} } else
brelse(iloc.bh);
jbd_debug(4, "superblock will point to %lu\n", inode->i_ino); jbd_debug(4, "superblock will point to %lu\n", inode->i_ino);
jbd_debug(4, "orphan inode %lu will point to %d\n", jbd_debug(4, "orphan inode %lu will point to %d\n",
inode->i_ino, NEXT_ORPHAN(inode)); inode->i_ino, NEXT_ORPHAN(inode));

View File

@ -459,16 +459,18 @@ static int set_flexbg_block_bitmap(struct super_block *sb, handle_t *handle,
BUFFER_TRACE(bh, "get_write_access"); BUFFER_TRACE(bh, "get_write_access");
err = ext4_journal_get_write_access(handle, bh); err = ext4_journal_get_write_access(handle, bh);
if (err) if (err) {
brelse(bh);
return err; return err;
}
ext4_debug("mark block bitmap %#04llx (+%llu/%u)\n", ext4_debug("mark block bitmap %#04llx (+%llu/%u)\n",
first_cluster, first_cluster - start, count2); first_cluster, first_cluster - start, count2);
ext4_set_bits(bh->b_data, first_cluster - start, count2); ext4_set_bits(bh->b_data, first_cluster - start, count2);
err = ext4_handle_dirty_metadata(handle, NULL, bh); err = ext4_handle_dirty_metadata(handle, NULL, bh);
brelse(bh);
if (unlikely(err)) if (unlikely(err))
return err; return err;
brelse(bh);
} }
return 0; return 0;
@ -605,7 +607,6 @@ static int setup_new_flex_group_blocks(struct super_block *sb,
bh = bclean(handle, sb, block); bh = bclean(handle, sb, block);
if (IS_ERR(bh)) { if (IS_ERR(bh)) {
err = PTR_ERR(bh); err = PTR_ERR(bh);
bh = NULL;
goto out; goto out;
} }
overhead = ext4_group_overhead_blocks(sb, group); overhead = ext4_group_overhead_blocks(sb, group);
@ -618,9 +619,9 @@ static int setup_new_flex_group_blocks(struct super_block *sb,
ext4_mark_bitmap_end(EXT4_B2C(sbi, group_data[i].blocks_count), ext4_mark_bitmap_end(EXT4_B2C(sbi, group_data[i].blocks_count),
sb->s_blocksize * 8, bh->b_data); sb->s_blocksize * 8, bh->b_data);
err = ext4_handle_dirty_metadata(handle, NULL, bh); err = ext4_handle_dirty_metadata(handle, NULL, bh);
brelse(bh);
if (err) if (err)
goto out; goto out;
brelse(bh);
handle_ib: handle_ib:
if (bg_flags[i] & EXT4_BG_INODE_UNINIT) if (bg_flags[i] & EXT4_BG_INODE_UNINIT)
@ -635,18 +636,16 @@ static int setup_new_flex_group_blocks(struct super_block *sb,
bh = bclean(handle, sb, block); bh = bclean(handle, sb, block);
if (IS_ERR(bh)) { if (IS_ERR(bh)) {
err = PTR_ERR(bh); err = PTR_ERR(bh);
bh = NULL;
goto out; goto out;
} }
ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb),
sb->s_blocksize * 8, bh->b_data); sb->s_blocksize * 8, bh->b_data);
err = ext4_handle_dirty_metadata(handle, NULL, bh); err = ext4_handle_dirty_metadata(handle, NULL, bh);
brelse(bh);
if (err) if (err)
goto out; goto out;
brelse(bh);
} }
bh = NULL;
/* Mark group tables in block bitmap */ /* Mark group tables in block bitmap */
for (j = 0; j < GROUP_TABLE_COUNT; j++) { for (j = 0; j < GROUP_TABLE_COUNT; j++) {
@ -685,7 +684,6 @@ static int setup_new_flex_group_blocks(struct super_block *sb,
} }
out: out:
brelse(bh);
err2 = ext4_journal_stop(handle); err2 = ext4_journal_stop(handle);
if (err2 && !err) if (err2 && !err)
err = err2; err = err2;
@ -873,6 +871,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh); err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh);
if (unlikely(err)) { if (unlikely(err)) {
ext4_std_error(sb, err); ext4_std_error(sb, err);
iloc.bh = NULL;
goto exit_inode; goto exit_inode;
} }
brelse(dind); brelse(dind);
@ -924,6 +923,7 @@ static int add_new_gdb_meta_bg(struct super_block *sb,
sizeof(struct buffer_head *), sizeof(struct buffer_head *),
GFP_NOFS); GFP_NOFS);
if (!n_group_desc) { if (!n_group_desc) {
brelse(gdb_bh);
err = -ENOMEM; err = -ENOMEM;
ext4_warning(sb, "not enough memory for %lu groups", ext4_warning(sb, "not enough memory for %lu groups",
gdb_num + 1); gdb_num + 1);
@ -939,8 +939,6 @@ static int add_new_gdb_meta_bg(struct super_block *sb,
kvfree(o_group_desc); kvfree(o_group_desc);
BUFFER_TRACE(gdb_bh, "get_write_access"); BUFFER_TRACE(gdb_bh, "get_write_access");
err = ext4_journal_get_write_access(handle, gdb_bh); err = ext4_journal_get_write_access(handle, gdb_bh);
if (unlikely(err))
brelse(gdb_bh);
return err; return err;
} }
@ -1124,8 +1122,10 @@ static void update_backups(struct super_block *sb, sector_t blk_off, char *data,
backup_block, backup_block - backup_block, backup_block -
ext4_group_first_block_no(sb, group)); ext4_group_first_block_no(sb, group));
BUFFER_TRACE(bh, "get_write_access"); BUFFER_TRACE(bh, "get_write_access");
if ((err = ext4_journal_get_write_access(handle, bh))) if ((err = ext4_journal_get_write_access(handle, bh))) {
brelse(bh);
break; break;
}
lock_buffer(bh); lock_buffer(bh);
memcpy(bh->b_data, data, size); memcpy(bh->b_data, data, size);
if (rest) if (rest)
@ -2023,7 +2023,7 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
err = ext4_alloc_flex_bg_array(sb, n_group + 1); err = ext4_alloc_flex_bg_array(sb, n_group + 1);
if (err) if (err)
return err; goto out;
err = ext4_mb_alloc_groupinfo(sb, n_group + 1); err = ext4_mb_alloc_groupinfo(sb, n_group + 1);
if (err) if (err)
@ -2059,6 +2059,10 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
n_blocks_count_retry = 0; n_blocks_count_retry = 0;
free_flex_gd(flex_gd); free_flex_gd(flex_gd);
flex_gd = NULL; flex_gd = NULL;
if (resize_inode) {
iput(resize_inode);
resize_inode = NULL;
}
goto retry; goto retry;
} }

View File

@ -4075,6 +4075,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_groups_count = blocks_count; sbi->s_groups_count = blocks_count;
sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count, sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count,
(EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=
le32_to_cpu(es->s_inodes_count)) {
ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",
le32_to_cpu(es->s_inodes_count),
((u64)sbi->s_groups_count * sbi->s_inodes_per_group));
ret = -EINVAL;
goto failed_mount;
}
db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
EXT4_DESC_PER_BLOCK(sb); EXT4_DESC_PER_BLOCK(sb);
if (ext4_has_feature_meta_bg(sb)) { if (ext4_has_feature_meta_bg(sb)) {
@ -4094,14 +4102,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
ret = -ENOMEM; ret = -ENOMEM;
goto failed_mount; goto failed_mount;
} }
if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=
le32_to_cpu(es->s_inodes_count)) {
ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",
le32_to_cpu(es->s_inodes_count),
((u64)sbi->s_groups_count * sbi->s_inodes_per_group));
ret = -EINVAL;
goto failed_mount;
}
bgl_lock_init(sbi->s_blockgroup_lock); bgl_lock_init(sbi->s_blockgroup_lock);
@ -4510,6 +4510,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter);
percpu_counter_destroy(&sbi->s_dirs_counter); percpu_counter_destroy(&sbi->s_dirs_counter);
percpu_counter_destroy(&sbi->s_dirtyclusters_counter); percpu_counter_destroy(&sbi->s_dirtyclusters_counter);
percpu_free_rwsem(&sbi->s_journal_flag_rwsem);
failed_mount5: failed_mount5:
ext4_ext_release(sb); ext4_ext_release(sb);
ext4_release_system_zone(sb); ext4_release_system_zone(sb);

View File

@ -1031,10 +1031,8 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode,
inode_lock(ea_inode); inode_lock(ea_inode);
ret = ext4_reserve_inode_write(handle, ea_inode, &iloc); ret = ext4_reserve_inode_write(handle, ea_inode, &iloc);
if (ret) { if (ret)
iloc.bh = NULL;
goto out; goto out;
}
ref_count = ext4_xattr_inode_get_ref(ea_inode); ref_count = ext4_xattr_inode_get_ref(ea_inode);
ref_count += ref_change; ref_count += ref_change;
@ -1080,12 +1078,10 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode,
} }
ret = ext4_mark_iloc_dirty(handle, ea_inode, &iloc); ret = ext4_mark_iloc_dirty(handle, ea_inode, &iloc);
iloc.bh = NULL;
if (ret) if (ret)
ext4_warning_inode(ea_inode, ext4_warning_inode(ea_inode,
"ext4_mark_iloc_dirty() failed ret=%d", ret); "ext4_mark_iloc_dirty() failed ret=%d", ret);
out: out:
brelse(iloc.bh);
inode_unlock(ea_inode); inode_unlock(ea_inode);
return ret; return ret;
} }
@ -1388,6 +1384,12 @@ static int ext4_xattr_inode_write(handle_t *handle, struct inode *ea_inode,
bh = ext4_getblk(handle, ea_inode, block, 0); bh = ext4_getblk(handle, ea_inode, block, 0);
if (IS_ERR(bh)) if (IS_ERR(bh))
return PTR_ERR(bh); return PTR_ERR(bh);
if (!bh) {
WARN_ON_ONCE(1);
EXT4_ERROR_INODE(ea_inode,
"ext4_getblk() return bh = NULL");
return -EFSCORRUPTED;
}
ret = ext4_journal_get_write_access(handle, bh); ret = ext4_journal_get_write_access(handle, bh);
if (ret) if (ret)
goto out; goto out;
@ -2276,8 +2278,10 @@ static struct buffer_head *ext4_xattr_get_block(struct inode *inode)
if (!bh) if (!bh)
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
error = ext4_xattr_check_block(inode, bh); error = ext4_xattr_check_block(inode, bh);
if (error) if (error) {
brelse(bh);
return ERR_PTR(error); return ERR_PTR(error);
}
return bh; return bh;
} }
@ -2397,6 +2401,8 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
error = ext4_xattr_block_set(handle, inode, &i, &bs); error = ext4_xattr_block_set(handle, inode, &i, &bs);
} else if (error == -ENOSPC) { } else if (error == -ENOSPC) {
if (EXT4_I(inode)->i_file_acl && !bs.s.base) { if (EXT4_I(inode)->i_file_acl && !bs.s.base) {
brelse(bs.bh);
bs.bh = NULL;
error = ext4_xattr_block_find(inode, &i, &bs); error = ext4_xattr_block_find(inode, &i, &bs);
if (error) if (error)
goto cleanup; goto cleanup;
@ -2617,6 +2623,8 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
kfree(buffer); kfree(buffer);
if (is) if (is)
brelse(is->iloc.bh); brelse(is->iloc.bh);
if (bs)
brelse(bs->bh);
kfree(is); kfree(is);
kfree(bs); kfree(bs);
@ -2696,7 +2704,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
struct ext4_inode *raw_inode, handle_t *handle) struct ext4_inode *raw_inode, handle_t *handle)
{ {
struct ext4_xattr_ibody_header *header; struct ext4_xattr_ibody_header *header;
struct buffer_head *bh;
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
static unsigned int mnt_count; static unsigned int mnt_count;
size_t min_offs; size_t min_offs;
@ -2737,13 +2744,17 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
* EA block can hold new_extra_isize bytes. * EA block can hold new_extra_isize bytes.
*/ */
if (EXT4_I(inode)->i_file_acl) { if (EXT4_I(inode)->i_file_acl) {
struct buffer_head *bh;
bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);
error = -EIO; error = -EIO;
if (!bh) if (!bh)
goto cleanup; goto cleanup;
error = ext4_xattr_check_block(inode, bh); error = ext4_xattr_check_block(inode, bh);
if (error) if (error) {
brelse(bh);
goto cleanup; goto cleanup;
}
base = BHDR(bh); base = BHDR(bh);
end = bh->b_data + bh->b_size; end = bh->b_data + bh->b_size;
min_offs = end - base; min_offs = end - base;