Skip to content

Commit

Permalink
Relax the rw_verify_area() error checking.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Cc: Ulrich Drepper <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: Al Viro <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Linus Torvalds committed Jan 5, 2006
1 parent a020ff4 commit e28cc71
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 12 deletions.
4 changes: 2 additions & 2 deletions arch/mips/kernel/linux32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion fs/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
34 changes: 25 additions & 9 deletions fs/read_write.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -182,22 +183,33 @@ asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high,
}
#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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down

0 comments on commit e28cc71

Please sign in to comment.