splice: change exported internal do_splice() helper to take kernel offset
authorJens Axboe <axboe@kernel.dk>
Thu, 22 Oct 2020 20:15:51 +0000 (14:15 -0600)
committerJens Axboe <axboe@kernel.dk>
Thu, 22 Oct 2020 20:15:51 +0000 (14:15 -0600)
With the set_fs change, we can no longer rely on copy_{to,from}_user()
accepting a kernel pointer, and it was bad form to do so anyway. Clean
this up and change the internal helper that io_uring uses to deal with
kernel pointers instead. This puts the offset copy in/out in __do_splice()
instead, which just calls the same helper.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/splice.c
include/linux/splice.h

index 70cc52a..d9305af 100644 (file)
@@ -1107,9 +1107,8 @@ static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
 /*
  * Determine where to splice to/from.
  */
-long do_splice(struct file *in, loff_t __user *off_in,
-               struct file *out, loff_t __user *off_out,
-               size_t len, unsigned int flags)
+long do_splice(struct file *in, loff_t *off_in, struct file *out,
+              loff_t *off_out, size_t len, unsigned int flags)
 {
        struct pipe_inode_info *ipipe;
        struct pipe_inode_info *opipe;
@@ -1143,8 +1142,7 @@ long do_splice(struct file *in, loff_t __user *off_in,
                if (off_out) {
                        if (!(out->f_mode & FMODE_PWRITE))
                                return -EINVAL;
-                       if (copy_from_user(&offset, off_out, sizeof(loff_t)))
-                               return -EFAULT;
+                       offset = *off_out;
                } else {
                        offset = out->f_pos;
                }
@@ -1165,8 +1163,8 @@ long do_splice(struct file *in, loff_t __user *off_in,
 
                if (!off_out)
                        out->f_pos = offset;
-               else if (copy_to_user(off_out, &offset, sizeof(loff_t)))
-                       ret = -EFAULT;
+               else
+                       *off_out = offset;
 
                return ret;
        }
@@ -1177,8 +1175,7 @@ long do_splice(struct file *in, loff_t __user *off_in,
                if (off_in) {
                        if (!(in->f_mode & FMODE_PREAD))
                                return -EINVAL;
-                       if (copy_from_user(&offset, off_in, sizeof(loff_t)))
-                               return -EFAULT;
+                       offset = *off_in;
                } else {
                        offset = in->f_pos;
                }
@@ -1202,8 +1199,8 @@ long do_splice(struct file *in, loff_t __user *off_in,
                        wakeup_pipe_readers(opipe);
                if (!off_in)
                        in->f_pos = offset;
-               else if (copy_to_user(off_in, &offset, sizeof(loff_t)))
-                       ret = -EFAULT;
+               else
+                       *off_in = offset;
 
                return ret;
        }
@@ -1211,6 +1208,46 @@ long do_splice(struct file *in, loff_t __user *off_in,
        return -EINVAL;
 }
 
+static long __do_splice(struct file *in, loff_t __user *off_in,
+                       struct file *out, loff_t __user *off_out,
+                       size_t len, unsigned int flags)
+{
+       struct pipe_inode_info *ipipe;
+       struct pipe_inode_info *opipe;
+       loff_t offset, *__off_in = NULL, *__off_out = NULL;
+       long ret;
+
+       ipipe = get_pipe_info(in, true);
+       opipe = get_pipe_info(out, true);
+
+       if (ipipe && off_in)
+               return -ESPIPE;
+       if (opipe && off_out)
+               return -ESPIPE;
+
+       if (off_out) {
+               if (copy_from_user(&offset, off_out, sizeof(loff_t)))
+                       return -EFAULT;
+               __off_out = &offset;
+       }
+       if (off_in) {
+               if (copy_from_user(&offset, off_in, sizeof(loff_t)))
+                       return -EFAULT;
+               __off_in = &offset;
+       }
+
+       ret = do_splice(in, __off_in, out, __off_out, len, flags);
+       if (ret < 0)
+               return ret;
+
+       if (__off_out && copy_to_user(off_out, __off_out, sizeof(loff_t)))
+               return -EFAULT;
+       if (__off_in && copy_to_user(off_in, __off_in, sizeof(loff_t)))
+               return -EFAULT;
+
+       return ret;
+}
+
 static int iter_to_pipe(struct iov_iter *from,
                        struct pipe_inode_info *pipe,
                        unsigned flags)
@@ -1405,8 +1442,8 @@ SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
        if (in.file) {
                out = fdget(fd_out);
                if (out.file) {
-                       error = do_splice(in.file, off_in, out.file, off_out,
-                                         len, flags);
+                       error = __do_splice(in.file, off_in, out.file, off_out,
+                                               len, flags);
                        fdput(out);
                }
                fdput(in);
index 5c47013..a55179f 100644 (file)
@@ -78,8 +78,8 @@ extern ssize_t add_to_pipe(struct pipe_inode_info *,
                              struct pipe_buffer *);
 extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,
                                      splice_direct_actor *);
-extern long do_splice(struct file *in, loff_t __user *off_in,
-                     struct file *out, loff_t __user *off_out,
+extern long do_splice(struct file *in, loff_t *off_in,
+                     struct file *out, loff_t *off_out,
                      size_t len, unsigned int flags);
 
 extern long do_tee(struct file *in, struct file *out, size_t len,