|
@@ -568,6 +568,79 @@ out:
|
|
return ret;
|
|
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
|
|
static inline long
|
|
copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64)
|
|
copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64)
|
|
{
|
|
{
|
|
@@ -1077,70 +1150,21 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
|
|
{
|
|
{
|
|
compat_ssize_t tot_len;
|
|
compat_ssize_t tot_len;
|
|
struct iovec iovstack[UIO_FASTIOV];
|
|
struct iovec iovstack[UIO_FASTIOV];
|
|
- struct iovec *iov=iovstack, *vector;
|
|
|
|
|
|
+ struct iovec *iov;
|
|
ssize_t ret;
|
|
ssize_t ret;
|
|
- int seg;
|
|
|
|
io_fn_t fn;
|
|
io_fn_t fn;
|
|
iov_fn_t fnv;
|
|
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;
|
|
ret = -EINVAL;
|
|
- if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0))
|
|
|
|
- goto out;
|
|
|
|
if (!file->f_op)
|
|
if (!file->f_op)
|
|
goto out;
|
|
goto out;
|
|
- if (nr_segs > UIO_FASTIOV) {
|
|
|
|
- ret = -ENOMEM;
|
|
|
|
- iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
|
|
|
|
- if (!iov)
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
ret = -EFAULT;
|
|
ret = -EFAULT;
|
|
if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
|
|
if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
|
|
goto out;
|
|
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) {
|
|
if (tot_len == 0) {
|
|
ret = 0;
|
|
ret = 0;
|
|
goto out;
|
|
goto out;
|