summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-12-20 07:04:57 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2016-12-22 22:58:37 -0500
commitc00d2c7e89880036f288a764599b2b8b87c0a364 (patch)
tree4f8690123b657080bd6c682e5f326b1b118ed630
parente93b1cc8a8965da137ffea0b88e5f62fa1d2a9e6 (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.c97
-rw-r--r--fs/compat.c75
-rw-r--r--include/linux/aio.h5
-rw-r--r--kernel/sys_ni.c3
4 files changed, 98 insertions, 82 deletions
diff --git a/fs/aio.c b/fs/aio.c
index 8edf253484af..8c79e1a53af9 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -1367,6 +1367,39 @@ out:
1367 return ret; 1367 return ret;
1368} 1368}
1369 1369
1370#ifdef CONFIG_COMPAT
1371COMPAT_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
1398out:
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
1594long do_io_submit(aio_context_t ctx_id, long nr, 1627static 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
1699static inline long
1700copy_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
1716COMPAT_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
1837COMPAT_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
490COMPAT_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
509COMPAT_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
605static inline long
606copy_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
622COMPAT_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
641struct compat_ncp_mount_data { 566struct 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
16extern void exit_aio(struct mm_struct *mm); 16extern void exit_aio(struct mm_struct *mm);
17extern long do_io_submit(aio_context_t ctx_id, long nr,
18 struct iocb __user *__user *iocbpp, bool compat);
19void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); 17void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel);
20#else 18#else
21static inline void exit_aio(struct mm_struct *mm) { } 19static inline void exit_aio(struct mm_struct *mm) { }
22static inline long do_io_submit(aio_context_t ctx_id, long nr,
23 struct iocb __user * __user *iocbpp,
24 bool compat) { return 0; }
25static inline void kiocb_set_cancel_fn(struct kiocb *req, 20static 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);
150cond_syscall(sys_io_submit); 150cond_syscall(sys_io_submit);
151cond_syscall(sys_io_cancel); 151cond_syscall(sys_io_cancel);
152cond_syscall(sys_io_getevents); 152cond_syscall(sys_io_getevents);
153cond_syscall(compat_sys_io_setup);
154cond_syscall(compat_sys_io_submit);
155cond_syscall(compat_sys_io_getevents);
153cond_syscall(sys_sysfs); 156cond_syscall(sys_sysfs);
154cond_syscall(sys_syslog); 157cond_syscall(sys_syslog);
155cond_syscall(sys_process_vm_readv); 158cond_syscall(sys_process_vm_readv);