Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+58 -5
+48 -5
fs/fuse/file.c
··· 1634 1634 * and 64bit. Fortunately we can determine which structure the server 1635 1635 * used from the size of the reply. 1636 1636 */ 1637 - static int fuse_copy_ioctl_iovec(struct iovec *dst, void *src, 1638 - size_t transferred, unsigned count, 1639 - bool is_compat) 1637 + static int fuse_copy_ioctl_iovec_old(struct iovec *dst, void *src, 1638 + size_t transferred, unsigned count, 1639 + bool is_compat) 1640 1640 { 1641 1641 #ifdef CONFIG_COMPAT 1642 1642 if (count * sizeof(struct compat_iovec) == transferred) { ··· 1679 1679 } 1680 1680 return 0; 1681 1681 } 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 + 1682 1718 1683 1719 /* 1684 1720 * For ioctls, there is no generic way to determine how much memory ··· 1782 1746 size_t in_size, out_size, transferred; 1783 1747 int err; 1784 1748 1749 + #if BITS_PER_LONG == 32 1750 + inarg.flags |= FUSE_IOCTL_32BIT; 1751 + #else 1752 + if (flags & FUSE_IOCTL_COMPAT) 1753 + inarg.flags |= FUSE_IOCTL_32BIT; 1754 + #endif 1755 + 1785 1756 /* assume all the iovs returned by client always fits in a page */ 1786 - BUILD_BUG_ON(sizeof(struct iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); 1757 + BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); 1787 1758 1788 1759 err = -ENOMEM; 1789 1760 pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL); ··· 1905 1862 goto out; 1906 1863 1907 1864 vaddr = kmap_atomic(pages[0], KM_USER0); 1908 - err = fuse_copy_ioctl_iovec(iov_page, vaddr, 1865 + err = fuse_copy_ioctl_iovec(fc, iov_page, vaddr, 1909 1866 transferred, in_iovs + out_iovs, 1910 1867 (flags & FUSE_IOCTL_COMPAT) != 0); 1911 1868 kunmap_atomic(vaddr, KM_USER0);
+10
include/linux/fuse.h
··· 44 44 * 45 45 * 7.16 46 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 52 #ifndef _LINUX_FUSE_H ··· 206 203 * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine 207 204 * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed 208 205 * FUSE_IOCTL_RETRY: retry with new iovecs 206 + * FUSE_IOCTL_32BIT: 32bit ioctl 209 207 * 210 208 * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs 211 209 */ 212 210 #define FUSE_IOCTL_COMPAT (1 << 0) 213 211 #define FUSE_IOCTL_UNRESTRICTED (1 << 1) 214 212 #define FUSE_IOCTL_RETRY (1 << 2) 213 + #define FUSE_IOCTL_32BIT (1 << 3) 215 214 216 215 #define FUSE_IOCTL_MAX_IOV 256 217 216 ··· 527 522 __u64 arg; 528 523 __u32 in_size; 529 524 __u32 out_size; 525 + }; 526 + 527 + struct fuse_ioctl_iovec { 528 + __u64 base; 529 + __u64 len; 530 530 }; 531 531 532 532 struct fuse_ioctl_out {