Skip to content

Commit 073f21a

Browse files
committed
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
2 parents 497b5b1 + 7572777 commit 073f21a

File tree

1 file changed

+66
-6
lines changed

1 file changed

+66
-6
lines changed

fs/fuse/file.c

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/kernel.h>
1414
#include <linux/sched.h>
1515
#include <linux/module.h>
16+
#include <linux/compat.h>
1617

1718
static const struct file_operations fuse_direct_io_file_operations;
1819

@@ -1627,6 +1628,58 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
16271628
return 0;
16281629
}
16291630

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+
16301683
/*
16311684
* For ioctls, there is no generic way to determine how much memory
16321685
* 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,
18081861
in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
18091862
goto out;
18101863

1811-
err = -EIO;
1812-
if ((in_iovs + out_iovs) * sizeof(struct iovec) != transferred)
1813-
goto out;
1814-
1815-
/* okay, copy in iovs and retry */
18161864
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);
18181868
kunmap_atomic(vaddr, KM_USER0);
1869+
if (err)
1870+
goto out;
18191871

18201872
in_iov = page_address(iov_page);
18211873
out_iov = in_iov + in_iovs;
18221874

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+
18231883
goto retry;
18241884
}
18251885

0 commit comments

Comments
 (0)