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;
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.
In fact these fixes were introduced by Linux as a higher-level protection against flaws discovered by Georgi Guninski treated in bug 82141.
Mass-Ccing kern-sec@gentoo.org to make sure Kernel Security guys know about all of these...
Created attachment 56379 [details, diff] Patch
mips-sources fixed.
All fixed, closing bug.