Skip to content

Commit e39bfb5

Browse files
committed
Merge tag 'for-6.6/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm
Pull device mapper fixes from Mike Snitzer: - Fix DM core retrieve_deps() UAF race due to missing locking of a DM table's list of devices that is managed using dm_{get,put}_device. - Revert DM core's half-baked RCU optimization if IO submitter has set REQ_NOWAIT. Can be revisited, and properly justified, after comprehensively auditing all of DM to also pass GFP_NOWAIT for any allocations if REQ_NOWAIT used. * tag 'for-6.6/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: dm: don't attempt to queue IO under RCU protection dm: fix a race condition in retrieve_deps
2 parents 5bc357b + a9ce385 commit e39bfb5

File tree

4 files changed

+33
-30
lines changed

4 files changed

+33
-30
lines changed

drivers/md/dm-core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ struct dm_table {
214214

215215
/* a list of devices used by this table */
216216
struct list_head devices;
217+
struct rw_semaphore devices_lock;
217218

218219
/* events get handed up using this callback */
219220
void (*event_fn)(void *data);

drivers/md/dm-ioctl.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,8 @@ static void retrieve_deps(struct dm_table *table,
16301630
struct dm_dev_internal *dd;
16311631
struct dm_target_deps *deps;
16321632

1633+
down_read(&table->devices_lock);
1634+
16331635
deps = get_result_buffer(param, param_size, &len);
16341636

16351637
/*
@@ -1644,7 +1646,7 @@ static void retrieve_deps(struct dm_table *table,
16441646
needed = struct_size(deps, dev, count);
16451647
if (len < needed) {
16461648
param->flags |= DM_BUFFER_FULL_FLAG;
1647-
return;
1649+
goto out;
16481650
}
16491651

16501652
/*
@@ -1656,6 +1658,9 @@ static void retrieve_deps(struct dm_table *table,
16561658
deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev);
16571659

16581660
param->data_size = param->data_start + needed;
1661+
1662+
out:
1663+
up_read(&table->devices_lock);
16591664
}
16601665

16611666
static int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size)

drivers/md/dm-table.c

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ int dm_table_create(struct dm_table **result, blk_mode_t mode,
135135
return -ENOMEM;
136136

137137
INIT_LIST_HEAD(&t->devices);
138+
init_rwsem(&t->devices_lock);
138139

139140
if (!num_targets)
140141
num_targets = KEYS_PER_NODE;
@@ -359,16 +360,20 @@ int __ref dm_get_device(struct dm_target *ti, const char *path, blk_mode_t mode,
359360
if (dev == disk_devt(t->md->disk))
360361
return -EINVAL;
361362

363+
down_write(&t->devices_lock);
364+
362365
dd = find_device(&t->devices, dev);
363366
if (!dd) {
364367
dd = kmalloc(sizeof(*dd), GFP_KERNEL);
365-
if (!dd)
366-
return -ENOMEM;
368+
if (!dd) {
369+
r = -ENOMEM;
370+
goto unlock_ret_r;
371+
}
367372

368373
r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev);
369374
if (r) {
370375
kfree(dd);
371-
return r;
376+
goto unlock_ret_r;
372377
}
373378

374379
refcount_set(&dd->count, 1);
@@ -378,12 +383,17 @@ int __ref dm_get_device(struct dm_target *ti, const char *path, blk_mode_t mode,
378383
} else if (dd->dm_dev->mode != (mode | dd->dm_dev->mode)) {
379384
r = upgrade_mode(dd, mode, t->md);
380385
if (r)
381-
return r;
386+
goto unlock_ret_r;
382387
}
383388
refcount_inc(&dd->count);
384389
out:
390+
up_write(&t->devices_lock);
385391
*result = dd->dm_dev;
386392
return 0;
393+
394+
unlock_ret_r:
395+
up_write(&t->devices_lock);
396+
return r;
387397
}
388398
EXPORT_SYMBOL(dm_get_device);
389399

@@ -419,9 +429,12 @@ static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
419429
void dm_put_device(struct dm_target *ti, struct dm_dev *d)
420430
{
421431
int found = 0;
422-
struct list_head *devices = &ti->table->devices;
432+
struct dm_table *t = ti->table;
433+
struct list_head *devices = &t->devices;
423434
struct dm_dev_internal *dd;
424435

436+
down_write(&t->devices_lock);
437+
425438
list_for_each_entry(dd, devices, list) {
426439
if (dd->dm_dev == d) {
427440
found = 1;
@@ -430,14 +443,17 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d)
430443
}
431444
if (!found) {
432445
DMERR("%s: device %s not in table devices list",
433-
dm_device_name(ti->table->md), d->name);
434-
return;
446+
dm_device_name(t->md), d->name);
447+
goto unlock_ret;
435448
}
436449
if (refcount_dec_and_test(&dd->count)) {
437-
dm_put_table_device(ti->table->md, d);
450+
dm_put_table_device(t->md, d);
438451
list_del(&dd->list);
439452
kfree(dd);
440453
}
454+
455+
unlock_ret:
456+
up_write(&t->devices_lock);
441457
}
442458
EXPORT_SYMBOL(dm_put_device);
443459

drivers/md/dm.c

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -715,24 +715,6 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU)
715715
rcu_read_unlock();
716716
}
717717

718-
static inline struct dm_table *dm_get_live_table_bio(struct mapped_device *md,
719-
int *srcu_idx, blk_opf_t bio_opf)
720-
{
721-
if (bio_opf & REQ_NOWAIT)
722-
return dm_get_live_table_fast(md);
723-
else
724-
return dm_get_live_table(md, srcu_idx);
725-
}
726-
727-
static inline void dm_put_live_table_bio(struct mapped_device *md, int srcu_idx,
728-
blk_opf_t bio_opf)
729-
{
730-
if (bio_opf & REQ_NOWAIT)
731-
dm_put_live_table_fast(md);
732-
else
733-
dm_put_live_table(md, srcu_idx);
734-
}
735-
736718
static char *_dm_claim_ptr = "I belong to device-mapper";
737719

738720
/*
@@ -1833,9 +1815,8 @@ static void dm_submit_bio(struct bio *bio)
18331815
struct mapped_device *md = bio->bi_bdev->bd_disk->private_data;
18341816
int srcu_idx;
18351817
struct dm_table *map;
1836-
blk_opf_t bio_opf = bio->bi_opf;
18371818

1838-
map = dm_get_live_table_bio(md, &srcu_idx, bio_opf);
1819+
map = dm_get_live_table(md, &srcu_idx);
18391820

18401821
/* If suspended, or map not yet available, queue this IO for later */
18411822
if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) ||
@@ -1851,7 +1832,7 @@ static void dm_submit_bio(struct bio *bio)
18511832

18521833
dm_split_and_process_bio(md, map, bio);
18531834
out:
1854-
dm_put_live_table_bio(md, srcu_idx, bio_opf);
1835+
dm_put_live_table(md, srcu_idx);
18551836
}
18561837

18571838
static bool dm_poll_dm_io(struct dm_io *io, struct io_comp_batch *iob,

0 commit comments

Comments
 (0)