diff options
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r-- | fs/btrfs/send.c | 62 |
1 files changed, 41 insertions, 21 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 8d358c547c59..6a8c86074aa4 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
@@ -5939,6 +5939,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
5939 | u32 i; | 5939 | u32 i; |
5940 | u64 *clone_sources_tmp = NULL; | 5940 | u64 *clone_sources_tmp = NULL; |
5941 | int clone_sources_to_rollback = 0; | 5941 | int clone_sources_to_rollback = 0; |
5942 | unsigned alloc_size; | ||
5942 | int sort_clone_roots = 0; | 5943 | int sort_clone_roots = 0; |
5943 | int index; | 5944 | int index; |
5944 | 5945 | ||
@@ -5978,6 +5979,12 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
5978 | goto out; | 5979 | goto out; |
5979 | } | 5980 | } |
5980 | 5981 | ||
5982 | if (arg->clone_sources_count > | ||
5983 | ULLONG_MAX / sizeof(*arg->clone_sources)) { | ||
5984 | ret = -EINVAL; | ||
5985 | goto out; | ||
5986 | } | ||
5987 | |||
5981 | if (!access_ok(VERIFY_READ, arg->clone_sources, | 5988 | if (!access_ok(VERIFY_READ, arg->clone_sources, |
5982 | sizeof(*arg->clone_sources) * | 5989 | sizeof(*arg->clone_sources) * |
5983 | arg->clone_sources_count)) { | 5990 | arg->clone_sources_count)) { |
@@ -6022,40 +6029,53 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
6022 | sctx->clone_roots_cnt = arg->clone_sources_count; | 6029 | sctx->clone_roots_cnt = arg->clone_sources_count; |
6023 | 6030 | ||
6024 | sctx->send_max_size = BTRFS_SEND_BUF_SIZE; | 6031 | sctx->send_max_size = BTRFS_SEND_BUF_SIZE; |
6025 | sctx->send_buf = vmalloc(sctx->send_max_size); | 6032 | sctx->send_buf = kmalloc(sctx->send_max_size, GFP_KERNEL | __GFP_NOWARN); |
6026 | if (!sctx->send_buf) { | 6033 | if (!sctx->send_buf) { |
6027 | ret = -ENOMEM; | 6034 | sctx->send_buf = vmalloc(sctx->send_max_size); |
6028 | goto out; | 6035 | if (!sctx->send_buf) { |
6036 | ret = -ENOMEM; | ||
6037 | goto out; | ||
6038 | } | ||
6029 | } | 6039 | } |
6030 | 6040 | ||
6031 | sctx->read_buf = vmalloc(BTRFS_SEND_READ_SIZE); | 6041 | sctx->read_buf = kmalloc(BTRFS_SEND_READ_SIZE, GFP_KERNEL | __GFP_NOWARN); |
6032 | if (!sctx->read_buf) { | 6042 | if (!sctx->read_buf) { |
6033 | ret = -ENOMEM; | 6043 | sctx->read_buf = vmalloc(BTRFS_SEND_READ_SIZE); |
6034 | goto out; | 6044 | if (!sctx->read_buf) { |
6045 | ret = -ENOMEM; | ||
6046 | goto out; | ||
6047 | } | ||
6035 | } | 6048 | } |
6036 | 6049 | ||
6037 | sctx->pending_dir_moves = RB_ROOT; | 6050 | sctx->pending_dir_moves = RB_ROOT; |
6038 | sctx->waiting_dir_moves = RB_ROOT; | 6051 | sctx->waiting_dir_moves = RB_ROOT; |
6039 | sctx->orphan_dirs = RB_ROOT; | 6052 | sctx->orphan_dirs = RB_ROOT; |
6040 | 6053 | ||
6041 | sctx->clone_roots = vzalloc(sizeof(struct clone_root) * | 6054 | alloc_size = sizeof(struct clone_root) * (arg->clone_sources_count + 1); |
6042 | (arg->clone_sources_count + 1)); | 6055 | |
6056 | sctx->clone_roots = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN); | ||
6043 | if (!sctx->clone_roots) { | 6057 | if (!sctx->clone_roots) { |
6044 | ret = -ENOMEM; | 6058 | sctx->clone_roots = vzalloc(alloc_size); |
6045 | goto out; | 6059 | if (!sctx->clone_roots) { |
6060 | ret = -ENOMEM; | ||
6061 | goto out; | ||
6062 | } | ||
6046 | } | 6063 | } |
6047 | 6064 | ||
6065 | alloc_size = arg->clone_sources_count * sizeof(*arg->clone_sources); | ||
6066 | |||
6048 | if (arg->clone_sources_count) { | 6067 | if (arg->clone_sources_count) { |
6049 | clone_sources_tmp = vmalloc(arg->clone_sources_count * | 6068 | clone_sources_tmp = kmalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN); |
6050 | sizeof(*arg->clone_sources)); | ||
6051 | if (!clone_sources_tmp) { | 6069 | if (!clone_sources_tmp) { |
6052 | ret = -ENOMEM; | 6070 | clone_sources_tmp = vmalloc(alloc_size); |
6053 | goto out; | 6071 | if (!clone_sources_tmp) { |
6072 | ret = -ENOMEM; | ||
6073 | goto out; | ||
6074 | } | ||
6054 | } | 6075 | } |
6055 | 6076 | ||
6056 | ret = copy_from_user(clone_sources_tmp, arg->clone_sources, | 6077 | ret = copy_from_user(clone_sources_tmp, arg->clone_sources, |
6057 | arg->clone_sources_count * | 6078 | alloc_size); |
6058 | sizeof(*arg->clone_sources)); | ||
6059 | if (ret) { | 6079 | if (ret) { |
6060 | ret = -EFAULT; | 6080 | ret = -EFAULT; |
6061 | goto out; | 6081 | goto out; |
@@ -6089,7 +6109,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
6089 | sctx->clone_roots[i].root = clone_root; | 6109 | sctx->clone_roots[i].root = clone_root; |
6090 | clone_sources_to_rollback = i + 1; | 6110 | clone_sources_to_rollback = i + 1; |
6091 | } | 6111 | } |
6092 | vfree(clone_sources_tmp); | 6112 | kvfree(clone_sources_tmp); |
6093 | clone_sources_tmp = NULL; | 6113 | clone_sources_tmp = NULL; |
6094 | } | 6114 | } |
6095 | 6115 | ||
@@ -6207,15 +6227,15 @@ out: | |||
6207 | btrfs_root_dec_send_in_progress(sctx->parent_root); | 6227 | btrfs_root_dec_send_in_progress(sctx->parent_root); |
6208 | 6228 | ||
6209 | kfree(arg); | 6229 | kfree(arg); |
6210 | vfree(clone_sources_tmp); | 6230 | kvfree(clone_sources_tmp); |
6211 | 6231 | ||
6212 | if (sctx) { | 6232 | if (sctx) { |
6213 | if (sctx->send_filp) | 6233 | if (sctx->send_filp) |
6214 | fput(sctx->send_filp); | 6234 | fput(sctx->send_filp); |
6215 | 6235 | ||
6216 | vfree(sctx->clone_roots); | 6236 | kvfree(sctx->clone_roots); |
6217 | vfree(sctx->send_buf); | 6237 | kvfree(sctx->send_buf); |
6218 | vfree(sctx->read_buf); | 6238 | kvfree(sctx->read_buf); |
6219 | 6239 | ||
6220 | name_cache_free(sctx); | 6240 | name_cache_free(sctx); |
6221 | 6241 | ||