From: angel <[email protected]> The thin-pool target rejects identical argument strings, but two names for the same block device bypass the check. In read-only metadata mode, opening metadata first and data with write mode invokes dm_get_device() upgrade_mode(). That swaps and frees the old dm_dev while pool_ctr retains metadata_dev, causing a slab-use-after-free when __pool_find() reads metadata_dev->bdev.
Open the data device before metadata so an alias cannot trigger a mode upgrade between the two references. Compare the opened block devices and reject aliases before using either device. Adjust the error paths to release the devices in the matching order. Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=83827f9a59abe148bff4 Fixes: 70de2cbda8a5 ("dm thin: add sanity checks to thin-pool and external snapshot creation") Signed-off-by: Eduard Zateev <[email protected]> --- drivers/md/dm-thin.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 358ed77ff..0899cbaf9 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -3326,17 +3326,24 @@ static int pool_ctr(struct dm_target *ti, unsigned int argc, char **argv) metadata_mode = BLK_OPEN_READ | ((pf.mode == PM_READ_ONLY) ? 0 : BLK_OPEN_WRITE); + r = dm_get_device(ti, argv[1], BLK_OPEN_READ | BLK_OPEN_WRITE, &data_dev); + if (r) { + ti->error = "Error getting data device"; + goto out_unlock; + } + r = dm_get_device(ti, argv[0], metadata_mode, &metadata_dev); if (r) { ti->error = "Error opening metadata block device"; - goto out_unlock; + goto out_data; } warn_if_metadata_device_too_big(metadata_dev->bdev); - r = dm_get_device(ti, argv[1], BLK_OPEN_READ | BLK_OPEN_WRITE, &data_dev); - if (r) { - ti->error = "Error getting data device"; - goto out_metadata; + /* make sure metadata and data are different devices */ + if (metadata_dev->bdev == data_dev->bdev) { + ti->error = "Error setting metadata or data device"; + r = -EINVAL; + goto out; } if (kstrtoul(argv[2], 10, &block_size) || !block_size || @@ -3426,9 +3433,9 @@ static int pool_ctr(struct dm_target *ti, unsigned int argc, char **argv) out_free_pt: kfree(pt); out: - dm_put_device(ti, data_dev); -out_metadata: dm_put_device(ti, metadata_dev); +out_data: + dm_put_device(ti, data_dev); out_unlock: mutex_unlock(&dm_thin_pool_table.mutex); base-commit: 21a7f44fd0765fcc5194130b28767786eba1d1e7 -- 2.56.0
