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

btrfs: tree_search, search_ioctl: direct copy to userspace

By copying each found item seperatly to userspace, we do not need extra
buffer in the kernel.

Signed-off-by: Gerhard Heift <Gerhard@Heift.Name>
Signed-off-by: Chris Mason <clm@fb.com>
Acked-by: David Sterba <dsterba@suse.cz>

authored by

Gerhard Heift and committed by
Chris Mason
ba346b35 550ac1d8

+33 -15
+33 -15
fs/btrfs/ioctl.c
··· 1958 1958 struct btrfs_key *key, 1959 1959 struct btrfs_ioctl_search_key *sk, 1960 1960 size_t *buf_size, 1961 - char *buf, 1961 + char __user *ubuf, 1962 1962 unsigned long *sk_offset, 1963 1963 int *num_found) 1964 1964 { ··· 2018 2018 sh.transid = found_transid; 2019 2019 2020 2020 /* copy search result header */ 2021 - memcpy(buf + *sk_offset, &sh, sizeof(sh)); 2021 + if (copy_to_user(ubuf + *sk_offset, &sh, sizeof(sh))) { 2022 + ret = -EFAULT; 2023 + goto out; 2024 + } 2025 + 2022 2026 *sk_offset += sizeof(sh); 2023 2027 2024 2028 if (item_len) { 2025 - char *p = buf + *sk_offset; 2029 + char __user *up = ubuf + *sk_offset; 2026 2030 /* copy the item */ 2027 - read_extent_buffer(leaf, p, 2028 - item_off, item_len); 2031 + if (read_extent_buffer_to_user(leaf, up, 2032 + item_off, item_len)) { 2033 + ret = -EFAULT; 2034 + goto out; 2035 + } 2036 + 2029 2037 *sk_offset += item_len; 2030 2038 } 2031 2039 (*num_found)++; ··· 2060 2052 } else 2061 2053 ret = 1; 2062 2054 out: 2055 + /* 2056 + * 0: all items from this leaf copied, continue with next 2057 + * 1: * more items can be copied, but unused buffer is too small 2058 + * * all items were found 2059 + * Either way, it will stops the loop which iterates to the next 2060 + * leaf 2061 + * -EOVERFLOW: item was to large for buffer 2062 + * -EFAULT: could not copy extent buffer back to userspace 2063 + */ 2063 2064 return ret; 2064 2065 } 2065 2066 2066 2067 static noinline int search_ioctl(struct inode *inode, 2067 2068 struct btrfs_ioctl_search_key *sk, 2068 2069 size_t *buf_size, 2069 - char *buf) 2070 + char __user *ubuf) 2070 2071 { 2071 2072 struct btrfs_root *root; 2072 2073 struct btrfs_key key; ··· 2123 2106 ret = 0; 2124 2107 goto err; 2125 2108 } 2126 - ret = copy_to_sk(root, path, &key, sk, buf_size, buf, 2109 + ret = copy_to_sk(root, path, &key, sk, buf_size, ubuf, 2127 2110 &sk_offset, &num_found); 2128 2111 btrfs_release_path(path); 2129 2112 if (ret) ··· 2141 2124 static noinline int btrfs_ioctl_tree_search(struct file *file, 2142 2125 void __user *argp) 2143 2126 { 2144 - struct btrfs_ioctl_search_args *args; 2127 + struct btrfs_ioctl_search_args __user *uargs; 2128 + struct btrfs_ioctl_search_key sk; 2145 2129 struct inode *inode; 2146 2130 int ret; 2147 2131 size_t buf_size; ··· 2150 2132 if (!capable(CAP_SYS_ADMIN)) 2151 2133 return -EPERM; 2152 2134 2153 - args = memdup_user(argp, sizeof(*args)); 2154 - if (IS_ERR(args)) 2155 - return PTR_ERR(args); 2135 + uargs = (struct btrfs_ioctl_search_args __user *)argp; 2156 2136 2157 - buf_size = sizeof(args->buf); 2137 + if (copy_from_user(&sk, &uargs->key, sizeof(sk))) 2138 + return -EFAULT; 2139 + 2140 + buf_size = sizeof(uargs->buf); 2158 2141 2159 2142 inode = file_inode(file); 2160 - ret = search_ioctl(inode, &args->key, &buf_size, args->buf); 2143 + ret = search_ioctl(inode, &sk, &buf_size, uargs->buf); 2161 2144 2162 2145 /* 2163 2146 * In the origin implementation an overflow is handled by returning a ··· 2167 2148 if (ret == -EOVERFLOW) 2168 2149 ret = 0; 2169 2150 2170 - if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) 2151 + if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk))) 2171 2152 ret = -EFAULT; 2172 - kfree(args); 2173 2153 return ret; 2174 2154 } 2175 2155