diff options
author | Jeff Moyer <jmoyer@redhat.com> | 2010-05-26 17:44:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-27 12:12:53 -0400 |
commit | 9d85cba718efeef9ca00ce3f7f34f5880737aa9b (patch) | |
tree | d445d35d915b65dac171e58f83c75feb15d625fd /fs/aio.c | |
parent | b83733639a494d5f42fa00a2506563fbd2d3015d (diff) |
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>
Diffstat (limited to 'fs/aio.c')
-rw-r--r-- | fs/aio.c | 65 |
1 files changed, 41 insertions, 24 deletions
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/blkdev.h> | 36 | #include <linux/blkdev.h> |
37 | #include <linux/mempool.h> | 37 | #include <linux/mempool.h> |
38 | #include <linux/hash.h> | 38 | #include <linux/hash.h> |
39 | #include <linux/compat.h> | ||
39 | 40 | ||
40 | #include <asm/kmap_types.h> | 41 | #include <asm/kmap_types.h> |
41 | #include <asm/uaccess.h> | 42 | #include <asm/uaccess.h> |
@@ -1384,13 +1385,22 @@ static ssize_t aio_fsync(struct kiocb *iocb) | |||
1384 | return ret; | 1385 | return ret; |
1385 | } | 1386 | } |
1386 | 1387 | ||
1387 | static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb) | 1388 | static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb, bool compat) |
1388 | { | 1389 | { |
1389 | ssize_t ret; | 1390 | ssize_t ret; |
1390 | 1391 | ||
1391 | ret = rw_copy_check_uvector(type, (struct iovec __user *)kiocb->ki_buf, | 1392 | #ifdef CONFIG_COMPAT |
1392 | kiocb->ki_nbytes, 1, | 1393 | if (compat) |
1393 | &kiocb->ki_inline_vec, &kiocb->ki_iovec); | 1394 | ret = compat_rw_copy_check_uvector(type, |
1395 | (struct compat_iovec __user *)kiocb->ki_buf, | ||
1396 | kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec, | ||
1397 | &kiocb->ki_iovec); | ||
1398 | else | ||
1399 | #endif | ||
1400 | ret = rw_copy_check_uvector(type, | ||
1401 | (struct iovec __user *)kiocb->ki_buf, | ||
1402 | kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec, | ||
1403 | &kiocb->ki_iovec); | ||
1394 | if (ret < 0) | 1404 | if (ret < 0) |
1395 | goto out; | 1405 | goto out; |
1396 | 1406 | ||
@@ -1420,7 +1430,7 @@ static ssize_t aio_setup_single_vector(struct kiocb *kiocb) | |||
1420 | * Performs the initial checks and aio retry method | 1430 | * Performs the initial checks and aio retry method |
1421 | * setup for the kiocb at the time of io submission. | 1431 | * setup for the kiocb at the time of io submission. |
1422 | */ | 1432 | */ |
1423 | static ssize_t aio_setup_iocb(struct kiocb *kiocb) | 1433 | static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat) |
1424 | { | 1434 | { |
1425 | struct file *file = kiocb->ki_filp; | 1435 | struct file *file = kiocb->ki_filp; |
1426 | ssize_t ret = 0; | 1436 | ssize_t ret = 0; |
@@ -1469,7 +1479,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb) | |||
1469 | ret = security_file_permission(file, MAY_READ); | 1479 | ret = security_file_permission(file, MAY_READ); |
1470 | if (unlikely(ret)) | 1480 | if (unlikely(ret)) |
1471 | break; | 1481 | break; |
1472 | ret = aio_setup_vectored_rw(READ, kiocb); | 1482 | ret = aio_setup_vectored_rw(READ, kiocb, compat); |
1473 | if (ret) | 1483 | if (ret) |
1474 | break; | 1484 | break; |
1475 | ret = -EINVAL; | 1485 | ret = -EINVAL; |
@@ -1483,7 +1493,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb) | |||
1483 | ret = security_file_permission(file, MAY_WRITE); | 1493 | ret = security_file_permission(file, MAY_WRITE); |
1484 | if (unlikely(ret)) | 1494 | if (unlikely(ret)) |
1485 | break; | 1495 | break; |
1486 | ret = aio_setup_vectored_rw(WRITE, kiocb); | 1496 | ret = aio_setup_vectored_rw(WRITE, kiocb, compat); |
1487 | if (ret) | 1497 | if (ret) |
1488 | break; | 1498 | break; |
1489 | ret = -EINVAL; | 1499 | ret = -EINVAL; |
@@ -1548,7 +1558,8 @@ static void aio_batch_free(struct hlist_head *batch_hash) | |||
1548 | } | 1558 | } |
1549 | 1559 | ||
1550 | static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, | 1560 | static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, |
1551 | struct iocb *iocb, struct hlist_head *batch_hash) | 1561 | struct iocb *iocb, struct hlist_head *batch_hash, |
1562 | bool compat) | ||
1552 | { | 1563 | { |
1553 | struct kiocb *req; | 1564 | struct kiocb *req; |
1554 | struct file *file; | 1565 | struct file *file; |
@@ -1609,7 +1620,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, | |||
1609 | req->ki_left = req->ki_nbytes = iocb->aio_nbytes; | 1620 | req->ki_left = req->ki_nbytes = iocb->aio_nbytes; |
1610 | req->ki_opcode = iocb->aio_lio_opcode; | 1621 | req->ki_opcode = iocb->aio_lio_opcode; |
1611 | 1622 | ||
1612 | ret = aio_setup_iocb(req); | 1623 | ret = aio_setup_iocb(req, compat); |
1613 | 1624 | ||
1614 | if (ret) | 1625 | if (ret) |
1615 | goto out_put_req; | 1626 | goto out_put_req; |
@@ -1637,20 +1648,8 @@ out_put_req: | |||
1637 | return ret; | 1648 | return ret; |
1638 | } | 1649 | } |
1639 | 1650 | ||
1640 | /* sys_io_submit: | 1651 | long do_io_submit(aio_context_t ctx_id, long nr, |
1641 | * Queue the nr iocbs pointed to by iocbpp for processing. Returns | 1652 | struct iocb __user *__user *iocbpp, bool compat) |
1642 | * the number of iocbs queued. May return -EINVAL if the aio_context | ||
1643 | * specified by ctx_id is invalid, if nr is < 0, if the iocb at | ||
1644 | * *iocbpp[0] is not properly initialized, if the operation specified | ||
1645 | * is invalid for the file descriptor in the iocb. May fail with | ||
1646 | * -EFAULT if any of the data structures point to invalid data. May | ||
1647 | * fail with -EBADF if the file descriptor specified in the first | ||
1648 | * iocb is invalid. May fail with -EAGAIN if insufficient resources | ||
1649 | * are available to queue any iocbs. Will return 0 if nr is 0. Will | ||
1650 | * fail with -ENOSYS if not implemented. | ||
1651 | */ | ||
1652 | SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, | ||
1653 | struct iocb __user * __user *, iocbpp) | ||
1654 | { | 1653 | { |
1655 | struct kioctx *ctx; | 1654 | struct kioctx *ctx; |
1656 | long ret = 0; | 1655 | long ret = 0; |
@@ -1687,7 +1686,7 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, | |||
1687 | break; | 1686 | break; |
1688 | } | 1687 | } |
1689 | 1688 | ||
1690 | ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash); | 1689 | ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash, compat); |
1691 | if (ret) | 1690 | if (ret) |
1692 | break; | 1691 | break; |
1693 | } | 1692 | } |
@@ -1697,6 +1696,24 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, | |||
1697 | return i ? i : ret; | 1696 | return i ? i : ret; |
1698 | } | 1697 | } |
1699 | 1698 | ||
1699 | /* sys_io_submit: | ||
1700 | * Queue the nr iocbs pointed to by iocbpp for processing. Returns | ||
1701 | * the number of iocbs queued. May return -EINVAL if the aio_context | ||
1702 | * specified by ctx_id is invalid, if nr is < 0, if the iocb at | ||
1703 | * *iocbpp[0] is not properly initialized, if the operation specified | ||
1704 | * is invalid for the file descriptor in the iocb. May fail with | ||
1705 | * -EFAULT if any of the data structures point to invalid data. May | ||
1706 | * fail with -EBADF if the file descriptor specified in the first | ||
1707 | * iocb is invalid. May fail with -EAGAIN if insufficient resources | ||
1708 | * are available to queue any iocbs. Will return 0 if nr is 0. Will | ||
1709 | * fail with -ENOSYS if not implemented. | ||
1710 | */ | ||
1711 | SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, | ||
1712 | struct iocb __user * __user *, iocbpp) | ||
1713 | { | ||
1714 | return do_io_submit(ctx_id, nr, iocbpp, 0); | ||
1715 | } | ||
1716 | |||
1700 | /* lookup_kiocb | 1717 | /* lookup_kiocb |
1701 | * Finds a given iocb for cancellation. | 1718 | * Finds a given iocb for cancellation. |
1702 | */ | 1719 | */ |