dect
/
linux-2.6
Archived
13
0
Fork 0

readv/writev: do the same MAX_RW_COUNT truncation that read/write does

We used to protect against overflow, but rather than return an error, do
what read/write does, namely to limit the total size to MAX_RW_COUNT.
This is not only more consistent, but it also means that any broken
low-level read/write routine that still keeps counts in 'int' can't
break.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2010-10-29 10:36:49 -07:00
parent f56f44001c
commit 435f49a518
3 changed files with 40 additions and 35 deletions

View File

@ -606,14 +606,14 @@ ssize_t compat_rw_copy_check_uvector(int type,
/*
* Single unix specification:
* We should -EINVAL if an element length is not >= 0 and fitting an
* ssize_t. The total length is fitting an ssize_t
* ssize_t.
*
* Be careful here because iov_len is a size_t not an ssize_t
* In Linux, the total length is limited to MAX_RW_COUNT, there is
* no overflow possibility.
*/
tot_len = 0;
ret = -EINVAL;
for (seg = 0; seg < nr_segs; seg++) {
compat_ssize_t tmp = tot_len;
compat_uptr_t buf;
compat_ssize_t len;
@ -624,13 +624,13 @@ ssize_t compat_rw_copy_check_uvector(int type,
}
if (len < 0) /* size_t not fitting in compat_ssize_t .. */
goto out;
tot_len += len;
if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
goto out;
if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
ret = -EFAULT;
goto out;
}
if (len > MAX_RW_COUNT - tot_len)
len = MAX_RW_COUNT - tot_len;
tot_len += len;
iov->iov_base = compat_ptr(buf);
iov->iov_len = (compat_size_t) len;
uvector++;

View File

@ -243,8 +243,6 @@ bad:
* 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;
@ -624,6 +622,9 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
* if an element length is < 0 when cast to ssize_t or if the
* total length would overflow the ssize_t return value of the
* system call.
*
* Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
* overflow case.
*/
ret = 0;
for (seg = 0; seg < nr_segs; seg++) {
@ -632,7 +633,7 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
/* see if we we're about to use an invalid len or if
* it's about to overflow ssize_t */
if (len < 0 || (ret + len < ret)) {
if (len < 0) {
ret = -EINVAL;
goto out;
}
@ -640,7 +641,10 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
ret = -EFAULT;
goto out;
}
if (len > MAX_RW_COUNT - ret) {
len = MAX_RW_COUNT - ret;
iov[seg].iov_len = len;
}
ret += len;
}
out:

View File

@ -1867,6 +1867,7 @@ extern int current_umask(void);
/* /sys/fs */
extern struct kobject *fs_kobj;
#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
extern int rw_verify_area(int, struct file *, loff_t *, size_t);
#define FLOCK_VERIFY_READ 1