Relax the rw_verify_area() error checking.
authorLinus Torvalds <torvalds@g5.osdl.org>
Thu, 5 Jan 2006 00:20:40 +0000 (16:20 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 5 Jan 2006 00:20:40 +0000 (16:20 -0800)
In particular, allow over-large read- or write-requests to be downgraded
to a more reasonable range, rather than considering them outright errors.

We want to protect lower layers from (the sadly all too common) overflow
conditions, but prefer to do so by chopping the requests up, rather than
just refusing them outright.

Cc: Peter Anvin <hpa@zytor.com>
Cc: Ulrich Drepper <drepper@redhat.com>
Cc: Andi Kleen <ak@suse.de>
Cc: Al Viro <viro@ftp.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/mips/kernel/linux32.c
fs/compat.c
fs/read_write.c

index 330cf84d21feaf298636fe09b6d9be10d2637d03..60353f5acc48a42794bc9f5d19635a507e638176 100644 (file)
@@ -420,7 +420,7 @@ asmlinkage ssize_t sys32_pread(unsigned int fd, char * buf,
                goto out;
        pos = merge_64(a4, a5);
        ret = rw_verify_area(READ, file, &pos, count);
-       if (ret)
+       if (ret < 0)
                goto out;
        ret = -EINVAL;
        if (!file->f_op || !(read = file->f_op->read))
@@ -455,7 +455,7 @@ asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char * buf,
                goto out;
        pos = merge_64(a4, a5);
        ret = rw_verify_area(WRITE, file, &pos, count);
-       if (ret)
+       if (ret < 0)
                goto out;
        ret = -EINVAL;
        if (!file->f_op || !(write = file->f_op->write))
index 818634120b693c70c4e8fedc85c925e12f5d1482..55ac0324aaf1649f1326994f31267ad90e542063 100644 (file)
@@ -1170,7 +1170,7 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
        }
 
        ret = rw_verify_area(type, file, pos, tot_len);
-       if (ret)
+       if (ret < 0)
                goto out;
 
        fnv = NULL;
index a091ee4f430df6cc83317b59c4a2fc363c005080..df3468a22fea498889a5af4523556439d9f880bf 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/security.h>
 #include <linux/module.h>
 #include <linux/syscalls.h>
+#include <linux/pagemap.h>
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -182,22 +183,33 @@ bad:
 }
 #endif
 
+/*
+ * rw_verify_area doesn't like huge counts. We limit
+ * them to something that fits in "int" so that others
+ * won't have to do range checks all the time.
+ */
+#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
 
 int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
 {
        struct inode *inode;
        loff_t pos;
 
-       if (unlikely(count > INT_MAX))
+       if (unlikely((ssize_t) count < 0))
                goto Einval;
        pos = *ppos;
        if (unlikely((pos < 0) || (loff_t) (pos + count) < 0))
                goto Einval;
 
        inode = file->f_dentry->d_inode;
-       if (inode->i_flock && MANDATORY_LOCK(inode))
-               return locks_mandatory_area(read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, pos, count);
-       return 0;
+       if (inode->i_flock && MANDATORY_LOCK(inode)) {
+               int retval = locks_mandatory_area(
+                       read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+                       inode, file, pos, count);
+               if (retval < 0)
+                       return retval;
+       }
+       return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
 
 Einval:
        return -EINVAL;
@@ -244,7 +256,8 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
                return -EFAULT;
 
        ret = rw_verify_area(READ, file, pos, count);
-       if (!ret) {
+       if (ret >= 0) {
+               count = ret;
                ret = security_file_permission (file, MAY_READ);
                if (!ret) {
                        if (file->f_op->read)
@@ -295,7 +308,8 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
                return -EFAULT;
 
        ret = rw_verify_area(WRITE, file, pos, count);
-       if (!ret) {
+       if (ret >= 0) {
+               count = ret;
                ret = security_file_permission (file, MAY_WRITE);
                if (!ret) {
                        if (file->f_op->write)
@@ -497,7 +511,7 @@ static ssize_t do_readv_writev(int type, struct file *file,
        }
 
        ret = rw_verify_area(type, file, pos, tot_len);
-       if (ret)
+       if (ret < 0)
                goto out;
        ret = security_file_permission(file, type == READ ? MAY_READ : MAY_WRITE);
        if (ret)
@@ -653,8 +667,9 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
                if (!(in_file->f_mode & FMODE_PREAD))
                        goto fput_in;
        retval = rw_verify_area(READ, in_file, ppos, count);
-       if (retval)
+       if (retval < 0)
                goto fput_in;
+       count = retval;
 
        retval = security_file_permission (in_file, MAY_READ);
        if (retval)
@@ -674,8 +689,9 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
                goto fput_out;
        out_inode = out_file->f_dentry->d_inode;
        retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count);
-       if (retval)
+       if (retval < 0)
                goto fput_out;
+       count = retval;
 
        retval = security_file_permission (out_file, MAY_WRITE);
        if (retval)