* [PATCH 0/2] aio: fix the vectored aio routines
@ 2010-04-30 20:48 Jeff Moyer
2010-04-30 20:48 ` [PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev Jeff Moyer
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Jeff Moyer @ 2010-04-30 20:48 UTC (permalink / raw)
To: linux-aio; +Cc: linux-kernel, mjt
Hi,
It was reported[1] that 32 bit readv and writev AIO operations were
not functioning properly. It turns out that the code to convert the
32bit io vectors to 64 bits was never written. The results of that
can be pretty bad, but in my testing, it mostly ended up in generating
EFAULT as we walked off the list of I/O vectors provided.
This patch set fixes the problem in my environment. Comments, as always,
are greatly appreciated.
Cheers,
Jeff
[1] http://lkml.org/lkml/2010/3/8/309
[PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev
[PATCH 2/2] aio: fix the compat vectored operations
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev
2010-04-30 20:48 [PATCH 0/2] aio: fix the vectored aio routines Jeff Moyer
@ 2010-04-30 20:48 ` Jeff Moyer
2010-04-30 20:49 ` [PATCH 2/2] aio: fix the compat vectored operations Jeff Moyer
2010-05-04 20:13 ` [PATCH 0/2] aio: fix the vectored aio routines Andrew Morton
2 siblings, 0 replies; 5+ messages in thread
From: Jeff Moyer @ 2010-04-30 20:48 UTC (permalink / raw)
To: linux-aio; +Cc: linux-kernel, mjt, Jeff Moyer
This patch factors out code that will be used by both compat_do_readv_writev
and the compat aio submission code paths.
Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
---
fs/compat.c | 130 ++++++++++++++++++++++++++++-------------------
include/linux/compat.h | 4 ++
2 files changed, 81 insertions(+), 53 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c
index 4b6ed03..ae0066d 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -568,6 +568,79 @@ out:
return ret;
}
+/* A write operation does a read from user space and vice versa */
+#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
+
+ssize_t compat_rw_copy_check_uvector(int type,
+ const struct compat_iovec __user *uvector, unsigned long nr_segs,
+ unsigned long fast_segs, struct iovec *fast_pointer,
+ struct iovec **ret_pointer)
+{
+ compat_ssize_t tot_len;
+ struct iovec *iov = *ret_pointer = fast_pointer;
+ ssize_t ret = 0;
+ int seg;
+
+ /*
+ * SuS says "The readv() function *may* fail if the iovcnt argument
+ * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
+ * traditionally returned zero for zero segments, so...
+ */
+ if (nr_segs == 0)
+ goto out;
+
+ ret = -EINVAL;
+ if (nr_segs > UIO_MAXIOV || nr_segs < 0)
+ goto out;
+ if (nr_segs > fast_segs) {
+ ret = -ENOMEM;
+ iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
+ if (iov == NULL) {
+ *ret_pointer = fast_pointer;
+ goto out;
+ }
+ }
+ *ret_pointer = iov;
+
+ /*
+ * Single unix specification:
+ * We should -EINVAL if an element length is not >= 0 and fitting an
+ * ssize_t. The total length is fitting an ssize_t
+ *
+ * Be careful here because iov_len is a size_t not an ssize_t
+ */
+ tot_len = 0;
+ ret = -EINVAL;
+ for (seg = 0; seg < nr_segs; seg++) {
+ compat_ssize_t tmp = tot_len;
+ compat_uptr_t buf;
+ compat_ssize_t len;
+
+ if (__get_user(len, &uvector->iov_len) ||
+ __get_user(buf, &uvector->iov_base)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (len < 0) /* size_t not fitting in compat_ssize_t .. */
+ goto out;
+ tot_len += len;
+ if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
+ goto out;
+ if (!access_ok(vrfy_dir(type), buf, len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ iov->iov_base = compat_ptr(buf);
+ iov->iov_len = (compat_size_t) len;
+ uvector++;
+ iov++;
+ }
+ ret = tot_len;
+
+out:
+ return ret;
+}
+
static inline long
copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64)
{
@@ -1077,70 +1150,21 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
{
compat_ssize_t tot_len;
struct iovec iovstack[UIO_FASTIOV];
- struct iovec *iov=iovstack, *vector;
+ struct iovec *iov;
ssize_t ret;
- int seg;
io_fn_t fn;
iov_fn_t fnv;
- /*
- * SuS says "The readv() function *may* fail if the iovcnt argument
- * was less than or equal to 0, or greater than {IOV_MAX}. Linux has
- * traditionally returned zero for zero segments, so...
- */
- ret = 0;
- if (nr_segs == 0)
- goto out;
-
- /*
- * First get the "struct iovec" from user memory and
- * verify all the pointers
- */
ret = -EINVAL;
- if ((nr_segs > UIO_MAXIOV) || (nr_segs <= 0))
- goto out;
if (!file->f_op)
goto out;
- if (nr_segs > UIO_FASTIOV) {
- ret = -ENOMEM;
- iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
- if (!iov)
- goto out;
- }
+
ret = -EFAULT;
if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
goto out;
- /*
- * Single unix specification:
- * We should -EINVAL if an element length is not >= 0 and fitting an
- * ssize_t. The total length is fitting an ssize_t
- *
- * Be careful here because iov_len is a size_t not an ssize_t
- */
- tot_len = 0;
- vector = iov;
- ret = -EINVAL;
- for (seg = 0 ; seg < nr_segs; seg++) {
- compat_ssize_t tmp = tot_len;
- compat_ssize_t len;
- compat_uptr_t buf;
-
- if (__get_user(len, &uvector->iov_len) ||
- __get_user(buf, &uvector->iov_base)) {
- ret = -EFAULT;
- goto out;
- }
- if (len < 0) /* size_t not fitting an compat_ssize_t .. */
- goto out;
- tot_len += len;
- if (tot_len < tmp) /* maths overflow on the compat_ssize_t */
- goto out;
- vector->iov_base = compat_ptr(buf);
- vector->iov_len = (compat_size_t) len;
- uvector++;
- vector++;
- }
+ tot_len = compat_rw_copy_check_uvector(type, uvector, nr_segs,
+ UIO_FASTIOV, iovstack, &iov);
if (tot_len == 0) {
ret = 0;
goto out;
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 717c691..168f7da 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -356,5 +356,9 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename,
asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename,
int flags, int mode);
+extern ssize_t compat_rw_copy_check_uvector(int type,
+ const struct compat_iovec __user *uvector, unsigned long nr_segs,
+ unsigned long fast_segs, struct iovec *fast_pointer,
+ struct iovec **ret_pointer);
#endif /* CONFIG_COMPAT */
#endif /* _LINUX_COMPAT_H */
--
1.6.2.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] aio: fix the compat vectored operations
2010-04-30 20:48 [PATCH 0/2] aio: fix the vectored aio routines Jeff Moyer
2010-04-30 20:48 ` [PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev Jeff Moyer
@ 2010-04-30 20:49 ` Jeff Moyer
2010-05-04 20:13 ` [PATCH 0/2] aio: fix the vectored aio routines Andrew Morton
2 siblings, 0 replies; 5+ messages in thread
From: Jeff Moyer @ 2010-04-30 20:49 UTC (permalink / raw)
To: linux-aio; +Cc: linux-kernel, mjt, Jeff Moyer
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.
Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
---
fs/aio.c | 63 +++++++++++++++++++++++++++++++-------------------
fs/compat.c | 2 +-
include/linux/aio.h | 5 ++++
3 files changed, 45 insertions(+), 25 deletions(-)
diff --git a/fs/aio.c b/fs/aio.c
index 1cf12b3..5a38805 100644
--- a/fs/aio.c
+++ b/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,20 @@ 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);
+ 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
+ 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 +1428,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 +1477,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 +1491,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 +1556,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 +1618,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 +1646,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 +1684,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;
}
@@ -1696,6 +1693,24 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
put_ioctx(ctx);
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.
diff --git a/fs/compat.c b/fs/compat.c
index ae0066d..7dd8952 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -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;
}
diff --git a/include/linux/aio.h b/include/linux/aio.h
index 811dbb3..54b6ef8 100644
--- a/include/linux/aio.h
+++ b/include/linux/aio.h
@@ -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) { }
#endif /* CONFIG_AIO */
static inline struct kiocb *list_kiocb(struct list_head *h)
--
1.6.2.5
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 0/2] aio: fix the vectored aio routines
2010-04-30 20:48 [PATCH 0/2] aio: fix the vectored aio routines Jeff Moyer
2010-04-30 20:48 ` [PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev Jeff Moyer
2010-04-30 20:49 ` [PATCH 2/2] aio: fix the compat vectored operations Jeff Moyer
@ 2010-05-04 20:13 ` Andrew Morton
2010-05-04 20:16 ` Jeff Moyer
2 siblings, 1 reply; 5+ messages in thread
From: Andrew Morton @ 2010-05-04 20:13 UTC (permalink / raw)
To: Jeff Moyer; +Cc: linux-aio, linux-kernel, mjt, stable
On Fri, 30 Apr 2010 16:48:58 -0400
Jeff Moyer <jmoyer@redhat.com> wrote:
> Hi,
>
> It was reported[1] that 32 bit readv and writev AIO operations were
> not functioning properly. It turns out that the code to convert the
> 32bit io vectors to 64 bits was never written. The results of that
> can be pretty bad, but in my testing, it mostly ended up in generating
> EFAULT as we walked off the list of I/O vectors provided.
>
> This patch set fixes the problem in my environment. Comments, as always,
> are greatly appreciated.
>
> Cheers,
> Jeff
>
> [1] http://lkml.org/lkml/2010/3/8/309
>
> [PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev
> [PATCH 2/2] aio: fix the compat vectored operations
The patches are large(ish) and we're at -rc6. I'm inclined to merge
these into 2.6.35-rc1 with a -stable tag so they get backported into 2.6.34.1
after a bit of mainline testing, OK?
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 0/2] aio: fix the vectored aio routines
2010-05-04 20:13 ` [PATCH 0/2] aio: fix the vectored aio routines Andrew Morton
@ 2010-05-04 20:16 ` Jeff Moyer
0 siblings, 0 replies; 5+ messages in thread
From: Jeff Moyer @ 2010-05-04 20:16 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-aio, linux-kernel, mjt, stable
Andrew Morton <akpm@linux-foundation.org> writes:
> On Fri, 30 Apr 2010 16:48:58 -0400
> Jeff Moyer <jmoyer@redhat.com> wrote:
>
>> Hi,
>>
>> It was reported[1] that 32 bit readv and writev AIO operations were
>> not functioning properly. It turns out that the code to convert the
>> 32bit io vectors to 64 bits was never written. The results of that
>> can be pretty bad, but in my testing, it mostly ended up in generating
>> EFAULT as we walked off the list of I/O vectors provided.
>>
>> This patch set fixes the problem in my environment. Comments, as always,
>> are greatly appreciated.
>>
>> Cheers,
>> Jeff
>>
>> [1] http://lkml.org/lkml/2010/3/8/309
>>
>> [PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev
>> [PATCH 2/2] aio: fix the compat vectored operations
>
> The patches are large(ish) and we're at -rc6. I'm inclined to merge
> these into 2.6.35-rc1 with a -stable tag so they get backported into 2.6.34.1
> after a bit of mainline testing, OK?
That's fine with me.
Cheers,
Jeff
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2010-05-04 20:17 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-04-30 20:48 [PATCH 0/2] aio: fix the vectored aio routines Jeff Moyer
2010-04-30 20:48 ` [PATCH 1/2] compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev Jeff Moyer
2010-04-30 20:49 ` [PATCH 2/2] aio: fix the compat vectored operations Jeff Moyer
2010-05-04 20:13 ` [PATCH 0/2] aio: fix the vectored aio routines Andrew Morton
2010-05-04 20:16 ` Jeff Moyer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox