Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1512010

Re: [PATCH] aio: fix a user triggered use after free (and fix freeze protection of aio writes)

From Christoph Hellwig <hch@lst.de>
Newsgroups linux.kernel
Subject Re: [PATCH] aio: fix a user triggered use after free (and fix freeze protection of aio writes)
Date 2016-10-30 07:40 +0100
Message-ID <sxRWF-173-1@gated-at.bofh.it> (permalink)
References <sxwyR-3dk-3@gated-at.bofh.it> <sxwyR-3dk-1@gated-at.bofh.it> <sxAVP-6bh-9@gated-at.bofh.it> <sxDK1-7WB-17@gated-at.bofh.it> <sxFVv-1aY-1@gated-at.bofh.it>
Organization linux.* mail to news gateway

Show all headers | View raw


On Sat, Oct 29, 2016 at 10:47:58AM -0700, Linus Torvalds wrote:
> On Sat, Oct 29, 2016 at 8:20 AM, Christoph Hellwig <hch@lst.de> wrote:
> >
> > We can't as that would not fix the use after free (at least for the lockdep
> > case - otherwise the call is a no-op).  Once iter_op returns aio_complete
> > might have dropped our reference to the file, and another thread might
> > have closed the fd so that the fput from aio_complete was the last one.
> 
> I don't concpetually mind the patch per se, but the repeated
> 
>              if (rw == WRITE) {
>                    ..
>              }
> 
>              if (rw == WRITE) {
>                    ..
>              }
> 
> is just insane and makes the code less legible than it should be.

I agree.  And I actually prepared a major refactor of the code to
get rid of it, but didn't dare to post it this late in the cycle.

Patch below, although this recently rebased version is not actually tested,
so be aware:

---
commit 8bc9e04f941645a610ca46804a95170bdd854450
Author: Christoph Hellwig <hch@lst.de>
Date:   Sun Oct 30 07:27:16 2016 +0100

    fs: refactor aio_run_iocb
    
    Signed-off-by: Christoph Hellwig <hch@lst.de>

diff --git a/fs/aio.c b/fs/aio.c
index bf315cd..bc5541e 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -1403,13 +1403,16 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
 	return -EINVAL;
 }
 
-typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *);
-
-static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len,
-				 struct iovec **iovec,
-				 bool compat,
-				 struct iov_iter *iter)
+static int aio_setup_rw(int rw, char __user *buf, size_t len,
+		struct iovec **iovec, bool vectored, bool compat,
+		struct iov_iter *iter)
 {
+	if (!vectored) {
+		ssize_t ret = import_single_range(rw, buf, len, *iovec, iter);
+		iovec = NULL;
+		return ret;
+	}
+
 #ifdef CONFIG_COMPAT
 	if (compat)
 		return compat_import_iovec(rw,
@@ -1420,110 +1423,100 @@ static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len,
 				len, UIO_FASTIOV, iovec, iter);
 }
 
-/*
- * aio_run_iocb:
- *	Performs the initial checks and io submission.
- */
-static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
-			    char __user *buf, size_t len, bool compat)
+static ssize_t aio_read(struct kiocb *req, char __user *buf, size_t len,
+		bool vectored, bool compat)
 {
 	struct file *file = req->ki_filp;
-	ssize_t ret;
-	int rw;
-	fmode_t mode;
-	rw_iter_op *iter_op;
 	struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
 	struct iov_iter iter;
+	ssize_t ret;
 
-	switch (opcode) {
-	case IOCB_CMD_PREAD:
-	case IOCB_CMD_PREADV:
-		mode	= FMODE_READ;
-		rw	= READ;
-		iter_op	= file->f_op->read_iter;
-		goto rw_common;
+	if (unlikely(!(file->f_mode & FMODE_READ)))
+		return -EBADF;
+	if (unlikely(!file->f_op->read_iter))
+		return -EINVAL;
 
-	case IOCB_CMD_PWRITE:
-	case IOCB_CMD_PWRITEV:
-		mode	= FMODE_WRITE;
-		rw	= WRITE;
-		iter_op	= file->f_op->write_iter;
-		goto rw_common;
-rw_common:
-		if (unlikely(!(file->f_mode & mode)))
-			return -EBADF;
-
-		if (!iter_op)
-			return -EINVAL;
+	ret = aio_setup_rw(READ, buf, len, &iovec, vectored, compat, &iter);
+	if (ret)
+		goto out_free_iovec;
 
-		if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV)
-			ret = aio_setup_vectored_rw(rw, buf, len,
-						&iovec, compat, &iter);
-		else {
-			ret = import_single_range(rw, buf, len, iovec, &iter);
-			iovec = NULL;
-		}
-		if (!ret)
-			ret = rw_verify_area(rw, file, &req->ki_pos,
-					     iov_iter_count(&iter));
-		if (ret < 0) {
-			kfree(iovec);
-			return ret;
-		}
+	ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter));
+	if (ret < 0)
+		goto out_free_iovec;
 
-		if (rw == WRITE) {
-			file_start_write(file);
-			req->ki_flags |= IOCB_WRITE;
-		}
+	ret = file->f_op->read_iter(req, &iter);
+out_free_iovec:
+	kfree(iovec);
+	return ret;
+}
 
-		if (rw == WRITE) {
-			/*
-			 * We release freeze protection in aio_complete(). Fool
-			 * lockdep by telling it the lock got released so that
-			 * it doesn't complain about held lock when we return
-			 * to userspace.
-			 */
-			__sb_writers_release(file_inode(file)->i_sb,
-					SB_FREEZE_WRITE);
-		}
+static ssize_t aio_write(struct kiocb *req, char __user *buf, size_t len,
+		bool vectored, bool compat)
+{
+	struct file *file = req->ki_filp;
+	struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
+	struct iov_iter iter;
+	ssize_t ret;
 
-		ret = iter_op(req, &iter);
+	if (unlikely(!(file->f_mode & FMODE_WRITE)))
+		return -EBADF;
+	if (unlikely(!file->f_op->write_iter))
+		return -EINVAL;
 
-		kfree(iovec);
-		break;
+	ret = aio_setup_rw(WRITE, buf, len, &iovec, vectored, compat, &iter);
+	if (ret)
+		goto out_free_iovec;
 
-	case IOCB_CMD_FDSYNC:
-		if (!file->f_op->aio_fsync)
-			return -EINVAL;
+	ret = rw_verify_area(WRITE, file, &req->ki_pos, iov_iter_count(&iter));
+	if (ret < 0)
+		goto out_free_iovec;
 
-		ret = file->f_op->aio_fsync(req, 1);
-		break;
+	file_start_write(file);
+	req->ki_flags |= IOCB_WRITE;
+	ret = file->f_op->write_iter(req, &iter);
 
-	case IOCB_CMD_FSYNC:
-		if (!file->f_op->aio_fsync)
-			return -EINVAL;
+	/*
+	 * We release freeze protection in aio_complete(). Fool
+	 * lockdep by telling it the lock got released so that
+	 * it doesn't complain about held lock when we return
+	 * to userspace.
+	 */
+	 __sb_writers_release(file_inode(file)->i_sb, SB_FREEZE_WRITE);
 
-		ret = file->f_op->aio_fsync(req, 0);
-		break;
+out_free_iovec:
+	kfree(iovec);
+	return ret;
+}
 
+static ssize_t aio_fsync(struct kiocb *req, bool datasync)
+{
+	struct file *file = req->ki_filp;
+
+	if (!file->f_op->aio_fsync)
+		return -EINVAL;
+	return file->f_op->aio_fsync(req, datasync);
+}
+
+static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
+			    char __user *buf, size_t len, bool compat)
+{
+	switch (opcode) {
+	case IOCB_CMD_PREAD:
+		return aio_read(req, buf, len, false, compat);
+	case IOCB_CMD_PREADV:
+		return aio_read(req, buf, len, true, compat);
+	case IOCB_CMD_PWRITE:
+		return aio_write(req, buf, len, false, compat);
+	case IOCB_CMD_PWRITEV:
+		return aio_write(req, buf, len, true, compat);
+	case IOCB_CMD_FDSYNC:
+		return aio_fsync(req, true);
+	case IOCB_CMD_FSYNC:
+		return aio_fsync(req, false);
 	default:
 		pr_debug("EINVAL: no operation provided\n");
 		return -EINVAL;
 	}
-
-	if (ret != -EIOCBQUEUED) {
-		/*
-		 * There's no easy way to restart the syscall since other AIO's
-		 * may be already running. Just fail this IO with EINTR.
-		 */
-		if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR ||
-			     ret == -ERESTARTNOHAND ||
-			     ret == -ERESTART_RESTARTBLOCK))
-			ret = -EINTR;
-		aio_complete(req, ret, 0);
-	}
-
-	return 0;
 }
 
 static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
@@ -1591,8 +1584,17 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
 			   (char __user *)(unsigned long)iocb->aio_buf,
 			   iocb->aio_nbytes,
 			   compat);
-	if (ret)
-		goto out_put_req;
+	if (ret != -EIOCBQUEUED) {
+		/*
+		 * There's no easy way to restart the syscall since other AIO's
+		 * may be already running. Just fail this IO with EINTR.
+		 */
+		if (unlikely(ret == -ERESTARTSYS || ret == -ERESTARTNOINTR ||
+			     ret == -ERESTARTNOHAND ||
+			     ret == -ERESTART_RESTARTBLOCK))
+			ret = -EINTR;
+		aio_complete(&req->common, ret, 0);
+	}
 
 	return 0;
 out_put_req:

Back to linux.kernel | Previous | NextPrevious in thread | Find similar | Unroll thread


Thread

[PATCH] aio: fix a user triggered use after free (and fix freeze protection of aio writes) Christoph Hellwig <hch@lst.de> - 2016-10-29 09:50 +0200
  Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Al Viro <viro@ZenIV.linux.org.uk> - 2016-10-29 14:30 +0200
    Re: [PATCH] aio: fix a user triggered use after free (and fix         freeze protection of aio writes) Christoph Hellwig <hch@lst.de> - 2016-10-29 17:30 +0200
      Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Al Viro <viro@ZenIV.linux.org.uk> - 2016-10-29 18:20 +0200
        Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Al Viro <viro@ZenIV.linux.org.uk> - 2016-10-29 18:40 +0200
        Re: [PATCH] aio: fix a user triggered use after free (and fix         freeze protection of aio writes) Christoph Hellwig <hch@lst.de> - 2016-10-30 07:40 +0100
      Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Linus Torvalds <torvalds@linux-foundation.org> - 2016-10-29 19:50 +0200
        Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Al Viro <viro@ZenIV.linux.org.uk> - 2016-10-29 21:00 +0200
          Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Linus Torvalds <torvalds@linux-foundation.org> - 2016-10-29 21:10 +0200
            Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Al Viro <viro@ZenIV.linux.org.uk> - 2016-10-29 21:20 +0200
              Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Linus Torvalds <torvalds@linux-foundation.org> - 2016-10-29 22:10 +0200
                Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Jan Kara <jack@suse.cz> - 2016-10-30 10:50 +0100
                Re: [PATCH] aio: fix a user triggered use after free (and fix freeze  protection of aio writes) Jan Kara <jack@suse.cz> - 2016-10-30 12:00 +0100
                Re: [PATCH] aio: fix a user triggered use after free (and fix         freeze protection of aio writes) Christoph Hellwig <hch@lst.de> - 2016-10-30 17:00 +0100
        Re: [PATCH] aio: fix a user triggered use after free (and fix         freeze protection of aio writes) Christoph Hellwig <hch@lst.de> - 2016-10-30 07:40 +0100

csiph-web