diff mbox series

[1/2] quota: Don't overflow quota file offsets

Message ID 20201102172733.23444-2-jack@suse.cz (mailing list archive)
State New, archived
Headers show
Series quota: Handle corrupted quota file better | expand

Commit Message

Jan Kara Nov. 2, 2020, 5:27 p.m. UTC
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...

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/quota/quota_tree.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Comments

Andreas Dilger Nov. 2, 2020, 9:30 p.m. UTC | #1
On Nov 2, 2020, at 10:27 AM, Jan Kara <jack@suse.cz> wrote:
> 
> 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...
> 
> Signed-off-by: Jan Kara <jack@suse.cz>

Out of curiosity, is this 26 million *quota entries*, or is it just a UID
larger than 26M?  At one point the quota files were sparse and indexed by
the UID, but I guess very file name "quota tree" means this is not correct.
Is there some document/comment that describes the on-disk quota file format?

In any case, the change makes sense regardless, since ->quota_read() takes
loff_t for the offset.

Reviewed-by: Andreas Dilger <adilger@dilger.ca>

> ---
> 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:
> --
> 2.16.4
> 


Cheers, Andreas
Jan Kara Nov. 3, 2020, 8:57 a.m. UTC | #2
On Mon 02-11-20 14:30:37, Andreas Dilger wrote:
> On Nov 2, 2020, at 10:27 AM, Jan Kara <jack@suse.cz> wrote:
> > 
> > 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...
> > 
> > Signed-off-by: Jan Kara <jack@suse.cz>
> 
> Out of curiosity, is this 26 million *quota entries*, or is it just a UID
> larger than 26M?  At one point the quota files were sparse and indexed by
> the UID, but I guess very file name "quota tree" means this is not correct.
> Is there some document/comment that describes the on-disk quota file format?

It is really 26M different UIDs/GIDs/ProjectIDs. The sparse file format you
describe is the original quota format (implemented by fs/quota/quota_v1.c)
that the current format superseeded sometime in 1999 or so ;). The current
format uses radix tree for structure lookup and then structures are just
stored in a linear array. There's documentation of the format in
quota-tools in doc/quotadoc.sgml.

> In any case, the change makes sense regardless, since ->quota_read() takes
> loff_t for the offset.
> 
> Reviewed-by: Andreas Dilger <adilger@dilger.ca>

Thanks!

									Honza
> 
> > ---
> > 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:
> > --
> > 2.16.4
> > 
> 
> 
> Cheers, Andreas
> 
> 
> 
> 
>
diff mbox series

Patch

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: