Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d85cba7 authored by Jeff Moyer's avatar Jeff Moyer Committed by Linus Torvalds
Browse files

aio: fix the compat vectored operations



The aio compat code was not converting the struct iovecs from 32bit to
64bit pointers, causing either EINVAL to be returned from io_getevents, or
EFAULT as the result of the I/O.  This patch passes a compat flag to
io_submit to signal that pointer conversion is necessary for a given iocb
array.

A variant of this was tested by Michael Tokarev.  I have also updated the
libaio test harness to exercise this code path with good success.
Further, I grabbed a copy of ltp and ran the
testcases/kernel/syscall/readv and writev tests there (compiled with -m32
on my 64bit system).  All seems happy, but extra eyes on this would be
welcome.

[akpm@linux-foundation.org: coding-style fixes]
[akpm@linux-foundation.org: fix CONFIG_COMPAT=n build]
Signed-off-by: default avatarJeff Moyer <jmoyer@redhat.com>
Reported-by: default avatarMichael Tokarev <mjt@tls.msk.ru>
Cc: Zach Brown <zach.brown@oracle.com>
Cc: <stable@kernel.org>		[2.6.35.1]
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b8373363
Loading
Loading
Loading
Loading
+41 −24
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@
#include <linux/blkdev.h>
#include <linux/blkdev.h>
#include <linux/mempool.h>
#include <linux/mempool.h>
#include <linux/hash.h>
#include <linux/hash.h>
#include <linux/compat.h>


#include <asm/kmap_types.h>
#include <asm/kmap_types.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
@@ -1384,13 +1385,22 @@ static ssize_t aio_fsync(struct kiocb *iocb)
	return ret;
	return ret;
}
}


static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb)
static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb, bool compat)
{
{
	ssize_t ret;
	ssize_t ret;


	ret = rw_copy_check_uvector(type, (struct iovec __user *)kiocb->ki_buf,
#ifdef CONFIG_COMPAT
				    kiocb->ki_nbytes, 1,
	if (compat)
				    &kiocb->ki_inline_vec, &kiocb->ki_iovec);
		ret = compat_rw_copy_check_uvector(type,
				(struct compat_iovec __user *)kiocb->ki_buf,
				kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec,
				&kiocb->ki_iovec);
	else
#endif
		ret = rw_copy_check_uvector(type,
				(struct iovec __user *)kiocb->ki_buf,
				kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec,
				&kiocb->ki_iovec);
	if (ret < 0)
	if (ret < 0)
		goto out;
		goto out;


@@ -1420,7 +1430,7 @@ static ssize_t aio_setup_single_vector(struct kiocb *kiocb)
 *	Performs the initial checks and aio retry method
 *	Performs the initial checks and aio retry method
 *	setup for the kiocb at the time of io submission.
 *	setup for the kiocb at the time of io submission.
 */
 */
static ssize_t aio_setup_iocb(struct kiocb *kiocb)
static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat)
{
{
	struct file *file = kiocb->ki_filp;
	struct file *file = kiocb->ki_filp;
	ssize_t ret = 0;
	ssize_t ret = 0;
@@ -1469,7 +1479,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
		ret = security_file_permission(file, MAY_READ);
		ret = security_file_permission(file, MAY_READ);
		if (unlikely(ret))
		if (unlikely(ret))
			break;
			break;
		ret = aio_setup_vectored_rw(READ, kiocb);
		ret = aio_setup_vectored_rw(READ, kiocb, compat);
		if (ret)
		if (ret)
			break;
			break;
		ret = -EINVAL;
		ret = -EINVAL;
@@ -1483,7 +1493,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
		ret = security_file_permission(file, MAY_WRITE);
		ret = security_file_permission(file, MAY_WRITE);
		if (unlikely(ret))
		if (unlikely(ret))
			break;
			break;
		ret = aio_setup_vectored_rw(WRITE, kiocb);
		ret = aio_setup_vectored_rw(WRITE, kiocb, compat);
		if (ret)
		if (ret)
			break;
			break;
		ret = -EINVAL;
		ret = -EINVAL;
@@ -1548,7 +1558,8 @@ static void aio_batch_free(struct hlist_head *batch_hash)
}
}


static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
			 struct iocb *iocb, struct hlist_head *batch_hash)
			 struct iocb *iocb, struct hlist_head *batch_hash,
			 bool compat)
{
{
	struct kiocb *req;
	struct kiocb *req;
	struct file *file;
	struct file *file;
@@ -1609,7 +1620,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
	req->ki_left = req->ki_nbytes = iocb->aio_nbytes;
	req->ki_left = req->ki_nbytes = iocb->aio_nbytes;
	req->ki_opcode = iocb->aio_lio_opcode;
	req->ki_opcode = iocb->aio_lio_opcode;


	ret = aio_setup_iocb(req);
	ret = aio_setup_iocb(req, compat);


	if (ret)
	if (ret)
		goto out_put_req;
		goto out_put_req;
@@ -1637,20 +1648,8 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
	return ret;
	return ret;
}
}


/* sys_io_submit:
long do_io_submit(aio_context_t ctx_id, long nr,
 *	Queue the nr iocbs pointed to by iocbpp for processing.  Returns
		  struct iocb __user *__user *iocbpp, bool compat)
 *	the number of iocbs queued.  May return -EINVAL if the aio_context
 *	specified by ctx_id is invalid, if nr is < 0, if the iocb at
 *	*iocbpp[0] is not properly initialized, if the operation specified
 *	is invalid for the file descriptor in the iocb.  May fail with
 *	-EFAULT if any of the data structures point to invalid data.  May
 *	fail with -EBADF if the file descriptor specified in the first
 *	iocb is invalid.  May fail with -EAGAIN if insufficient resources
 *	are available to queue any iocbs.  Will return 0 if nr is 0.  Will
 *	fail with -ENOSYS if not implemented.
 */
SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
		struct iocb __user * __user *, iocbpp)
{
{
	struct kioctx *ctx;
	struct kioctx *ctx;
	long ret = 0;
	long ret = 0;
@@ -1687,7 +1686,7 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
			break;
			break;
		}
		}


		ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash);
		ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash, compat);
		if (ret)
		if (ret)
			break;
			break;
	}
	}
@@ -1697,6 +1696,24 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
	return i ? i : ret;
	return i ? i : ret;
}
}


/* sys_io_submit:
 *	Queue the nr iocbs pointed to by iocbpp for processing.  Returns
 *	the number of iocbs queued.  May return -EINVAL if the aio_context
 *	specified by ctx_id is invalid, if nr is < 0, if the iocb at
 *	*iocbpp[0] is not properly initialized, if the operation specified
 *	is invalid for the file descriptor in the iocb.  May fail with
 *	-EFAULT if any of the data structures point to invalid data.  May
 *	fail with -EBADF if the file descriptor specified in the first
 *	iocb is invalid.  May fail with -EAGAIN if insufficient resources
 *	are available to queue any iocbs.  Will return 0 if nr is 0.  Will
 *	fail with -ENOSYS if not implemented.
 */
SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
		struct iocb __user * __user *, iocbpp)
{
	return do_io_submit(ctx_id, nr, iocbpp, 0);
}

/* lookup_kiocb
/* lookup_kiocb
 *	Finds a given iocb for cancellation.
 *	Finds a given iocb for cancellation.
 */
 */
+1 −1
Original line number Original line Diff line number Diff line
@@ -673,7 +673,7 @@ compat_sys_io_submit(aio_context_t ctx_id, int nr, u32 __user *iocb)
	iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64));
	iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64));
	ret = copy_iocb(nr, iocb, iocb64);
	ret = copy_iocb(nr, iocb, iocb64);
	if (!ret)
	if (!ret)
		ret = sys_io_submit(ctx_id, nr, iocb64);
		ret = do_io_submit(ctx_id, nr, iocb64, 1);
	return ret;
	return ret;
}
}


+5 −0
Original line number Original line Diff line number Diff line
@@ -212,6 +212,8 @@ extern void kick_iocb(struct kiocb *iocb);
extern int aio_complete(struct kiocb *iocb, long res, long res2);
extern int aio_complete(struct kiocb *iocb, long res, long res2);
struct mm_struct;
struct mm_struct;
extern void exit_aio(struct mm_struct *mm);
extern void exit_aio(struct mm_struct *mm);
extern long do_io_submit(aio_context_t ctx_id, long nr,
			 struct iocb __user *__user *iocbpp, bool compat);
#else
#else
static inline ssize_t wait_on_sync_kiocb(struct kiocb *iocb) { return 0; }
static inline ssize_t wait_on_sync_kiocb(struct kiocb *iocb) { return 0; }
static inline int aio_put_req(struct kiocb *iocb) { return 0; }
static inline int aio_put_req(struct kiocb *iocb) { return 0; }
@@ -219,6 +221,9 @@ static inline void kick_iocb(struct kiocb *iocb) { }
static inline int aio_complete(struct kiocb *iocb, long res, long res2) { return 0; }
static inline int aio_complete(struct kiocb *iocb, long res, long res2) { return 0; }
struct mm_struct;
struct mm_struct;
static inline void exit_aio(struct mm_struct *mm) { }
static inline void exit_aio(struct mm_struct *mm) { }
static inline long do_io_submit(aio_context_t ctx_id, long nr,
				struct iocb __user * __user *iocbpp,
				bool compat) { return 0; }
#endif /* CONFIG_AIO */
#endif /* CONFIG_AIO */


static inline struct kiocb *list_kiocb(struct list_head *h)
static inline struct kiocb *list_kiocb(struct list_head *h)