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: Jeff Moyer <jmoyer@redhat.com> Reported-by: Michael Tokarev <mjt@tls.msk.ru> Cc: Zach Brown <zach.brown@oracle.com> Cc: <stable@kernel.org> [2.6.35.1] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Родитель
b83733639a
Коммит
9d85cba718
65
fs/aio.c
65
fs/aio.c
|
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1637,20 +1648,8 @@ out_put_req:
|
|||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -212,6 +212,8 @@ 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; }
|
||||
|
@ -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; }
|
||||
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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче