Skip to content

Commit 37f53a2

Browse files
bmarzinsMikulas Patocka
authored andcommitted
dm: fix dm_blk_report_zones
If dm_get_live_table() returned NULL, dm_put_live_table() was never called. Also, it is possible that md->zone_revalidate_map will change while calling this function. Only read it once, so that we are always using the same value. Otherwise we might miss a call to dm_put_live_table(). Finally, while md->zone_revalidate_map is set and a process is calling blk_revalidate_disk_zones() to set up the zone append emulation resources, it is possible that another process, perhaps triggered by blkdev_report_zones_ioctl(), will call dm_blk_report_zones(). If blk_revalidate_disk_zones() fails, these resources can be freed while the other process is still using them, causing a use-after-free error. blk_revalidate_disk_zones() will only ever be called when initially setting up the zone append emulation resources, such as when setting up a zoned dm-crypt table for the first time. Further table swaps will not set md->zone_revalidate_map or call blk_revalidate_disk_zones(). However it must be called using the new table (referenced by md->zone_revalidate_map) and the new queue limits while the DM device is suspended. dm_blk_report_zones() needs some way to distinguish between a call from blk_revalidate_disk_zones(), which must be allowed to use md->zone_revalidate_map to access this not yet activated table, and all other calls to dm_blk_report_zones(), which should not be allowed while the device is suspended and cannot use md->zone_revalidate_map, since the zone resources might be freed by the process currently calling blk_revalidate_disk_zones(). Solve this by tracking the process that sets md->zone_revalidate_map in dm_revalidate_zones() and only allowing that process to make use of it in dm_blk_report_zones(). Fixes: f211268 ("dm: Use the block layer zone append emulation") Reviewed-by: Damien Le Moal <[email protected]> Tested-by: Damien Le Moal <[email protected]> Signed-off-by: Benjamin Marzinski <[email protected]> Signed-off-by: Mikulas Patocka <[email protected]>
1 parent 4ea30ec commit 37f53a2

File tree

2 files changed

+18
-8
lines changed

2 files changed

+18
-8
lines changed

drivers/md/dm-core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ struct mapped_device {
141141
#ifdef CONFIG_BLK_DEV_ZONED
142142
unsigned int nr_zones;
143143
void *zone_revalidate_map;
144+
struct task_struct *revalidate_map_task;
144145
#endif
145146

146147
#ifdef CONFIG_IMA

drivers/md/dm-zone.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,31 @@ int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
5656
{
5757
struct mapped_device *md = disk->private_data;
5858
struct dm_table *map;
59-
int srcu_idx, ret;
59+
struct dm_table *zone_revalidate_map = md->zone_revalidate_map;
60+
int srcu_idx, ret = -EIO;
61+
bool put_table = false;
6062

61-
if (!md->zone_revalidate_map) {
62-
/* Regular user context */
63+
if (!zone_revalidate_map || md->revalidate_map_task != current) {
64+
/*
65+
* Regular user context or
66+
* Zone revalidation during __bind() is in progress, but this
67+
* call is from a different process
68+
*/
6369
if (dm_suspended_md(md))
6470
return -EAGAIN;
6571

6672
map = dm_get_live_table(md, &srcu_idx);
67-
if (!map)
68-
return -EIO;
73+
put_table = true;
6974
} else {
7075
/* Zone revalidation during __bind() */
71-
map = md->zone_revalidate_map;
76+
map = zone_revalidate_map;
7277
}
7378

74-
ret = dm_blk_do_report_zones(md, map, sector, nr_zones, cb, data);
79+
if (map)
80+
ret = dm_blk_do_report_zones(md, map, sector, nr_zones, cb,
81+
data);
7582

76-
if (!md->zone_revalidate_map)
83+
if (put_table)
7784
dm_put_live_table(md, srcu_idx);
7885

7986
return ret;
@@ -175,7 +182,9 @@ int dm_revalidate_zones(struct dm_table *t, struct request_queue *q)
175182
* our table for dm_blk_report_zones() to use directly.
176183
*/
177184
md->zone_revalidate_map = t;
185+
md->revalidate_map_task = current;
178186
ret = blk_revalidate_disk_zones(disk);
187+
md->revalidate_map_task = NULL;
179188
md->zone_revalidate_map = NULL;
180189

181190
if (ret) {

0 commit comments

Comments
 (0)