From 5190db9fdd20fa5ba6084c98a3bc71c2fdf6a871 Mon Sep 17 00:00:00 2001 From: Roman Anufriev Date: Sun, 18 Oct 2020 05:56:54 +0300 Subject: [PATCH 01/10] fs/quota: update quota state flags scheme with project quota flags Current quota state flags scheme doesn't include project quota and thus shows all flags after DQUOT_USAGE_ENABLED wrong. Fix this and also add DQUOT_NOLIST_DIRTY to the scheme. Link: https://lore.kernel.org/r/1602989814-28922-1-git-send-email-dotdot@yandex-team.ru Signed-off-by: Roman Anufriev Signed-off-by: Jan Kara --- include/linux/quota.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/include/linux/quota.h b/include/linux/quota.h index 27aab84fcbaa..18ebd39c9487 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -448,17 +448,18 @@ struct quota_format_type { }; /** - * Quota state flags - they actually come in two flavors - for users and groups. + * Quota state flags - they come in three flavors - for users, groups and projects. * * Actual typed flags layout: - * USRQUOTA GRPQUOTA - * DQUOT_USAGE_ENABLED 0x0001 0x0002 - * DQUOT_LIMITS_ENABLED 0x0004 0x0008 - * DQUOT_SUSPENDED 0x0010 0x0020 + * USRQUOTA GRPQUOTA PRJQUOTA + * DQUOT_USAGE_ENABLED 0x0001 0x0002 0x0004 + * DQUOT_LIMITS_ENABLED 0x0008 0x0010 0x0020 + * DQUOT_SUSPENDED 0x0040 0x0080 0x0100 * * Following bits are used for non-typed flags: - * DQUOT_QUOTA_SYS_FILE 0x0040 - * DQUOT_NEGATIVE_USAGE 0x0080 + * DQUOT_QUOTA_SYS_FILE 0x0200 + * DQUOT_NEGATIVE_USAGE 0x0400 + * DQUOT_NOLIST_DIRTY 0x0800 */ enum { _DQUOT_USAGE_ENABLED = 0, /* Track disk usage for users */ From a219ee41899bcfabd63cd6617256d5be13a7f4b6 Mon Sep 17 00:00:00 2001 From: Xianting Tian Date: Sat, 10 Oct 2020 17:43:35 +0800 Subject: [PATCH 02/10] ext2: Remove unnecessary blank Remove unnecessary blank when calling kmalloc_array(). Link: https://lore.kernel.org/r/20201010094335.39797-1-tian.xianting@h3c.com Signed-off-by: Xianting Tian Signed-off-by: Jan Kara --- fs/ext2/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 09f1fe676972..6c4753277916 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1070,7 +1070,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) / EXT2_BLOCKS_PER_GROUP(sb)) + 1; db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); - sbi->s_group_desc = kmalloc_array (db_count, + sbi->s_group_desc = kmalloc_array(db_count, sizeof(struct buffer_head *), GFP_KERNEL); if (sbi->s_group_desc == NULL) { From 10f04d40a9fa29785206c619f80d8beedb778837 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 2 Nov 2020 16:32:10 +0100 Subject: [PATCH 03/10] 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(-) 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: From 11c514a99bb960941535134f0587102855e8ddee Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 2 Nov 2020 16:16:29 +0100 Subject: [PATCH 04/10] 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(+) 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); From 2356eb80ca42deba0bf2523c62530d4c79dad08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Sun, 8 Nov 2020 01:40:45 +0100 Subject: [PATCH 05/10] docs: filesystems: Reduce ext2.rst to one top-level heading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prevents the other headings like "Options" and "Specification" from leaking out and being listed separately in the table of contents. Link: https://lore.kernel.org/r/20201108004045.1378676-1-j.neuschaefer@gmx.net Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jan Kara --- Documentation/filesystems/ext2.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/filesystems/ext2.rst b/Documentation/filesystems/ext2.rst index d83dbbb162e2..c2fce22cfd03 100644 --- a/Documentation/filesystems/ext2.rst +++ b/Documentation/filesystems/ext2.rst @@ -1,6 +1,7 @@ .. SPDX-License-Identifier: GPL-2.0 +============================== The Second Extended Filesystem ============================== From a6fbd0ab3d7a1a02e61733a80c22fb01c65819b9 Mon Sep 17 00:00:00 2001 From: Ira Weiny Date: Thu, 12 Nov 2020 09:42:44 -0800 Subject: [PATCH 06/10] fs/ext2: Use ext2_put_page There are 3 places in namei.c where the equivalent of ext2_put_page() is open coded on a page which was returned from the ext2_get_page() call [through the use of ext2_find_entry() and ext2_dotdot()]. Move ext2_put_page() to ext2.h and use it in namei.c Also add a comment regarding the proper way to release the page returned from ext2_find_entry() and ext2_dotdot(). Link: https://lore.kernel.org/r/20201112174244.701325-1-ira.weiny@intel.com Signed-off-by: Ira Weiny Signed-off-by: Jan Kara --- fs/ext2/dir.c | 14 ++++++++------ fs/ext2/ext2.h | 7 +++++++ fs/ext2/namei.c | 15 +++++---------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 70355ab6740e..14aa45316ad2 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -66,12 +66,6 @@ static inline unsigned ext2_chunk_size(struct inode *inode) return inode->i_sb->s_blocksize; } -static inline void ext2_put_page(struct page *page) -{ - kunmap(page); - put_page(page); -} - /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. @@ -336,6 +330,8 @@ ext2_readdir(struct file *file, struct dir_context *ctx) * returns the page in which the entry was found (as a parameter - res_page), * and the entry itself. Page is returned mapped and unlocked. * Entry is guaranteed to be valid. + * + * On Success ext2_put_page() should be called on *res_page. */ struct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir, const struct qstr *child, struct page **res_page) @@ -401,6 +397,12 @@ struct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir, return de; } +/** + * Return the '..' directory entry and the page in which the entry was found + * (as a parameter - p). + * + * On Success ext2_put_page() should be called on *p. + */ struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) { struct page *page = ext2_get_page(dir, 0, 0); diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 5136b7289e8d..2a4175fbaf5e 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include /* XXX Here for now... not interested in restructing headers JUST now */ @@ -745,6 +747,11 @@ extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *); extern int ext2_empty_dir (struct inode *); extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **); extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *, int); +static inline void ext2_put_page(struct page *page) +{ + kunmap(page); + put_page(page); +} /* ialloc.c */ extern struct inode * ext2_new_inode (struct inode *, umode_t, const struct qstr *); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 5bf2c145643b..ea980f1e2e99 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -389,23 +389,18 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, if (dir_de) { if (old_dir != new_dir) ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0); - else { - kunmap(dir_page); - put_page(dir_page); - } + else + ext2_put_page(dir_page); inode_dec_link_count(old_dir); } return 0; out_dir: - if (dir_de) { - kunmap(dir_page); - put_page(dir_page); - } + if (dir_de) + ext2_put_page(dir_page); out_old: - kunmap(old_page); - put_page(old_page); + ext2_put_page(old_page); out: return err; } From 8fca3c8a3451514c6f20dd26d5e66e78220d16e3 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 20 Nov 2020 12:28:25 -0600 Subject: [PATCH 07/10] ext2: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of just letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Link: https://lore.kernel.org/r/73d8ae2d06d639815672ee9ee4550ea4bfa08489.1605896059.git.gustavoars@kernel.org Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jan Kara --- fs/ext2/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 11c5c6fe75bb..78c417d3c898 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1256,6 +1256,7 @@ static void __ext2_truncate_blocks(struct inode *inode, loff_t offset) mark_inode_dirty(inode); ext2_free_branches(inode, &nr, &nr+1, 3); } + break; case EXT2_TIND_BLOCK: ; } From d24396c5290ba8ab04ba505176874c4e04a2d53c Mon Sep 17 00:00:00 2001 From: Rustam Kovhaev Date: Sun, 1 Nov 2020 06:09:58 -0800 Subject: [PATCH 08/10] reiserfs: add check for an invalid ih_entry_count when directory item has an invalid value set for ih_entry_count it might trigger use-after-free or out-of-bounds read in bin_search_in_dir_item() ih_entry_count * IH_SIZE for directory item should not be larger than ih_item_len Link: https://lore.kernel.org/r/20201101140958.3650143-1-rkovhaev@gmail.com Reported-and-tested-by: syzbot+83b6f7cf9922cae5c4d7@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=83b6f7cf9922cae5c4d7 Signed-off-by: Rustam Kovhaev Signed-off-by: Jan Kara --- fs/reiserfs/stree.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index 8bf88d690729..476a7ff49482 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c @@ -454,6 +454,12 @@ static int is_leaf(char *buf, int blocksize, struct buffer_head *bh) "(second one): %h", ih); return 0; } + if (is_direntry_le_ih(ih) && (ih_item_len(ih) < (ih_entry_count(ih) * IH_SIZE))) { + reiserfs_warning(NULL, "reiserfs-5093", + "item entry count seems wrong %h", + ih); + return 0; + } prev_location = ih_location(ih); } From e51d68e76d604c6d5d1eb13ae1d6da7f6c8c0dfc Mon Sep 17 00:00:00 2001 From: Anant Thazhemadam Date: Wed, 9 Dec 2020 01:13:38 +0530 Subject: [PATCH 09/10] 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(-) 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; From f7387170339afb473a0d95b7732f904346f9795e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 28 Sep 2020 14:26:13 +0200 Subject: [PATCH 10/10] writeback: don't warn on an unregistered BDI in __mark_inode_dirty BDIs get unregistered during device removal, and this WARN can be trivially triggered by hot-removing a NVMe device while running fsx It is otherwise harmless as we still hold a BDI reference, and the writeback has been shut down already. Link: https://lore.kernel.org/r/20200928122613.434820-1-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/fs-writeback.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index e6005c78bfa9..acfb55834af2 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -2321,10 +2321,6 @@ void __mark_inode_dirty(struct inode *inode, int flags) wb = locked_inode_to_wb_and_lock_list(inode); - WARN((wb->bdi->capabilities & BDI_CAP_WRITEBACK) && - !test_bit(WB_registered, &wb->state), - "bdi-%s not registered\n", bdi_dev_name(wb->bdi)); - inode->dirtied_when = jiffies; if (dirtytime) inode->dirtied_time_when = jiffies;