From 7fde62bffb576d384ea49a3aed3403d5609ee5bc Mon Sep 17 00:00:00 2001From: Chris Mason <chris.mason@oracle.com>Date: Tue, 16 Mar 2010 15:40:10 -0400Subject: [PATCH] Btrfs: buffer results in the space_info ioctlThe space_info ioctl was using copy_to_user inside rcu_read_lock. Thiscommit changes things to copy into a buffer first and then dump theresult down to userland.Signed-off-by: Chris Mason <chris.mason@oracle.com>--- fs/btrfs/ioctl.c | 57 +++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 46 insertions(+), 11 deletions(-)diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.cindex 38a6886..4329610 100644--- a/fs/btrfs/ioctl.c+++ b/fs/btrfs/ioctl.c@@ -1848,39 +1848,74 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) struct btrfs_ioctl_space_args space_args; struct btrfs_ioctl_space_info space; struct btrfs_ioctl_space_info *dest;+ struct btrfs_ioctl_space_info *dest_orig;+ struct btrfs_ioctl_space_info *user_dest; struct btrfs_space_info *info;+ int alloc_size; int ret = 0;+ int slot_count = 0; if (copy_from_user(&space_args, (struct btrfs_ioctl_space_args __user *)arg, sizeof(space_args))) return -EFAULT;+ /* first we count slots */+ rcu_read_lock();+ list_for_each_entry_rcu(info, &root->fs_info->space_info, list)+ slot_count++;+ rcu_read_unlock();++ /* space_slots == 0 means they are asking for a count */+ if (space_args.space_slots == 0) {+ space_args.total_spaces = slot_count;+ goto out;+ }+ alloc_size = sizeof(*dest) * slot_count;+ /* we generally have at most 6 or so space infos, one for each raid+ * level. So, a whole page should be more than enough for everyone+ */+ if (alloc_size > PAGE_CACHE_SIZE)+ return -ENOMEM;+ space_args.total_spaces = 0;- dest = (struct btrfs_ioctl_space_info *)- (arg + sizeof(struct btrfs_ioctl_space_args));+ dest = kmalloc(alloc_size, GFP_NOFS);+ if (!dest)+ return -ENOMEM;+ dest_orig = dest;+ /* now we have a buffer to copy into */ rcu_read_lock(); list_for_each_entry_rcu(info, &root->fs_info->space_info, list) {- if (!space_args.space_slots) {- space_args.total_spaces++;- continue;- }+ /* make sure we don't copy more than we allocated+ * in our buffer+ */+ if (slot_count == 0)+ break;+ slot_count--;++ /* make sure userland has enough room in their buffer */ if (space_args.total_spaces >= space_args.space_slots) break;+ space.flags = info->flags; space.total_bytes = info->total_bytes; space.used_bytes = info->bytes_used;- if (copy_to_user(dest, &space, sizeof(space))) {- ret = -EFAULT;- break;- }+ memcpy(dest, &space, sizeof(space)); dest++; space_args.total_spaces++; } rcu_read_unlock();- if (copy_to_user(arg, &space_args, sizeof(space_args)))+ user_dest = (struct btrfs_ioctl_space_info *)+ (arg + sizeof(struct btrfs_ioctl_space_args));++ if (copy_to_user(user_dest, dest_orig, alloc_size))+ ret = -EFAULT;++ kfree(dest_orig);+out:+ if (ret == 0 && copy_to_user(arg, &space_args, sizeof(space_args))) ret = -EFAULT; return ret;-- 1.7.0.2