From 10f04d40a9fa29785206c619f80d8beedb778837 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 2 Nov 2020 16:32:10 +0100 Subject: quota: Don't overflow quota file offsets The on-disk quota format supports quota files with upto 2^32 blocks. Be careful when computing quota file offsets in the quota files from block numbers as they can overflow 32-bit types. Since quota files larger than 4GB would require ~26 millions of quota users, this is mostly a theoretical concern now but better be careful, fuzzers would find the problem sooner or later anyway... Reviewed-by: Andreas Dilger Signed-off-by: Jan Kara --- fs/quota/quota_tree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs/quota') diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c index a6f856f341dc..c5562c871c8b 100644 --- a/fs/quota/quota_tree.c +++ b/fs/quota/quota_tree.c @@ -62,7 +62,7 @@ static ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) memset(buf, 0, info->dqi_usable_bs); return sb->s_op->quota_read(sb, info->dqi_type, buf, - info->dqi_usable_bs, blk << info->dqi_blocksize_bits); + info->dqi_usable_bs, (loff_t)blk << info->dqi_blocksize_bits); } static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) @@ -71,7 +71,7 @@ static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) ssize_t ret; ret = sb->s_op->quota_write(sb, info->dqi_type, buf, - info->dqi_usable_bs, blk << info->dqi_blocksize_bits); + info->dqi_usable_bs, (loff_t)blk << info->dqi_blocksize_bits); if (ret != info->dqi_usable_bs) { quota_error(sb, "dquota write failed"); if (ret >= 0) @@ -284,7 +284,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info, blk); goto out_buf; } - dquot->dq_off = (blk << info->dqi_blocksize_bits) + + dquot->dq_off = ((loff_t)blk << info->dqi_blocksize_bits) + sizeof(struct qt_disk_dqdbheader) + i * info->dqi_entry_size; kfree(buf); @@ -559,7 +559,7 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, ret = -EIO; goto out_buf; } else { - ret = (blk << info->dqi_blocksize_bits) + sizeof(struct + ret = ((loff_t)blk << info->dqi_blocksize_bits) + sizeof(struct qt_disk_dqdbheader) + i * info->dqi_entry_size; } out_buf: -- cgit v1.2.3 From 11c514a99bb960941535134f0587102855e8ddee Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 2 Nov 2020 16:16:29 +0100 Subject: quota: Sanity-check quota file headers on load Perform basic sanity checks of quota headers to avoid kernel crashes on corrupted quota files. CC: stable@vger.kernel.org Reported-by: syzbot+f816042a7ae2225f25ba@syzkaller.appspotmail.com Reviewed-by: Andreas Dilger Signed-off-by: Jan Kara --- fs/quota/quota_v2.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'fs/quota') diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c index e69a2bfdd81c..c21106557a37 100644 --- a/fs/quota/quota_v2.c +++ b/fs/quota/quota_v2.c @@ -157,6 +157,25 @@ static int v2_read_file_info(struct super_block *sb, int type) qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk); qinfo->dqi_ops = &v2r1_qtree_ops; } + ret = -EUCLEAN; + /* Some sanity checks of the read headers... */ + if ((loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits > + i_size_read(sb_dqopt(sb)->files[type])) { + quota_error(sb, "Number of blocks too big for quota file size (%llu > %llu).", + (loff_t)qinfo->dqi_blocks << qinfo->dqi_blocksize_bits, + i_size_read(sb_dqopt(sb)->files[type])); + goto out; + } + if (qinfo->dqi_free_blk >= qinfo->dqi_blocks) { + quota_error(sb, "Free block number too big (%u >= %u).", + qinfo->dqi_free_blk, qinfo->dqi_blocks); + goto out; + } + if (qinfo->dqi_free_entry >= qinfo->dqi_blocks) { + quota_error(sb, "Block with free entry too big (%u >= %u).", + qinfo->dqi_free_entry, qinfo->dqi_blocks); + goto out; + } ret = 0; out: up_read(&dqopt->dqio_sem); -- cgit v1.2.3 From e51d68e76d604c6d5d1eb13ae1d6da7f6c8c0dfc Mon Sep 17 00:00:00 2001 From: Anant Thazhemadam Date: Wed, 9 Dec 2020 01:13:38 +0530 Subject: fs: quota: fix array-index-out-of-bounds bug by passing correct argument to vfs_cleanup_quota_inode() When dquot_resume() was last updated, the argument that got passed to vfs_cleanup_quota_inode was incorrectly set. If type = -1 and dquot_load_quota_sb() returns a negative value, then vfs_cleanup_quota_inode() gets called with -1 passed as an argument, and this leads to an array-index-out-of-bounds bug. Fix this issue by correctly passing the arguments. Fixes: ae45f07d47cc ("quota: Simplify dquot_resume()") Link: https://lore.kernel.org/r/20201208194338.7064-1-anant.thazhemadam@gmail.com Reported-by: syzbot+2643e825238d7aabb37f@syzkaller.appspotmail.com Tested-by: syzbot+2643e825238d7aabb37f@syzkaller.appspotmail.com CC: stable@vger.kernel.org Signed-off-by: Anant Thazhemadam Signed-off-by: Jan Kara --- fs/quota/dquot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/quota') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index bb02989d92b6..4f1373463766 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2455,7 +2455,7 @@ int dquot_resume(struct super_block *sb, int type) ret = dquot_load_quota_sb(sb, cnt, dqopt->info[cnt].dqi_fmt_id, flags); if (ret < 0) - vfs_cleanup_quota_inode(sb, type); + vfs_cleanup_quota_inode(sb, cnt); } return ret; -- cgit v1.2.3