All pastes #1842734 Raw Edit

Unnamed

public diff v1 · immutable
#1842734 ·published 2010-03-16 19:45 UTC
rendered paste body
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