Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 198835
b: refs/heads/master
c: b837336
h: refs/heads/master
i:
  198833: 4c789aa
  198831: 30af8ce
v: v3
  • Loading branch information
Jeff Moyer authored and Linus Torvalds committed May 27, 2010
1 parent 7e9feb8 commit 0fc8d4c
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 54 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5b530fc18324602e1ecb70cd280c2194b2656a5e
refs/heads/master: b83733639a494d5f42fa00a2506563fbd2d3015d
130 changes: 77 additions & 53 deletions trunk/fs/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,79 @@ compat_sys_io_getevents(aio_context_t ctx_id,
return ret;
}

/* A write operation does a read from user space and vice versa */
#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)

ssize_t compat_rw_copy_check_uvector(int type,
const struct compat_iovec __user *uvector, unsigned long nr_segs,
unsigned long fast_segs, struct iovec *fast_pointer,
struct iovec **ret_pointer)
{
compat_ssize_t tot_len;
struct iovec *iov = *ret_pointer = fast_pointer;
ssize_t ret = 0;
int seg;

/*
* SuS says "The readv() function *may* fail if the iovcnt argument
* was less than or equal to 0, or greater than {IOV_MAX}. Linux has
* traditionally returned zero for zero segments, so...
*/
if (nr_segs == 0)
goto out;

ret = -EINVAL;
if (nr_segs > UIO_MAXIOV || nr_segs < 0)
goto out;
if (nr_segs > fast_segs) {
ret = -ENOMEM;
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
if (iov == NULL) {
*ret_pointer = fast_pointer;
goto out;
}
}
*ret_pointer = iov;

/*
* 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
*
* Be careful here because iov_len is a size_t not an ssize_t
*/
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;

if (__get_user(len, &uvector->iov_len) ||
__get_user(buf, &uvector->iov_base)) {
ret = -EFAULT;
goto out;
}
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), buf, len)) {
ret = -EFAULT;
goto out;
}
iov->iov_base = compat_ptr(buf);
iov->iov_len = (compat_size_t) len;
uvector++;
iov++;
}
ret = tot_len;

out:
return ret;
}

static inline long
copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64)
{
Expand Down Expand Up @@ -1077,70 +1150,21 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
{
compat_ssize_t tot_len;
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov=iovstack, *vector;
struct iovec *iov;
ssize_t ret;
int seg;
io_fn_t fn;
iov_fn_t fnv;

/*
* SuS says "The readv() function *may* fail if the iovcnt argument
* was less than or equal to 0, or greater than {IOV_MAX}. Linux has
* traditionally returned zero for zero segments, so...
*/
ret = 0;
if (nr_segs == 0)
goto out;

/*
* First get the "struct iovec" from user memory and
* verify all the pointers
*/
ret = -EINVAL;
if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0))
goto out;
if (!file->f_op)
goto out;
if (nr_segs > UIO_FASTIOV) {
ret = -ENOMEM;
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
if (!iov)
goto out;
}

ret = -EFAULT;
if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
goto out;

/*
* 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
*
* Be careful here because iov_len is a size_t not an ssize_t
*/
tot_len = 0;
vector = iov;
ret = -EINVAL;
for (seg = 0 ; seg < nr_segs; seg++) {
compat_ssize_t tmp = tot_len;
compat_ssize_t len;
compat_uptr_t buf;

if (__get_user(len, &uvector->iov_len) ||
__get_user(buf, &uvector->iov_base)) {
ret = -EFAULT;
goto out;
}
if (len < 0) /* size_t not fitting an compat_ssize_t .. */
goto out;
tot_len += len;
if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
goto out;
vector->iov_base = compat_ptr(buf);
vector->iov_len = (compat_size_t) len;
uvector++;
vector++;
}
tot_len = compat_rw_copy_check_uvector(type, uvector, nr_segs,
UIO_FASTIOV, iovstack, &iov);
if (tot_len == 0) {
ret = 0;
goto out;
Expand Down
4 changes: 4 additions & 0 deletions trunk/include/linux/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,5 +356,9 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename,
asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename,
int flags, int mode);

extern ssize_t compat_rw_copy_check_uvector(int type,
const struct compat_iovec __user *uvector, unsigned long nr_segs,
unsigned long fast_segs, struct iovec *fast_pointer,
struct iovec **ret_pointer);
#endif /* CONFIG_COMPAT */
#endif /* _LINUX_COMPAT_H */

0 comments on commit 0fc8d4c

Please sign in to comment.