diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2010-12-07 14:16:56 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2010-12-07 14:16:56 -0500 |
commit | 1baa26b2be92fe9917e2f7ef46d423b5dfa4da71 (patch) | |
tree | 1b4f24c149a01e74c17cb0c290952dd09778ca1d | |
parent | 02c048b919455aaa38628563cdcc2e691c8a9f53 (diff) |
fuse: fix ioctl ABI
In kernel ABI version 7.16 and later FUSE_IOCTL_RETRY reply from a
unrestricted IOCTL request shall return with an array of 'struct
fuse_ioctl_iovec' instead of 'struct iovec'. This fixes the ABI
ambiguity of 32bit vs. 64bit.
Reported-by: "ccmail111" <ccmail111@yahoo.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
CC: Tejun Heo <tj@kernel.org>
-rw-r--r-- | fs/fuse/file.c | 53 | ||||
-rw-r--r-- | include/linux/fuse.h | 10 |
2 files changed, 58 insertions, 5 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ca3b6bbb3790..95da1bc1c826 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -1634,9 +1634,9 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov, | |||
1634 | * and 64bit. Fortunately we can determine which structure the server | 1634 | * and 64bit. Fortunately we can determine which structure the server |
1635 | * used from the size of the reply. | 1635 | * used from the size of the reply. |
1636 | */ | 1636 | */ |
1637 | static int fuse_copy_ioctl_iovec(struct iovec *dst, void *src, | 1637 | static int fuse_copy_ioctl_iovec_old(struct iovec *dst, void *src, |
1638 | size_t transferred, unsigned count, | 1638 | size_t transferred, unsigned count, |
1639 | bool is_compat) | 1639 | bool is_compat) |
1640 | { | 1640 | { |
1641 | #ifdef CONFIG_COMPAT | 1641 | #ifdef CONFIG_COMPAT |
1642 | if (count * sizeof(struct compat_iovec) == transferred) { | 1642 | if (count * sizeof(struct compat_iovec) == transferred) { |
@@ -1680,6 +1680,42 @@ static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count) | |||
1680 | return 0; | 1680 | return 0; |
1681 | } | 1681 | } |
1682 | 1682 | ||
1683 | static int fuse_copy_ioctl_iovec(struct fuse_conn *fc, struct iovec *dst, | ||
1684 | void *src, size_t transferred, unsigned count, | ||
1685 | bool is_compat) | ||
1686 | { | ||
1687 | unsigned i; | ||
1688 | struct fuse_ioctl_iovec *fiov = src; | ||
1689 | |||
1690 | if (fc->minor < 16) { | ||
1691 | return fuse_copy_ioctl_iovec_old(dst, src, transferred, | ||
1692 | count, is_compat); | ||
1693 | } | ||
1694 | |||
1695 | if (count * sizeof(struct fuse_ioctl_iovec) != transferred) | ||
1696 | return -EIO; | ||
1697 | |||
1698 | for (i = 0; i < count; i++) { | ||
1699 | /* Did the server supply an inappropriate value? */ | ||
1700 | if (fiov[i].base != (unsigned long) fiov[i].base || | ||
1701 | fiov[i].len != (unsigned long) fiov[i].len) | ||
1702 | return -EIO; | ||
1703 | |||
1704 | dst[i].iov_base = (void __user *) (unsigned long) fiov[i].base; | ||
1705 | dst[i].iov_len = (size_t) fiov[i].len; | ||
1706 | |||
1707 | #ifdef CONFIG_COMPAT | ||
1708 | if (is_compat && | ||
1709 | (ptr_to_compat(dst[i].iov_base) != fiov[i].base || | ||
1710 | (compat_size_t) dst[i].iov_len != fiov[i].len)) | ||
1711 | return -EIO; | ||
1712 | #endif | ||
1713 | } | ||
1714 | |||
1715 | return 0; | ||
1716 | } | ||
1717 | |||
1718 | |||
1683 | /* | 1719 | /* |
1684 | * For ioctls, there is no generic way to determine how much memory | 1720 | * For ioctls, there is no generic way to determine how much memory |
1685 | * needs to be read and/or written. Furthermore, ioctls are allowed | 1721 | * needs to be read and/or written. Furthermore, ioctls are allowed |
@@ -1746,8 +1782,15 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1746 | size_t in_size, out_size, transferred; | 1782 | size_t in_size, out_size, transferred; |
1747 | int err; | 1783 | int err; |
1748 | 1784 | ||
1785 | #if BITS_PER_LONG == 32 | ||
1786 | inarg.flags |= FUSE_IOCTL_32BIT; | ||
1787 | #else | ||
1788 | if (flags & FUSE_IOCTL_COMPAT) | ||
1789 | inarg.flags |= FUSE_IOCTL_32BIT; | ||
1790 | #endif | ||
1791 | |||
1749 | /* assume all the iovs returned by client always fits in a page */ | 1792 | /* assume all the iovs returned by client always fits in a page */ |
1750 | BUILD_BUG_ON(sizeof(struct iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); | 1793 | BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); |
1751 | 1794 | ||
1752 | err = -ENOMEM; | 1795 | err = -ENOMEM; |
1753 | pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL); | 1796 | pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL); |
@@ -1862,7 +1905,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1862 | goto out; | 1905 | goto out; |
1863 | 1906 | ||
1864 | vaddr = kmap_atomic(pages[0], KM_USER0); | 1907 | vaddr = kmap_atomic(pages[0], KM_USER0); |
1865 | err = fuse_copy_ioctl_iovec(iov_page, vaddr, | 1908 | err = fuse_copy_ioctl_iovec(fc, iov_page, vaddr, |
1866 | transferred, in_iovs + out_iovs, | 1909 | transferred, in_iovs + out_iovs, |
1867 | (flags & FUSE_IOCTL_COMPAT) != 0); | 1910 | (flags & FUSE_IOCTL_COMPAT) != 0); |
1868 | kunmap_atomic(vaddr, KM_USER0); | 1911 | kunmap_atomic(vaddr, KM_USER0); |
diff --git a/include/linux/fuse.h b/include/linux/fuse.h index cf11881f4938..d464de53db43 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h | |||
@@ -44,6 +44,9 @@ | |||
44 | * | 44 | * |
45 | * 7.16 | 45 | * 7.16 |
46 | * - add BATCH_FORGET request | 46 | * - add BATCH_FORGET request |
47 | * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct | ||
48 | * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' | ||
49 | * - add FUSE_IOCTL_32BIT flag | ||
47 | */ | 50 | */ |
48 | 51 | ||
49 | #ifndef _LINUX_FUSE_H | 52 | #ifndef _LINUX_FUSE_H |
@@ -203,12 +206,14 @@ struct fuse_file_lock { | |||
203 | * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine | 206 | * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine |
204 | * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed | 207 | * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed |
205 | * FUSE_IOCTL_RETRY: retry with new iovecs | 208 | * FUSE_IOCTL_RETRY: retry with new iovecs |
209 | * FUSE_IOCTL_32BIT: 32bit ioctl | ||
206 | * | 210 | * |
207 | * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs | 211 | * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs |
208 | */ | 212 | */ |
209 | #define FUSE_IOCTL_COMPAT (1 << 0) | 213 | #define FUSE_IOCTL_COMPAT (1 << 0) |
210 | #define FUSE_IOCTL_UNRESTRICTED (1 << 1) | 214 | #define FUSE_IOCTL_UNRESTRICTED (1 << 1) |
211 | #define FUSE_IOCTL_RETRY (1 << 2) | 215 | #define FUSE_IOCTL_RETRY (1 << 2) |
216 | #define FUSE_IOCTL_32BIT (1 << 3) | ||
212 | 217 | ||
213 | #define FUSE_IOCTL_MAX_IOV 256 | 218 | #define FUSE_IOCTL_MAX_IOV 256 |
214 | 219 | ||
@@ -524,6 +529,11 @@ struct fuse_ioctl_in { | |||
524 | __u32 out_size; | 529 | __u32 out_size; |
525 | }; | 530 | }; |
526 | 531 | ||
532 | struct fuse_ioctl_iovec { | ||
533 | __u64 base; | ||
534 | __u64 len; | ||
535 | }; | ||
536 | |||
527 | struct fuse_ioctl_out { | 537 | struct fuse_ioctl_out { |
528 | __s32 result; | 538 | __s32 result; |
529 | __u32 flags; | 539 | __u32 flags; |