From ae2a0f749780830a02d32d5e444e30d943a07b21 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Jun 2024 09:07:46 +0200 Subject: [PATCH] 6.9-stable patches added patches: btrfs-zoned-fix-use-after-free-due-to-race-with-dev-replace.patch --- ...er-free-due-to-race-with-dev-replace.patch | 107 ++++++++++++++++++ queue-6.9/series | 1 + 2 files changed, 108 insertions(+) create mode 100644 queue-6.9/btrfs-zoned-fix-use-after-free-due-to-race-with-dev-replace.patch diff --git a/queue-6.9/btrfs-zoned-fix-use-after-free-due-to-race-with-dev-replace.patch b/queue-6.9/btrfs-zoned-fix-use-after-free-due-to-race-with-dev-replace.patch new file mode 100644 index 00000000000..35cabbfc022 --- /dev/null +++ b/queue-6.9/btrfs-zoned-fix-use-after-free-due-to-race-with-dev-replace.patch @@ -0,0 +1,107 @@ +From 0090d6e1b210551e63cf43958dc7a1ec942cdde9 Mon Sep 17 00:00:00 2001 +From: Filipe Manana +Date: Wed, 8 May 2024 11:51:07 +0100 +Subject: btrfs: zoned: fix use-after-free due to race with dev replace + +From: Filipe Manana + +commit 0090d6e1b210551e63cf43958dc7a1ec942cdde9 upstream. + +While loading a zone's info during creation of a block group, we can race +with a device replace operation and then trigger a use-after-free on the +device that was just replaced (source device of the replace operation). + +This happens because at btrfs_load_zone_info() we extract a device from +the chunk map into a local variable and then use the device while not +under the protection of the device replace rwsem. So if there's a device +replace operation happening when we extract the device and that device +is the source of the replace operation, we will trigger a use-after-free +if before we finish using the device the replace operation finishes and +frees the device. + +Fix this by enlarging the critical section under the protection of the +device replace rwsem so that all uses of the device are done inside the +critical section. + +CC: stable@vger.kernel.org # 6.1.x: 15c12fcc50a1: btrfs: zoned: introduce a zone_info struct in btrfs_load_block_group_zone_info +CC: stable@vger.kernel.org # 6.1.x: 09a46725cc84: btrfs: zoned: factor out per-zone logic from btrfs_load_block_group_zone_info +CC: stable@vger.kernel.org # 6.1.x: 9e0e3e74dc69: btrfs: zoned: factor out single bg handling from btrfs_load_block_group_zone_info +CC: stable@vger.kernel.org # 6.1.x: 87463f7e0250: btrfs: zoned: factor out DUP bg handling from btrfs_load_block_group_zone_info +CC: stable@vger.kernel.org # 6.1.x +Reviewed-by: Johannes Thumshirn +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/zoned.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -1290,7 +1290,7 @@ static int btrfs_load_zone_info(struct b + struct btrfs_chunk_map *map) + { + struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; +- struct btrfs_device *device = map->stripes[zone_idx].dev; ++ struct btrfs_device *device; + int dev_replace_is_ongoing = 0; + unsigned int nofs_flag; + struct blk_zone zone; +@@ -1298,7 +1298,11 @@ static int btrfs_load_zone_info(struct b + + info->physical = map->stripes[zone_idx].physical; + ++ down_read(&dev_replace->rwsem); ++ device = map->stripes[zone_idx].dev; ++ + if (!device->bdev) { ++ up_read(&dev_replace->rwsem); + info->alloc_offset = WP_MISSING_DEV; + return 0; + } +@@ -1308,6 +1312,7 @@ static int btrfs_load_zone_info(struct b + __set_bit(zone_idx, active); + + if (!btrfs_dev_is_sequential(device, info->physical)) { ++ up_read(&dev_replace->rwsem); + info->alloc_offset = WP_CONVENTIONAL; + return 0; + } +@@ -1315,11 +1320,9 @@ static int btrfs_load_zone_info(struct b + /* This zone will be used for allocation, so mark this zone non-empty. */ + btrfs_dev_clear_zone_empty(device, info->physical); + +- down_read(&dev_replace->rwsem); + dev_replace_is_ongoing = btrfs_dev_replace_is_ongoing(dev_replace); + if (dev_replace_is_ongoing && dev_replace->tgtdev != NULL) + btrfs_dev_clear_zone_empty(dev_replace->tgtdev, info->physical); +- up_read(&dev_replace->rwsem); + + /* + * The group is mapped to a sequential zone. Get the zone write pointer +@@ -1330,6 +1333,7 @@ static int btrfs_load_zone_info(struct b + ret = btrfs_get_dev_zone(device, info->physical, &zone); + memalloc_nofs_restore(nofs_flag); + if (ret) { ++ up_read(&dev_replace->rwsem); + if (ret != -EIO && ret != -EOPNOTSUPP) + return ret; + info->alloc_offset = WP_MISSING_DEV; +@@ -1341,6 +1345,7 @@ static int btrfs_load_zone_info(struct b + "zoned: unexpected conventional zone %llu on device %s (devid %llu)", + zone.start << SECTOR_SHIFT, rcu_str_deref(device->name), + device->devid); ++ up_read(&dev_replace->rwsem); + return -EIO; + } + +@@ -1368,6 +1373,8 @@ static int btrfs_load_zone_info(struct b + break; + } + ++ up_read(&dev_replace->rwsem); ++ + return 0; + } + diff --git a/queue-6.9/series b/queue-6.9/series index 9d6a130387a..e56e2b02f3a 100644 --- a/queue-6.9/series +++ b/queue-6.9/series @@ -271,3 +271,4 @@ intel_th-pci-add-sapphire-rapids-soc-support.patch intel_th-pci-add-meteor-lake-s-support.patch intel_th-pci-add-lunar-lake-support.patch pmdomain-ti-sci-fix-duplicate-pd-referrals.patch +btrfs-zoned-fix-use-after-free-due-to-race-with-dev-replace.patch -- 2.47.3