Re: [PATCH] Typecasting required for comparing unlike datatypes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 12/16/2010 05:54 AM, KAMEZAWA Hiroyuki wrote:
On Wed, 15 Dec 2010 09:50:24 +0000
Al Viro<viro@xxxxxxxxxxxxxxxxxx>  wrote:

On Fri, Dec 10, 2010 at 05:18:51PM +0900, KAMEZAWA Hiroyuki wrote:
On Wed,  8 Dec 2010 18:25:00 +0530
Harsh Prateek Bora<harsh@xxxxxxxxxxxxxxxxxx>  wrote:

The existing code causes the if condition to pass when it should fail
on a *64-bit kernel* because of implicit data type conversions. It can
be observed by passing pos = -1 and count = some positive number.
This results in function returning EOVERFLOW instead of EINVAL.

With this patch, the function returns EINVAL when pos is -1 and count
is a positive number. This can be tested by calling sendfile with
offset = -1 and count = some positive number on a 64-bit kernel.

Signed-off-by: Harsh Prateek Bora<harsh@xxxxxxxxxxxxxxxxxx>

Acked-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@xxxxxxxxxxxxxx>

I'm sorry for annoying you.

NAK.  Look at what's getting done there; this check is the _only_ place
in function that uses count, it is constantly false if count is zero
_and_ we have only one caller that passes non-zero.  Moreover, in case of
count being zero we ignore offset as well.

Obvious things to do:
	a) take the check in question into the only caller that would
pass non-zero count (i.e. rw_verify_area())
	b) lose the second and the third arguments of __negative_fpos_check()
	c) sort out what the hell should be done in that place.

Note that current logics is very convoluted.  First of all, we rely on
loff_t being long long in the kernel and size_t being not bigger than
unsigned long long.  See ((loff_t)(pos + count)<  0) in there - if count
gets wider than pos, we suddenly get all kinds of odd crap.

That's OK; if we run into ports with size_t wider than 64 bits, we'll have
more trouble anyway.

So what we have for signed offset case is pos>= 0, count<= max loff_t - pos.
Fair enough.  For unsigned offset case it's more interesting - we want to
allow pos<  0, and we just want to prohibit wraparounds from negative to
positive.  IOW, it's pos>= 0 || count<  -pos; note that we already have
count<= max ssize_t, which we assume to be no more than max loff_t.

So what we need is this:
	if (unlikely(pos<  0)) {
		if it's signed
			-EINVAL
		if (count>= -pos) /* both values are in 0..LLONG_MAX */
			-EOVERFLOW
	} else if (unlikely((loff_t)(pos + count)<  0)) {
		if it's signed
			 -EINVAL
	}
and we are done with that.  IOW, how about the patch below?


Signed-off-by: Al Viro<viro@xxxxxxxxxxxxxxxxxx>

This seems much easier to read. thank you.

Acked-by: KAMEZAWA Hiroyuki<kamezawa.hiroyu@xxxxxxxxxxxxxx>

and sorry for my 1st implemenation.

---
diff --git a/fs/read_write.c b/fs/read_write.c
index 5d431ba..5520f8a 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -30,18 +30,9 @@ const struct file_operations generic_ro_fops = {

  EXPORT_SYMBOL(generic_ro_fops);

-static int
-__negative_fpos_check(struct file *file, loff_t pos, size_t count)
+static inline int unsigned_offsets(struct file *file)
  {
-	/*
-	 * pos or pos+count is negative here, check overflow.
-	 * too big "count" will be caught in rw_verify_area().
-	 */
-	if ((pos<  0)&&  (pos + count<  pos))
-		return -EOVERFLOW;
-	if (file->f_mode&  FMODE_UNSIGNED_OFFSET)
-		return 0;
-	return -EINVAL;
+	return file->f_mode&  FMODE_UNSIGNED_OFFSET;
  }

  /**
@@ -75,7 +66,7 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin)
  		break;
  	}

-	if (offset<  0&&  __negative_fpos_check(file, offset, 0))
+	if (offset<  0&&  !unsigned_offsets(file))
  		return -EINVAL;
  	if (offset>  inode->i_sb->s_maxbytes)
  		return -EINVAL;
@@ -152,7 +143,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin)
  			offset += file->f_pos;
  	}
  	retval = -EINVAL;
-	if (offset>= 0 || !__negative_fpos_check(file, offset, 0)) {
+	if (offset>= 0 || unsigned_offsets(file)) {
  		if (offset != file->f_pos) {
  			file->f_pos = offset;
  			file->f_version = 0;
@@ -252,9 +243,13 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count
  	if (unlikely((ssize_t) count<  0))
  		return retval;
  	pos = *ppos;
-	if (unlikely((pos<  0) || (loff_t) (pos + count)<  0)) {
-		retval = __negative_fpos_check(file, pos, count);
-		if (retval)
+	if (unlikely(pos<  0)) {
+		if (!unsigned_offsets(file))
+			return retval;
+		if (count>= -pos) /* both values are in 0..LLONG_MAX */
Yeah .. thats a better reorg of code, however I am not sure if we need a typecast above for future portability reasons (as count and pos can be of diff width somewhere/sometime). Also, I personally would like to keep the function name as is_offset_unsigned().
Anyways, +1 for ACK !

Regards,
Harsh
+			return -EOVERFLOW;
+	} else if (unlikely((loff_t) (pos + count)<  0)) {
+		if (!unsigned_offsets(file))
  			return retval;
  	}



--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux