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


Reply via email to