Skip to content

Commit

Permalink
aio: fix the compat vectored operations
Browse files Browse the repository at this point in the history
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.

[[email protected]: coding-style fixes]
[[email protected]: fix CONFIG_COMPAT=n build]
Signed-off-by: Jeff Moyer <[email protected]>
Reported-by: Michael Tokarev <[email protected]>
Cc: Zach Brown <[email protected]>
Cc: <[email protected]>		[2.6.35.1]
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
JeffMoyer authored and torvalds committed May 27, 2010
1 parent b837336 commit 9d85cba
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 25 deletions.
65 changes: 41 additions & 24 deletions fs/aio.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <linux/blkdev.h>
#include <linux/mempool.h>
#include <linux/hash.h>
#include <linux/compat.h>

#include <asm/kmap_types.h>
#include <asm/uaccess.h>
Expand Down Expand Up @@ -1384,13 +1385,22 @@ static ssize_t aio_fsync(struct kiocb *iocb)
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;

ret = rw_copy_check_uvector(type, (struct iovec __user *)kiocb->ki_buf,
kiocb->ki_nbytes, 1,
&kiocb->ki_inline_vec, &kiocb->ki_iovec);
#ifdef CONFIG_COMPAT
if (compat)
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)
goto out;

Expand Down Expand Up @@ -1420,7 +1430,7 @@ static ssize_t aio_setup_single_vector(struct kiocb *kiocb)
* Performs the initial checks and aio retry method
* 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;
ssize_t ret = 0;
Expand Down Expand Up @@ -1469,7 +1479,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
ret = security_file_permission(file, MAY_READ);
if (unlikely(ret))
break;
ret = aio_setup_vectored_rw(READ, kiocb);
ret = aio_setup_vectored_rw(READ, kiocb, compat);
if (ret)
break;
ret = -EINVAL;
Expand All @@ -1483,7 +1493,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
ret = security_file_permission(file, MAY_WRITE);
if (unlikely(ret))
break;
ret = aio_setup_vectored_rw(WRITE, kiocb);
ret = aio_setup_vectored_rw(WRITE, kiocb, compat);
if (ret)
break;
ret = -EINVAL;
Expand Down Expand Up @@ -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,
struct iocb *iocb, struct hlist_head *batch_hash)
struct iocb *iocb, struct hlist_head *batch_hash,
bool compat)
{
struct kiocb *req;
struct file *file;
Expand Down Expand Up @@ -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_opcode = iocb->aio_lio_opcode;

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

if (ret)
goto out_put_req;
Expand Down Expand Up @@ -1637,20 +1648,8 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
return 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)
long do_io_submit(aio_context_t ctx_id, long nr,
struct iocb __user *__user *iocbpp, bool compat)
{
struct kioctx *ctx;
long ret = 0;
Expand Down Expand Up @@ -1687,7 +1686,7 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
break;
}

ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash);
ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash, compat);
if (ret)
break;
}
Expand All @@ -1697,6 +1696,24 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
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
* Finds a given iocb for cancellation.
*/
Expand Down
2 changes: 1 addition & 1 deletion fs/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
ret = copy_iocb(nr, iocb, iocb64);
if (!ret)
ret = sys_io_submit(ctx_id, nr, iocb64);
ret = do_io_submit(ctx_id, nr, iocb64, 1);
return ret;
}

Expand Down
5 changes: 5 additions & 0 deletions include/linux/aio.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,18 @@ extern void kick_iocb(struct kiocb *iocb);
extern int aio_complete(struct kiocb *iocb, long res, long res2);
struct mm_struct;
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
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 void kick_iocb(struct kiocb *iocb) { }
static inline int aio_complete(struct kiocb *iocb, long res, long res2) { return 0; }
struct mm_struct;
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 */

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

0 comments on commit 9d85cba

Please sign in to comment.