Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 81295 - Kernel: Read/write VFS range checking issues (GENERIC-MAP-NOMATCH)
Summary: Kernel: Read/write VFS range checking issues (GENERIC-MAP-NOMATCH)
Status: RESOLVED FIXED
Alias: None
Product: Gentoo Security
Classification: Unclassified
Component: Kernel (show other bugs)
Hardware: All All
: High normal (vote)
Assignee: Gentoo Security
URL:
Whiteboard: [linux <2.4.30]
Keywords:
Depends on:
Blocks:
 
Reported: 2005-02-08 13:17 UTC by Sune Kloppenborg Jeppesen (RETIRED)
Modified: 2009-07-12 19:47 UTC (History)
1 user (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
Patch (2.4-81295.patch,8.33 KB, patch)
2005-04-15 13:56 UTC, Tim Yamin (RETIRED)
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Sune Kloppenborg Jeppesen (RETIRED) gentoo-dev 2005-02-08 13:17:14 UTC
This is a pretty straight backport of Linus f_maxcount plus ppos+count range
verification code which has been added to v2.6. 

Even though the known sign handling problems in v2.6 are not present in v2.4,
this change makes it very hard for unknown sign handling bugs in VFS read/write
methods to be exploited.

This will go in v2.4.30-pre1 which is going out ASAP.


diff -Nur --exclude=cscope.out linux-2.4.29.orig/arch/mips64/kernel/linux32.c linux-2.4.29/arch/mips64/kernel/linux32.c
--- linux-2.4.29.orig/arch/mips64/kernel/linux32.c      2005-02-07 14:34:22.000000000 -0200
+++ linux-2.4.29/arch/mips64/kernel/linux32.c   2005-02-07 17:39:42.952855072 -0200
@@ -1088,11 +1088,9 @@
                i--;
        }
 
-       inode = file->f_dentry->d_inode;
        /* VERIFY_WRITE actually means a read, as we write to user space */
-       retval = locks_verify_area((type == VERIFY_WRITE
-                                   ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
-                                  inode, file, file->f_pos, tot_len);
+       retval = rw_verify_area((type == VERIFY_WRITE ? READ : WRITE),
+                                  file, &file->f_pos, tot_len);
        if (retval) {
                if (iov != iovstack)
                        kfree(iov);
diff -Nur --exclude=cscope.out linux-2.4.29.orig/arch/parisc/kernel/sys_parisc32.c linux-2.4.29/arch/parisc/kernel/sys_parisc32.c
--- linux-2.4.29.orig/arch/parisc/kernel/sys_parisc32.c 2005-02-07 14:34:22.000000000 -0200
+++ linux-2.4.29/arch/parisc/kernel/sys_parisc32.c      2005-02-07 17:39:59.097400728 -0200
@@ -1671,11 +1671,9 @@
                i--;
        }
 
-       inode = file->f_dentry->d_inode;
        /* VERIFY_WRITE actually means a read, as we write to user space */
-       retval = locks_verify_area((type == VERIFY_WRITE
-                                   ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
-                                  inode, file, file->f_pos, tot_len);
+       retval = rw_verify_area((type == VERIFY_WRITE ? READ : WRITE),
+                                  file, &file->f_pos, tot_len);
        if (retval) {
                if (iov != iovstack)
                        kfree(iov);
diff -Nur --exclude=cscope.out linux-2.4.29.orig/arch/ppc64/kernel/sys_ppc32.c linux-2.4.29/arch/ppc64/kernel/sys_ppc32.c
--- linux-2.4.29.orig/arch/ppc64/kernel/sys_ppc32.c     2005-02-07 14:34:22.000000000 -0200
+++ linux-2.4.29/arch/ppc64/kernel/sys_ppc32.c  2005-02-07 17:40:23.377709560 -0200
@@ -183,11 +183,9 @@
                i--;
        }
 
-       inode = file->f_dentry->d_inode;
        /* VERIFY_WRITE actually means a read, as we write to user space */
-       retval = locks_verify_area((type == VERIFY_WRITE
-                                   ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
-                                  inode, file, file->f_pos, tot_len);
+       retval = rw_verify_area((type == VERIFY_WRITE ? READ : WRITE),
+                                  file, &file->f_pos, tot_len);
        if (retval) {
                if (iov != iovstack)
                        kfree(iov);
diff -Nur --exclude=cscope.out linux-2.4.29.orig/arch/s390x/kernel/linux32.c linux-2.4.29/arch/s390x/kernel/linux32.c
--- linux-2.4.29.orig/arch/s390x/kernel/linux32.c       2005-02-07 14:34:22.000000000 -0200
+++ linux-2.4.29/arch/s390x/kernel/linux32.c    2005-02-07 17:41:11.429404592 -0200
@@ -1108,7 +1108,6 @@
        unsigned long tot_len;
        struct iovec iovstack[UIO_FASTIOV];
        struct iovec *iov=iovstack, *ivp;
-       struct inode *inode;
        long retval, i;
        io_fn_t fn;
        iov_fn_t fnv;
@@ -1145,11 +1144,9 @@
                i--;
        }
 
-       inode = file->f_dentry->d_inode;
        /* VERIFY_WRITE actually means a read, as we write to user space */
-       retval = locks_verify_area((type == VERIFY_WRITE
-                                   ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
-                                  inode, file, file->f_pos, tot_len);
+       retval = rw_verify_area((type == VERIFY_WRITE ? READ : WRITE),
+                                  file, &file->f_pos, tot_len);
        if (retval)
                goto out;
 
diff -Nur --exclude=cscope.out linux-2.4.29.orig/arch/sparc64/kernel/sys_sparc32.c linux-2.4.29/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.29.orig/arch/sparc64/kernel/sys_sparc32.c 2005-02-07 14:34:22.000000000 -0200
+++ linux-2.4.29/arch/sparc64/kernel/sys_sparc32.c      2005-02-07 17:41:42.027752936 -0200
@@ -1093,7 +1093,6 @@
        __kernel_ssize_t32 tot_len;
        struct iovec iovstack[UIO_FASTIOV];
        struct iovec *iov=iovstack, *ivp;
-       struct inode *inode;
        long retval, i;
        io_fn_t fn;
        iov_fn_t fnv;
@@ -1140,11 +1139,9 @@
                i--;
        }
 
-       inode = file->f_dentry->d_inode;
        /* VERIFY_WRITE actually means a read, as we write to user space */
-       retval = locks_verify_area((type == VERIFY_WRITE
-                                   ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
-                                  inode, file, file->f_pos, tot_len);
+       retval = rw_verify_area((type == VERIFY_WRITE ? READ : WRITE),
+                                  file, &file->f_pos, tot_len);
        if (retval)
                goto out;
 
diff -Nur --exclude=cscope.out linux-2.4.29.orig/fs/file_table.c linux-2.4.29/fs/file_table.c
--- linux-2.4.29.orig/fs/file_table.c   2005-02-07 14:34:21.000000000 -0200
+++ linux-2.4.29/fs/file_table.c        2005-02-07 15:35:27.000000000 -0200
@@ -46,6 +46,7 @@
                f->f_version = ++event;
                f->f_uid = current->fsuid;
                f->f_gid = current->fsgid;
+               f->f_maxcount = INT_MAX;
                list_add(&f->f_list, &anon_list);
                file_list_unlock();
                return f;
diff -Nur --exclude=cscope.out linux-2.4.29.orig/fs/read_write.c linux-2.4.29/fs/read_write.c
--- linux-2.4.29.orig/fs/read_write.c   2005-02-07 14:34:21.000000000 -0200
+++ linux-2.4.29/fs/read_write.c        2005-02-07 17:35:32.965858872 -0200
@@ -40,6 +40,28 @@
        return -EISDIR;
 }
 
+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 > file->f_maxcount))
+               return -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, *ppos, count);
+       return 0;
+
+Einval:
+       return -EINVAL;
+}
+
 loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
 {
        long long retval;
@@ -168,8 +190,8 @@
        file = fget(fd);
        if (file) {
                if (file->f_mode & FMODE_READ) {
-                       ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode,
-                                               file, file->f_pos, count);
+                       ret = rw_verify_area(READ, file, &file->f_pos, count);
+
                        if (!ret) {
                                ssize_t (*read)(struct file *, char *, size_t, loff_t *);
                                ret = -EINVAL;
@@ -193,9 +215,7 @@
        file = fget(fd);
        if (file) {
                if (file->f_mode & FMODE_WRITE) {
-                       struct inode *inode = file->f_dentry->d_inode;
-                       ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
-                               file->f_pos, count);
+                       ret = rw_verify_area(WRITE, file, &file->f_pos, count);
                        if (!ret) {
                                ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
                                ret = -EINVAL;
@@ -224,7 +244,6 @@
        ssize_t ret, i;
        io_fn_t fn;
        iov_fn_t fnv;
-       struct inode *inode;
 
        /*
         * First get the "struct iovec" from user memory and
@@ -275,12 +294,11 @@
                        goto out;
        }
 
-       inode = file->f_dentry->d_inode;
        /* VERIFY_WRITE actually means a read, as we write to user space */
-       ret = locks_verify_area((type == VERIFY_WRITE
-                                ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
-                               inode, file, file->f_pos, tot_len);
-       if (ret) goto out;
+       ret = rw_verify_area((type == VERIFY_WRITE ? READ : WRITE),
+                               file, &file->f_pos, tot_len);
+       if (ret) 
+               goto out;
 
        fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev);
        if (fnv) {
@@ -383,8 +401,8 @@
                goto bad_file;
        if (!(file->f_mode & FMODE_READ))
                goto out;
-       ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode,
-                               file, pos, count);
+       ret = rw_verify_area(READ, file, &pos, count);
+
        if (ret)
                goto out;
        ret = -EINVAL;
@@ -414,8 +432,8 @@
                goto bad_file;
        if (!(file->f_mode & FMODE_WRITE))
                goto out;
-       ret = locks_verify_area(FLOCK_VERIFY_WRITE, file->f_dentry->d_inode,
-                               file, pos, count);
+       ret = rw_verify_area(WRITE, file, &pos, count);
+
        if (ret)
                goto out;
        ret = -EINVAL;
diff -Nur --exclude=cscope.out linux-2.4.29.orig/include/linux/fs.h linux-2.4.29/include/linux/fs.h
--- linux-2.4.29.orig/include/linux/fs.h        2005-02-07 14:34:25.000000000 -0200
+++ linux-2.4.29/include/linux/fs.h     2005-02-07 16:30:59.323741984 -0200
@@ -576,6 +576,7 @@
        unsigned int            f_uid, f_gid;
        int                     f_error;
 
+       size_t                  f_maxcount;
        unsigned long           f_version;
 
        /* needed for tty driver, and maybe others */
@@ -1056,14 +1057,7 @@
        return 0;
 }
 
-static inline int locks_verify_area(int read_write, struct inode *inode,
-                                   struct file *filp, loff_t offset,
-                                   size_t count)
-{
-       if (inode->i_flock && MANDATORY_LOCK(inode))
-               return locks_mandatory_area(read_write, inode, filp, offset, count);
-       return 0;
-}
+extern int rw_verify_area(int, struct file *, loff_t *, size_t);
 
 static inline int locks_verify_truncate(struct inode *inode,
                                    struct file *filp,
diff -Nur --exclude=cscope.out linux-2.4.29.orig/mm/filemap.c linux-2.4.29/mm/filemap.c
--- linux-2.4.29.orig/mm/filemap.c      2005-02-07 14:34:21.000000000 -0200
+++ linux-2.4.29/mm/filemap.c   2005-02-07 17:26:33.927805112 -0200
@@ -1870,7 +1870,7 @@
                goto fput_in;
        if (!in_inode->i_mapping->a_ops->readpage)
                goto fput_in;
-       retval = locks_verify_area(FLOCK_VERIFY_READ, in_inode, in_file, in_file->f_pos, count);
+       retval = rw_verify_area(READ, in_file, &in_file->f_pos, count);
        if (retval)
                goto fput_in;
 
@@ -1887,7 +1887,7 @@
        if (!out_file->f_op || !out_file->f_op->write)
                goto fput_out;
        out_inode = out_file->f_dentry->d_inode;
-       retval = locks_verify_area(FLOCK_VERIFY_WRITE, out_inode, out_file, out_file->f_pos, count);
+       retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count);
        if (retval)
                goto fput_out;
Comment 1 Thierry Carrez (RETIRED) gentoo-dev 2005-03-08 08:25:35 UTC
Public from Connectiva CLA-2005:930

Original 2.6 patch here:
http://linux.bkbits.net:8080/linux-2.6/patch@1.1966.1.67
http://linux.bkbits.net:8080/linux-2.6/patch@1.1966.1.68

From Alan Cox: 
Actually I think [this patch] raises bigger problems that it fixes. It can break
existing applications and it may leave 64bit machines without posix compliance in the 2.4 case.
Comment 2 Thierry Carrez (RETIRED) gentoo-dev 2005-03-08 08:29:33 UTC
In fact these fixes were introduced by Linux as a higher-level protection against flaws discovered by Georgi Guninski treated in bug 82141.
Comment 3 Thierry Carrez (RETIRED) gentoo-dev 2005-03-16 03:16:46 UTC
Mass-Ccing kern-sec@gentoo.org to make sure Kernel Security guys know about all
of these...
Comment 4 Tim Yamin (RETIRED) gentoo-dev 2005-04-15 13:56:41 UTC
Created attachment 56379 [details, diff]
Patch
Comment 5 Joshua Kinard gentoo-dev 2005-04-23 22:31:22 UTC
mips-sources fixed.
Comment 6 Tim Yamin (RETIRED) gentoo-dev 2005-08-20 11:27:45 UTC
All fixed, closing bug.