Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

f2fs: fix use-after-free issue when accessing sbi->stat_info

iput() on sbi->node_inode can update sbi->stat_info
in the below context, if the f2fs_write_checkpoint()
has failed with error.

f2fs_balance_fs_bg+0x1ac/0x1ec
f2fs_write_node_pages+0x4c/0x260
do_writepages+0x80/0xbc
__writeback_single_inode+0xdc/0x4ac
writeback_single_inode+0x9c/0x144
write_inode_now+0xc4/0xec
iput+0x194/0x22c
f2fs_put_super+0x11c/0x1e8
generic_shutdown_super+0x70/0xf4
kill_block_super+0x2c/0x5c
kill_f2fs_super+0x44/0x50
deactivate_locked_super+0x60/0x8c
deactivate_super+0x68/0x74
cleanup_mnt+0x40/0x78

Fix this by moving f2fs_destroy_stats() further below iput() in
both f2fs_put_super() and f2fs_fill_super() paths.

Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

authored by

Sahitya Tummala and committed by
Jaegeuk Kim
60aa4d55 bae0ee7a

+15 -12
+15 -12
fs/f2fs/super.c
··· 1058 1058 f2fs_write_checkpoint(sbi, &cpc); 1059 1059 } 1060 1060 1061 - /* f2fs_write_checkpoint can update stat informaion */ 1062 - f2fs_destroy_stats(sbi); 1063 - 1064 1061 /* 1065 1062 * normally superblock is clean, so we need to release this. 1066 1063 * In addition, EIO will skip do checkpoint, we need this as well. ··· 1076 1079 1077 1080 iput(sbi->node_inode); 1078 1081 iput(sbi->meta_inode); 1082 + 1083 + /* 1084 + * iput() can update stat information, if f2fs_write_checkpoint() 1085 + * above failed with error. 1086 + */ 1087 + f2fs_destroy_stats(sbi); 1079 1088 1080 1089 /* destroy f2fs internal modules */ 1081 1090 f2fs_destroy_node_manager(sbi); ··· 3259 3256 3260 3257 f2fs_build_gc_manager(sbi); 3261 3258 3259 + err = f2fs_build_stats(sbi); 3260 + if (err) 3261 + goto free_nm; 3262 + 3262 3263 /* get an inode for node space */ 3263 3264 sbi->node_inode = f2fs_iget(sb, F2FS_NODE_INO(sbi)); 3264 3265 if (IS_ERR(sbi->node_inode)) { 3265 3266 f2fs_msg(sb, KERN_ERR, "Failed to read node inode"); 3266 3267 err = PTR_ERR(sbi->node_inode); 3267 - goto free_nm; 3268 + goto free_stats; 3268 3269 } 3269 - 3270 - err = f2fs_build_stats(sbi); 3271 - if (err) 3272 - goto free_node_inode; 3273 3270 3274 3271 /* read root inode and dentry */ 3275 3272 root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); 3276 3273 if (IS_ERR(root)) { 3277 3274 f2fs_msg(sb, KERN_ERR, "Failed to read root inode"); 3278 3275 err = PTR_ERR(root); 3279 - goto free_stats; 3276 + goto free_node_inode; 3280 3277 } 3281 3278 if (!S_ISDIR(root->i_mode) || !root->i_blocks || 3282 3279 !root->i_size || !root->i_nlink) { 3283 3280 iput(root); 3284 3281 err = -EINVAL; 3285 - goto free_stats; 3282 + goto free_node_inode; 3286 3283 } 3287 3284 3288 3285 sb->s_root = d_make_root(root); /* allocate root dentry */ ··· 3406 3403 free_root_inode: 3407 3404 dput(sb->s_root); 3408 3405 sb->s_root = NULL; 3409 - free_stats: 3410 - f2fs_destroy_stats(sbi); 3411 3406 free_node_inode: 3412 3407 f2fs_release_ino_entry(sbi, true); 3413 3408 truncate_inode_pages_final(NODE_MAPPING(sbi)); 3414 3409 iput(sbi->node_inode); 3410 + free_stats: 3411 + f2fs_destroy_stats(sbi); 3415 3412 free_nm: 3416 3413 f2fs_destroy_node_manager(sbi); 3417 3414 free_sm: