Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jeff Moyer and committed by
Linus Torvalds
9d85cba7 b8373363

+47 -25
+41 -24
fs/aio.c
··· 36 36 #include <linux/blkdev.h> 37 37 #include <linux/mempool.h> 38 38 #include <linux/hash.h> 39 + #include <linux/compat.h> 39 40 40 41 #include <asm/kmap_types.h> 41 42 #include <asm/uaccess.h> ··· 1385 1384 return ret; 1386 1385 } 1387 1386 1388 - static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb) 1387 + static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb, bool compat) 1389 1388 { 1390 1389 ssize_t ret; 1391 1390 1392 - ret = rw_copy_check_uvector(type, (struct iovec __user *)kiocb->ki_buf, 1393 - kiocb->ki_nbytes, 1, 1394 - &kiocb->ki_inline_vec, &kiocb->ki_iovec); 1391 + #ifdef CONFIG_COMPAT 1392 + if (compat) 1393 + ret = compat_rw_copy_check_uvector(type, 1394 + (struct compat_iovec __user *)kiocb->ki_buf, 1395 + kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec, 1396 + &kiocb->ki_iovec); 1397 + else 1398 + #endif 1399 + ret = rw_copy_check_uvector(type, 1400 + (struct iovec __user *)kiocb->ki_buf, 1401 + kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec, 1402 + &kiocb->ki_iovec); 1395 1403 if (ret < 0) 1396 1404 goto out; 1397 1405 ··· 1430 1420 * Performs the initial checks and aio retry method 1431 1421 * setup for the kiocb at the time of io submission. 1432 1422 */ 1433 - static ssize_t aio_setup_iocb(struct kiocb *kiocb) 1423 + static ssize_t aio_setup_iocb(struct kiocb *kiocb, bool compat) 1434 1424 { 1435 1425 struct file *file = kiocb->ki_filp; 1436 1426 ssize_t ret = 0; ··· 1479 1469 ret = security_file_permission(file, MAY_READ); 1480 1470 if (unlikely(ret)) 1481 1471 break; 1482 - ret = aio_setup_vectored_rw(READ, kiocb); 1472 + ret = aio_setup_vectored_rw(READ, kiocb, compat); 1483 1473 if (ret) 1484 1474 break; 1485 1475 ret = -EINVAL; ··· 1493 1483 ret = security_file_permission(file, MAY_WRITE); 1494 1484 if (unlikely(ret)) 1495 1485 break; 1496 - ret = aio_setup_vectored_rw(WRITE, kiocb); 1486 + ret = aio_setup_vectored_rw(WRITE, kiocb, compat); 1497 1487 if (ret) 1498 1488 break; 1499 1489 ret = -EINVAL; ··· 1558 1548 } 1559 1549 1560 1550 static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, 1561 - struct iocb *iocb, struct hlist_head *batch_hash) 1551 + struct iocb *iocb, struct hlist_head *batch_hash, 1552 + bool compat) 1562 1553 { 1563 1554 struct kiocb *req; 1564 1555 struct file *file; ··· 1620 1609 req->ki_left = req->ki_nbytes = iocb->aio_nbytes; 1621 1610 req->ki_opcode = iocb->aio_lio_opcode; 1622 1611 1623 - ret = aio_setup_iocb(req); 1612 + ret = aio_setup_iocb(req, compat); 1624 1613 1625 1614 if (ret) 1626 1615 goto out_put_req; ··· 1648 1637 return ret; 1649 1638 } 1650 1639 1651 - /* sys_io_submit: 1652 - * Queue the nr iocbs pointed to by iocbpp for processing. Returns 1653 - * the number of iocbs queued. May return -EINVAL if the aio_context 1654 - * specified by ctx_id is invalid, if nr is < 0, if the iocb at 1655 - * *iocbpp[0] is not properly initialized, if the operation specified 1656 - * is invalid for the file descriptor in the iocb. May fail with 1657 - * -EFAULT if any of the data structures point to invalid data. May 1658 - * fail with -EBADF if the file descriptor specified in the first 1659 - * iocb is invalid. May fail with -EAGAIN if insufficient resources 1660 - * are available to queue any iocbs. Will return 0 if nr is 0. Will 1661 - * fail with -ENOSYS if not implemented. 1662 - */ 1663 - SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, 1664 - struct iocb __user * __user *, iocbpp) 1640 + long do_io_submit(aio_context_t ctx_id, long nr, 1641 + struct iocb __user *__user *iocbpp, bool compat) 1665 1642 { 1666 1643 struct kioctx *ctx; 1667 1644 long ret = 0; ··· 1686 1687 break; 1687 1688 } 1688 1689 1689 - ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash); 1690 + ret = io_submit_one(ctx, user_iocb, &tmp, batch_hash, compat); 1690 1691 if (ret) 1691 1692 break; 1692 1693 } ··· 1694 1695 1695 1696 put_ioctx(ctx); 1696 1697 return i ? i : ret; 1698 + } 1699 + 1700 + /* sys_io_submit: 1701 + * Queue the nr iocbs pointed to by iocbpp for processing. Returns 1702 + * the number of iocbs queued. May return -EINVAL if the aio_context 1703 + * specified by ctx_id is invalid, if nr is < 0, if the iocb at 1704 + * *iocbpp[0] is not properly initialized, if the operation specified 1705 + * is invalid for the file descriptor in the iocb. May fail with 1706 + * -EFAULT if any of the data structures point to invalid data. May 1707 + * fail with -EBADF if the file descriptor specified in the first 1708 + * iocb is invalid. May fail with -EAGAIN if insufficient resources 1709 + * are available to queue any iocbs. Will return 0 if nr is 0. Will 1710 + * fail with -ENOSYS if not implemented. 1711 + */ 1712 + SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, 1713 + struct iocb __user * __user *, iocbpp) 1714 + { 1715 + return do_io_submit(ctx_id, nr, iocbpp, 0); 1697 1716 } 1698 1717 1699 1718 /* lookup_kiocb
+1 -1
fs/compat.c
··· 673 673 iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64)); 674 674 ret = copy_iocb(nr, iocb, iocb64); 675 675 if (!ret) 676 - ret = sys_io_submit(ctx_id, nr, iocb64); 676 + ret = do_io_submit(ctx_id, nr, iocb64, 1); 677 677 return ret; 678 678 } 679 679
+5
include/linux/aio.h
··· 212 212 extern int aio_complete(struct kiocb *iocb, long res, long res2); 213 213 struct mm_struct; 214 214 extern void exit_aio(struct mm_struct *mm); 215 + extern long do_io_submit(aio_context_t ctx_id, long nr, 216 + struct iocb __user *__user *iocbpp, bool compat); 215 217 #else 216 218 static inline ssize_t wait_on_sync_kiocb(struct kiocb *iocb) { return 0; } 217 219 static inline int aio_put_req(struct kiocb *iocb) { return 0; } ··· 221 219 static inline int aio_complete(struct kiocb *iocb, long res, long res2) { return 0; } 222 220 struct mm_struct; 223 221 static inline void exit_aio(struct mm_struct *mm) { } 222 + static inline long do_io_submit(aio_context_t ctx_id, long nr, 223 + struct iocb __user * __user *iocbpp, 224 + bool compat) { return 0; } 224 225 #endif /* CONFIG_AIO */ 225 226 226 227 static inline struct kiocb *list_kiocb(struct list_head *h)