diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-12-20 07:04:57 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-12-22 22:58:37 -0500 |
commit | c00d2c7e89880036f288a764599b2b8b87c0a364 (patch) | |
tree | 4f8690123b657080bd6c682e5f326b1b118ed630 | |
parent | e93b1cc8a8965da137ffea0b88e5f62fa1d2a9e6 (diff) |
move aio compat to fs/aio.c
... and fix the minor buglet in compat io_submit() - native one
kills ioctx as cleanup when put_user() fails. Get rid of
bogus compat_... in !CONFIG_AIO case, while we are at it - they
should simply fail with ENOSYS, same as for native counterparts.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/aio.c | 97 | ||||
-rw-r--r-- | fs/compat.c | 75 | ||||
-rw-r--r-- | include/linux/aio.h | 5 | ||||
-rw-r--r-- | kernel/sys_ni.c | 3 |
4 files changed, 98 insertions, 82 deletions
@@ -1367,6 +1367,39 @@ out: | |||
1367 | return ret; | 1367 | return ret; |
1368 | } | 1368 | } |
1369 | 1369 | ||
1370 | #ifdef CONFIG_COMPAT | ||
1371 | COMPAT_SYSCALL_DEFINE2(io_setup, unsigned, nr_events, u32 __user *, ctx32p) | ||
1372 | { | ||
1373 | struct kioctx *ioctx = NULL; | ||
1374 | unsigned long ctx; | ||
1375 | long ret; | ||
1376 | |||
1377 | ret = get_user(ctx, ctx32p); | ||
1378 | if (unlikely(ret)) | ||
1379 | goto out; | ||
1380 | |||
1381 | ret = -EINVAL; | ||
1382 | if (unlikely(ctx || nr_events == 0)) { | ||
1383 | pr_debug("EINVAL: ctx %lu nr_events %u\n", | ||
1384 | ctx, nr_events); | ||
1385 | goto out; | ||
1386 | } | ||
1387 | |||
1388 | ioctx = ioctx_alloc(nr_events); | ||
1389 | ret = PTR_ERR(ioctx); | ||
1390 | if (!IS_ERR(ioctx)) { | ||
1391 | /* truncating is ok because it's a user address */ | ||
1392 | ret = put_user((u32)ioctx->user_id, ctx32p); | ||
1393 | if (ret) | ||
1394 | kill_ioctx(current->mm, ioctx, NULL); | ||
1395 | percpu_ref_put(&ioctx->users); | ||
1396 | } | ||
1397 | |||
1398 | out: | ||
1399 | return ret; | ||
1400 | } | ||
1401 | #endif | ||
1402 | |||
1370 | /* sys_io_destroy: | 1403 | /* sys_io_destroy: |
1371 | * Destroy the aio_context specified. May cancel any outstanding | 1404 | * Destroy the aio_context specified. May cancel any outstanding |
1372 | * AIOs and block on completion. Will fail with -ENOSYS if not | 1405 | * AIOs and block on completion. Will fail with -ENOSYS if not |
@@ -1591,8 +1624,8 @@ out_put_req: | |||
1591 | return ret; | 1624 | return ret; |
1592 | } | 1625 | } |
1593 | 1626 | ||
1594 | long do_io_submit(aio_context_t ctx_id, long nr, | 1627 | static long do_io_submit(aio_context_t ctx_id, long nr, |
1595 | struct iocb __user *__user *iocbpp, bool compat) | 1628 | struct iocb __user *__user *iocbpp, bool compat) |
1596 | { | 1629 | { |
1597 | struct kioctx *ctx; | 1630 | struct kioctx *ctx; |
1598 | long ret = 0; | 1631 | long ret = 0; |
@@ -1662,6 +1695,44 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, | |||
1662 | return do_io_submit(ctx_id, nr, iocbpp, 0); | 1695 | return do_io_submit(ctx_id, nr, iocbpp, 0); |
1663 | } | 1696 | } |
1664 | 1697 | ||
1698 | #ifdef CONFIG_COMPAT | ||
1699 | static inline long | ||
1700 | copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64) | ||
1701 | { | ||
1702 | compat_uptr_t uptr; | ||
1703 | int i; | ||
1704 | |||
1705 | for (i = 0; i < nr; ++i) { | ||
1706 | if (get_user(uptr, ptr32 + i)) | ||
1707 | return -EFAULT; | ||
1708 | if (put_user(compat_ptr(uptr), ptr64 + i)) | ||
1709 | return -EFAULT; | ||
1710 | } | ||
1711 | return 0; | ||
1712 | } | ||
1713 | |||
1714 | #define MAX_AIO_SUBMITS (PAGE_SIZE/sizeof(struct iocb *)) | ||
1715 | |||
1716 | COMPAT_SYSCALL_DEFINE3(io_submit, compat_aio_context_t, ctx_id, | ||
1717 | int, nr, u32 __user *, iocb) | ||
1718 | { | ||
1719 | struct iocb __user * __user *iocb64; | ||
1720 | long ret; | ||
1721 | |||
1722 | if (unlikely(nr < 0)) | ||
1723 | return -EINVAL; | ||
1724 | |||
1725 | if (nr > MAX_AIO_SUBMITS) | ||
1726 | nr = MAX_AIO_SUBMITS; | ||
1727 | |||
1728 | iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64)); | ||
1729 | ret = copy_iocb(nr, iocb, iocb64); | ||
1730 | if (!ret) | ||
1731 | ret = do_io_submit(ctx_id, nr, iocb64, 1); | ||
1732 | return ret; | ||
1733 | } | ||
1734 | #endif | ||
1735 | |||
1665 | /* lookup_kiocb | 1736 | /* lookup_kiocb |
1666 | * Finds a given iocb for cancellation. | 1737 | * Finds a given iocb for cancellation. |
1667 | */ | 1738 | */ |
@@ -1761,3 +1832,25 @@ SYSCALL_DEFINE5(io_getevents, aio_context_t, ctx_id, | |||
1761 | } | 1832 | } |
1762 | return ret; | 1833 | return ret; |
1763 | } | 1834 | } |
1835 | |||
1836 | #ifdef CONFIG_COMPAT | ||
1837 | COMPAT_SYSCALL_DEFINE5(io_getevents, compat_aio_context_t, ctx_id, | ||
1838 | compat_long_t, min_nr, | ||
1839 | compat_long_t, nr, | ||
1840 | struct io_event __user *, events, | ||
1841 | struct compat_timespec __user *, timeout) | ||
1842 | { | ||
1843 | struct timespec t; | ||
1844 | struct timespec __user *ut = NULL; | ||
1845 | |||
1846 | if (timeout) { | ||
1847 | if (compat_get_timespec(&t, timeout)) | ||
1848 | return -EFAULT; | ||
1849 | |||
1850 | ut = compat_alloc_user_space(sizeof(*ut)); | ||
1851 | if (copy_to_user(ut, &t, sizeof(t))) | ||
1852 | return -EFAULT; | ||
1853 | } | ||
1854 | return sys_io_getevents(ctx_id, min_nr, nr, events, ut); | ||
1855 | } | ||
1856 | #endif | ||
diff --git a/fs/compat.c b/fs/compat.c index 543b48c29ac3..3f4908c28698 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -487,45 +487,6 @@ COMPAT_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, | |||
487 | return compat_sys_fcntl64(fd, cmd, arg); | 487 | return compat_sys_fcntl64(fd, cmd, arg); |
488 | } | 488 | } |
489 | 489 | ||
490 | COMPAT_SYSCALL_DEFINE2(io_setup, unsigned, nr_reqs, u32 __user *, ctx32p) | ||
491 | { | ||
492 | long ret; | ||
493 | aio_context_t ctx64; | ||
494 | |||
495 | mm_segment_t oldfs = get_fs(); | ||
496 | if (unlikely(get_user(ctx64, ctx32p))) | ||
497 | return -EFAULT; | ||
498 | |||
499 | set_fs(KERNEL_DS); | ||
500 | /* The __user pointer cast is valid because of the set_fs() */ | ||
501 | ret = sys_io_setup(nr_reqs, (aio_context_t __user *) &ctx64); | ||
502 | set_fs(oldfs); | ||
503 | /* truncating is ok because it's a user address */ | ||
504 | if (!ret) | ||
505 | ret = put_user((u32) ctx64, ctx32p); | ||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | COMPAT_SYSCALL_DEFINE5(io_getevents, compat_aio_context_t, ctx_id, | ||
510 | compat_long_t, min_nr, | ||
511 | compat_long_t, nr, | ||
512 | struct io_event __user *, events, | ||
513 | struct compat_timespec __user *, timeout) | ||
514 | { | ||
515 | struct timespec t; | ||
516 | struct timespec __user *ut = NULL; | ||
517 | |||
518 | if (timeout) { | ||
519 | if (compat_get_timespec(&t, timeout)) | ||
520 | return -EFAULT; | ||
521 | |||
522 | ut = compat_alloc_user_space(sizeof(*ut)); | ||
523 | if (copy_to_user(ut, &t, sizeof(t)) ) | ||
524 | return -EFAULT; | ||
525 | } | ||
526 | return sys_io_getevents(ctx_id, min_nr, nr, events, ut); | ||
527 | } | ||
528 | |||
529 | /* A write operation does a read from user space and vice versa */ | 490 | /* A write operation does a read from user space and vice versa */ |
530 | #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) | 491 | #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) |
531 | 492 | ||
@@ -602,42 +563,6 @@ out: | |||
602 | return ret; | 563 | return ret; |
603 | } | 564 | } |
604 | 565 | ||
605 | static inline long | ||
606 | copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64) | ||
607 | { | ||
608 | compat_uptr_t uptr; | ||
609 | int i; | ||
610 | |||
611 | for (i = 0; i < nr; ++i) { | ||
612 | if (get_user(uptr, ptr32 + i)) | ||
613 | return -EFAULT; | ||
614 | if (put_user(compat_ptr(uptr), ptr64 + i)) | ||
615 | return -EFAULT; | ||
616 | } | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | #define MAX_AIO_SUBMITS (PAGE_SIZE/sizeof(struct iocb *)) | ||
621 | |||
622 | COMPAT_SYSCALL_DEFINE3(io_submit, compat_aio_context_t, ctx_id, | ||
623 | int, nr, u32 __user *, iocb) | ||
624 | { | ||
625 | struct iocb __user * __user *iocb64; | ||
626 | long ret; | ||
627 | |||
628 | if (unlikely(nr < 0)) | ||
629 | return -EINVAL; | ||
630 | |||
631 | if (nr > MAX_AIO_SUBMITS) | ||
632 | nr = MAX_AIO_SUBMITS; | ||
633 | |||
634 | iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64)); | ||
635 | ret = copy_iocb(nr, iocb, iocb64); | ||
636 | if (!ret) | ||
637 | ret = do_io_submit(ctx_id, nr, iocb64, 1); | ||
638 | return ret; | ||
639 | } | ||
640 | |||
641 | struct compat_ncp_mount_data { | 566 | struct compat_ncp_mount_data { |
642 | compat_int_t version; | 567 | compat_int_t version; |
643 | compat_uint_t ncp_fd; | 568 | compat_uint_t ncp_fd; |
diff --git a/include/linux/aio.h b/include/linux/aio.h index 9eb42dbc5582..fdd0a343f455 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h | |||
@@ -14,14 +14,9 @@ typedef int (kiocb_cancel_fn)(struct kiocb *); | |||
14 | /* prototypes */ | 14 | /* prototypes */ |
15 | #ifdef CONFIG_AIO | 15 | #ifdef CONFIG_AIO |
16 | extern void exit_aio(struct mm_struct *mm); | 16 | extern void exit_aio(struct mm_struct *mm); |
17 | extern long do_io_submit(aio_context_t ctx_id, long nr, | ||
18 | struct iocb __user *__user *iocbpp, bool compat); | ||
19 | void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); | 17 | void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); |
20 | #else | 18 | #else |
21 | static inline void exit_aio(struct mm_struct *mm) { } | 19 | static inline void exit_aio(struct mm_struct *mm) { } |
22 | static inline long do_io_submit(aio_context_t ctx_id, long nr, | ||
23 | struct iocb __user * __user *iocbpp, | ||
24 | bool compat) { return 0; } | ||
25 | static inline void kiocb_set_cancel_fn(struct kiocb *req, | 20 | static inline void kiocb_set_cancel_fn(struct kiocb *req, |
26 | kiocb_cancel_fn *cancel) { } | 21 | kiocb_cancel_fn *cancel) { } |
27 | #endif /* CONFIG_AIO */ | 22 | #endif /* CONFIG_AIO */ |
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 635482e60ca3..8acef8576ce9 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c | |||
@@ -150,6 +150,9 @@ cond_syscall(sys_io_destroy); | |||
150 | cond_syscall(sys_io_submit); | 150 | cond_syscall(sys_io_submit); |
151 | cond_syscall(sys_io_cancel); | 151 | cond_syscall(sys_io_cancel); |
152 | cond_syscall(sys_io_getevents); | 152 | cond_syscall(sys_io_getevents); |
153 | cond_syscall(compat_sys_io_setup); | ||
154 | cond_syscall(compat_sys_io_submit); | ||
155 | cond_syscall(compat_sys_io_getevents); | ||
153 | cond_syscall(sys_sysfs); | 156 | cond_syscall(sys_sysfs); |
154 | cond_syscall(sys_syslog); | 157 | cond_syscall(sys_syslog); |
155 | cond_syscall(sys_process_vm_readv); | 158 | cond_syscall(sys_process_vm_readv); |