Re: [PATCH 1/3] quota: Check next/prev free block number after reading from quota file

From: Zhihao Cheng
Date: Thu Sep 22 2022 - 04:14:10 EST


在 2022/9/21 21:37, Jan Kara 写道:
Hi Jan,
On Sat 20-08-22 19:05:12, Zhihao Cheng wrote:
Following process:
[...]

Link: https://bugzilla.kernel.org/show_bug.cgi?id=216372
Fixes: 1da177e4c3f4152 ("Linux-2.6.12-rc2")

It's better to just have:

CC: stable@xxxxxxxxxxxxxxx

here. Fixes tag pointing to kernel release is not very useful.
Will add in v2.

--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -71,6 +71,35 @@ static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
return ret;
}
+static inline int do_check_range(struct super_block *sb, uint val, uint max_val)
+{
+ if (val >= max_val) {
+ quota_error(sb, "Getting block too big (%u >= %u)",
+ val, max_val);
+ return -EUCLEAN;
+ }
+
+ return 0;
+}

I'd already provide min_val and the string for the message here as well (as
you do in patch 2). It is less churn in the next patch and free blocks
checking actually needs that as well. See below.

+
+static int check_free_block(struct qtree_mem_dqinfo *info,
+ struct qt_disk_dqdbheader *dh)
+{
+ int err = 0;
+ uint nextblk, prevblk;
+
+ nextblk = le32_to_cpu(dh->dqdh_next_free);
+ err = do_check_range(info->dqi_sb, nextblk, info->dqi_blocks);
+ if (err)
+ return err;
+ prevblk = le32_to_cpu(dh->dqdh_prev_free);
+ err = do_check_range(info->dqi_sb, prevblk, info->dqi_blocks);
+ if (err)
+ return err;

The free block should actually be > QT_TREEOFF so I'd add the check to
do_check_range().

'dh->dqdh_next_free' may be updated when quota entry removed, 'dh->dqdh_next_free' can be used for next new quota entris.
Before sending v2, I found 'dh->dqdh_next_free' and 'dh->dqdh_prev_free' can easily be zero in newly allocated blocks when continually creating files onwed by different users:
find_free_dqentry
get_free_dqblk
write_blk(info, info->dqi_blocks, buf) // zero'd qt_disk_dqdbheader
blk = info->dqi_blocks++ // allocate new one block
info->dqi_free_entry = blk // will be used for new quota entries

find_free_dqentry
if (info->dqi_free_entry)
blk = info->dqi_free_entry
read_blk(info, blk, buf) // dh->dqdh_next_free = dh->dqdh_prev_free = 0

I think it's normal when 'dh->dqdh_next_free' or 'dh->dqdh_prev_free' equals to 0.

Also rather than having check_free_block(), I'd provide a helper function
like check_dquot_block_header() which will check only free blocks pointers
now and in later patches you can add other checks there.
OK, will be updated in v2.

Honza