diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-12-14 14:07:39 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-12-14 14:07:39 -0500 |
| commit | 073f21ae1319348f4f8630003b7901e3be254327 (patch) | |
| tree | 2895f1b2c2aa0d2df792aee8afc8e4d4e691fe5c | |
| parent | 497b5b13c9e946651991e0296374a15d38590ce1 (diff) | |
| parent | 7572777eef78ebdee1ecb7c258c0ef94d35bad16 (diff) | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
fuse: verify ioctl retries
fuse: fix ioctl when server is 32bit
| -rw-r--r-- | fs/fuse/file.c | 72 |
1 files changed, 66 insertions, 6 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 9242d294fe90..8b984a2cebbd 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
| 14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/compat.h> | ||
| 16 | 17 | ||
| 17 | static const struct file_operations fuse_direct_io_file_operations; | 18 | static const struct file_operations fuse_direct_io_file_operations; |
| 18 | 19 | ||
| @@ -1628,6 +1629,58 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov, | |||
| 1628 | } | 1629 | } |
| 1629 | 1630 | ||
| 1630 | /* | 1631 | /* |
| 1632 | * CUSE servers compiled on 32bit broke on 64bit kernels because the | ||
| 1633 | * ABI was defined to be 'struct iovec' which is different on 32bit | ||
| 1634 | * and 64bit. Fortunately we can determine which structure the server | ||
| 1635 | * used from the size of the reply. | ||
| 1636 | */ | ||
| 1637 | static int fuse_copy_ioctl_iovec(struct iovec *dst, void *src, | ||
| 1638 | size_t transferred, unsigned count, | ||
| 1639 | bool is_compat) | ||
| 1640 | { | ||
| 1641 | #ifdef CONFIG_COMPAT | ||
| 1642 | if (count * sizeof(struct compat_iovec) == transferred) { | ||
| 1643 | struct compat_iovec *ciov = src; | ||
| 1644 | unsigned i; | ||
| 1645 | |||
| 1646 | /* | ||
| 1647 | * With this interface a 32bit server cannot support | ||
| 1648 | * non-compat (i.e. ones coming from 64bit apps) ioctl | ||
| 1649 | * requests | ||
| 1650 | */ | ||
| 1651 | if (!is_compat) | ||
| 1652 | return -EINVAL; | ||
| 1653 | |||
| 1654 | for (i = 0; i < count; i++) { | ||
| 1655 | dst[i].iov_base = compat_ptr(ciov[i].iov_base); | ||
| 1656 | dst[i].iov_len = ciov[i].iov_len; | ||
| 1657 | } | ||
| 1658 | return 0; | ||
| 1659 | } | ||
| 1660 | #endif | ||
| 1661 | |||
| 1662 | if (count * sizeof(struct iovec) != transferred) | ||
| 1663 | return -EIO; | ||
| 1664 | |||
| 1665 | memcpy(dst, src, transferred); | ||
| 1666 | return 0; | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | /* Make sure iov_length() won't overflow */ | ||
| 1670 | static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count) | ||
| 1671 | { | ||
| 1672 | size_t n; | ||
| 1673 | u32 max = FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT; | ||
| 1674 | |||
| 1675 | for (n = 0; n < count; n++) { | ||
| 1676 | if (iov->iov_len > (size_t) max) | ||
| 1677 | return -ENOMEM; | ||
| 1678 | max -= iov->iov_len; | ||
| 1679 | } | ||
| 1680 | return 0; | ||
| 1681 | } | ||
| 1682 | |||
| 1683 | /* | ||
| 1631 | * For ioctls, there is no generic way to determine how much memory | 1684 | * For ioctls, there is no generic way to determine how much memory |
| 1632 | * needs to be read and/or written. Furthermore, ioctls are allowed | 1685 | * needs to be read and/or written. Furthermore, ioctls are allowed |
| 1633 | * to dereference the passed pointer, so the parameter requires deep | 1686 | * to dereference the passed pointer, so the parameter requires deep |
| @@ -1808,18 +1861,25 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
| 1808 | in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) | 1861 | in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) |
| 1809 | goto out; | 1862 | goto out; |
| 1810 | 1863 | ||
| 1811 | err = -EIO; | ||
| 1812 | if ((in_iovs + out_iovs) * sizeof(struct iovec) != transferred) | ||
| 1813 | goto out; | ||
| 1814 | |||
| 1815 | /* okay, copy in iovs and retry */ | ||
| 1816 | vaddr = kmap_atomic(pages[0], KM_USER0); | 1864 | vaddr = kmap_atomic(pages[0], KM_USER0); |
| 1817 | memcpy(page_address(iov_page), vaddr, transferred); | 1865 | err = fuse_copy_ioctl_iovec(page_address(iov_page), vaddr, |
| 1866 | transferred, in_iovs + out_iovs, | ||
| 1867 | (flags & FUSE_IOCTL_COMPAT) != 0); | ||
| 1818 | kunmap_atomic(vaddr, KM_USER0); | 1868 | kunmap_atomic(vaddr, KM_USER0); |
| 1869 | if (err) | ||
| 1870 | goto out; | ||
| 1819 | 1871 | ||
| 1820 | in_iov = page_address(iov_page); | 1872 | in_iov = page_address(iov_page); |
| 1821 | out_iov = in_iov + in_iovs; | 1873 | out_iov = in_iov + in_iovs; |
| 1822 | 1874 | ||
| 1875 | err = fuse_verify_ioctl_iov(in_iov, in_iovs); | ||
| 1876 | if (err) | ||
| 1877 | goto out; | ||
| 1878 | |||
| 1879 | err = fuse_verify_ioctl_iov(out_iov, out_iovs); | ||
| 1880 | if (err) | ||
| 1881 | goto out; | ||
| 1882 | |||
| 1823 | goto retry; | 1883 | goto retry; |
| 1824 | } | 1884 | } |
| 1825 | 1885 | ||
