|
13 | 13 | #include <linux/kernel.h>
|
14 | 14 | #include <linux/sched.h>
|
15 | 15 | #include <linux/module.h>
|
| 16 | +#include <linux/compat.h> |
16 | 17 |
|
17 | 18 | static const struct file_operations fuse_direct_io_file_operations;
|
18 | 19 |
|
@@ -1627,6 +1628,58 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
|
1627 | 1628 | return 0;
|
1628 | 1629 | }
|
1629 | 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 | + |
1630 | 1683 | /*
|
1631 | 1684 | * For ioctls, there is no generic way to determine how much memory
|
1632 | 1685 | * needs to be read and/or written. Furthermore, ioctls are allowed
|
@@ -1808,18 +1861,25 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
1808 | 1861 | in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
|
1809 | 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 | 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 | 1868 | kunmap_atomic(vaddr, KM_USER0);
|
| 1869 | + if (err) |
| 1870 | + goto out; |
1819 | 1871 |
|
1820 | 1872 | in_iov = page_address(iov_page);
|
1821 | 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 | 1883 | goto retry;
|
1824 | 1884 | }
|
1825 | 1885 |
|
|
0 commit comments