[PATCH] fix offset checks in do_sendfile to use unsigned values (try #2)

From: Jeff Layton
Date: Wed Jul 22 2009 - 08:36:22 EST


This is the second version of this patch. Some of the checks do need
to use signed values. This patch should be more correct in that regard.
This also adds a check to make sure that "pos + count" doesn't
overflow.

If do_sendfile is called with a "max" value of 0, it grabs the lesser
s_maxbytes value of the two superblocks to use instead. There's a
problem here however. s_maxbytes is an unsigned long long and it gets
cast to a signed value. If both s_maxbytes values are large enough, max
will end up being negative and the comparisons in this code won't work
correctly.

Change do_sendfile to use unsigned values internally for the offset
checks against "max".

Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
fs/read_write.c | 21 ++++++++++++---------
1 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/fs/read_write.c b/fs/read_write.c
index 6c8c55d..2c5b402 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -788,11 +788,11 @@ SYSCALL_DEFINE5(pwritev, unsigned long, fd, const struct iovec __user *, vec,
}

static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
- size_t count, loff_t max)
+ size_t count, unsigned long long max)
{
struct file * in_file, * out_file;
struct inode * in_inode, * out_inode;
- loff_t pos;
+ unsigned long long pos, newpos;
ssize_t retval;
int fput_needed_in, fput_needed_out, fl;

@@ -835,14 +835,16 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
goto fput_out;
count = retval;

- if (!max)
- max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes);
-
- pos = *ppos;
retval = -EINVAL;
- if (unlikely(pos < 0))
+ if (unlikely(*ppos < 0))
goto fput_out;
- if (unlikely(pos + count > max)) {
+
+ if (!max)
+ max = min(in_inode->i_sb->s_maxbytes,
+ out_inode->i_sb->s_maxbytes);
+ pos = (unsigned long long) *ppos;
+ newpos = pos + count;
+ if (unlikely(newpos > max || newpos < count)) {
retval = -EOVERFLOW;
if (pos >= max)
goto fput_out;
@@ -869,7 +871,8 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,

inc_syscr(current);
inc_syscw(current);
- if (*ppos > max)
+ pos = (unsigned long long) *ppos;
+ if (pos > max)
retval = -EOVERFLOW;

fput_out:
--
1.6.2.5


--=-F43uCi3SlHVZxYj2YkbQ--

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