Commit ee6e00c8 authored by Jens Axboe's avatar Jens Axboe
Browse files

splice: change exported internal do_splice() helper to take kernel offset



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: default avatarJens Axboe <axboe@kernel.dk>
parent 4017eb91
Loading
Loading
Loading
Loading
+50 −13
Original line number Diff line number Diff line
@@ -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,7 +1442,7 @@ 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,
			error = __do_splice(in.file, off_in, out.file, off_out,
						len, flags);
			fdput(out);
		}
+2 −2
Original line number Diff line number Diff line
@@ -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,