aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe David Borba Manana <fdmanana@gmail.com>2013-08-12 15:56:58 -0400
committerChris Mason <chris.mason@fusionio.com>2013-09-01 08:16:25 -0400
commitf71717502460c5cd6409b66835b17ae00af6d5f1 (patch)
tree3ed17923473b0e93630d5a8e22b9d42363dd3a06
parentd73068018419c5999f594a52998621947dc1f7d0 (diff)
Btrfs: fix race conditions in BTRFS_IOC_FS_INFO ioctl
The handler for the ioctl BTRFS_IOC_FS_INFO was reading the number of devices before acquiring the device list mutex. This could lead to inconsistent results because the update of the device list and the number of devices counter (amongst other counters related to the device list) are updated in volumes.c while holding the device list mutex - except for 2 places, one was volumes.c:btrfs_prepare_sprout() and the other was volumes.c:device_list_add(). For example, if we have 2 devices, with IDs 1 and 2 and then add a new device, with ID 3, and while adding the device is in progress an BTRFS_IOC_FS_INFO ioctl arrives, it could return a number of devices of 2 and a max dev id of 3. This would be incorrect. Also, this ioctl handler was reading the fsid while it can be updated concurrently. This can happen when while a new device is being added and the current filesystem is in seeding mode. Example: $ mkfs.btrfs -f /dev/sdb1 $ mkfs.btrfs -f /dev/sdb2 $ btrfstune -S 1 /dev/sdb1 $ mount /dev/sdb1 /mnt/test $ btrfs device add /dev/sdb2 /mnt/test If during the last step a BTRFS_IOC_FS_INFO ioctl was requested, it could read an fsid that was never valid (some bits part of the old fsid and others part of the new fsid). Also, it could read a number of devices that doesn't match the number of devices in the list and the max device id, as explained before. Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
-rw-r--r--fs/btrfs/ioctl.c2
-rw-r--r--fs/btrfs/volumes.c5
2 files changed, 4 insertions, 3 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 0ce93ac17f69..ddb4bc1252ad 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2453,10 +2453,10 @@ static long btrfs_ioctl_fs_info(struct btrfs_root *root, void __user *arg)
2453 if (!fi_args) 2453 if (!fi_args)
2454 return -ENOMEM; 2454 return -ENOMEM;
2455 2455
2456 mutex_lock(&fs_devices->device_list_mutex);
2456 fi_args->num_devices = fs_devices->num_devices; 2457 fi_args->num_devices = fs_devices->num_devices;
2457 memcpy(&fi_args->fsid, root->fs_info->fsid, sizeof(fi_args->fsid)); 2458 memcpy(&fi_args->fsid, root->fs_info->fsid, sizeof(fi_args->fsid));
2458 2459
2459 mutex_lock(&fs_devices->device_list_mutex);
2460 list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) { 2460 list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
2461 if (device->devid > fi_args->max_id) 2461 if (device->devid > fi_args->max_id)
2462 fi_args->max_id = device->devid; 2462 fi_args->max_id = device->devid;
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 3f1c2c200691..74614e3b5ad3 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -492,10 +492,10 @@ static noinline int device_list_add(const char *path,
492 492
493 mutex_lock(&fs_devices->device_list_mutex); 493 mutex_lock(&fs_devices->device_list_mutex);
494 list_add_rcu(&device->dev_list, &fs_devices->devices); 494 list_add_rcu(&device->dev_list, &fs_devices->devices);
495 fs_devices->num_devices++;
495 mutex_unlock(&fs_devices->device_list_mutex); 496 mutex_unlock(&fs_devices->device_list_mutex);
496 497
497 device->fs_devices = fs_devices; 498 device->fs_devices = fs_devices;
498 fs_devices->num_devices++;
499 } else if (!device->name || strcmp(device->name->str, path)) { 499 } else if (!device->name || strcmp(device->name->str, path)) {
500 name = rcu_string_strdup(path, GFP_NOFS); 500 name = rcu_string_strdup(path, GFP_NOFS);
501 if (!name) 501 if (!name)
@@ -1852,7 +1852,6 @@ static int btrfs_prepare_sprout(struct btrfs_root *root)
1852 mutex_lock(&root->fs_info->fs_devices->device_list_mutex); 1852 mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
1853 list_splice_init_rcu(&fs_devices->devices, &seed_devices->devices, 1853 list_splice_init_rcu(&fs_devices->devices, &seed_devices->devices,
1854 synchronize_rcu); 1854 synchronize_rcu);
1855 mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
1856 1855
1857 list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list); 1856 list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list);
1858 list_for_each_entry(device, &seed_devices->devices, dev_list) { 1857 list_for_each_entry(device, &seed_devices->devices, dev_list) {
@@ -1868,6 +1867,8 @@ static int btrfs_prepare_sprout(struct btrfs_root *root)
1868 generate_random_uuid(fs_devices->fsid); 1867 generate_random_uuid(fs_devices->fsid);
1869 memcpy(root->fs_info->fsid, fs_devices->fsid, BTRFS_FSID_SIZE); 1868 memcpy(root->fs_info->fsid, fs_devices->fsid, BTRFS_FSID_SIZE);
1870 memcpy(disk_super->fsid, fs_devices->fsid, BTRFS_FSID_SIZE); 1869 memcpy(disk_super->fsid, fs_devices->fsid, BTRFS_FSID_SIZE);
1870 mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
1871
1871 super_flags = btrfs_super_flags(disk_super) & 1872 super_flags = btrfs_super_flags(disk_super) &
1872 ~BTRFS_SUPER_FLAG_SEEDING; 1873 ~BTRFS_SUPER_FLAG_SEEDING;
1873 btrfs_set_super_flags(disk_super, super_flags); 1874 btrfs_set_super_flags(disk_super, super_flags);