Skip to content

Commit 8715c6d

Browse files
committed
Merge tag 'for-6.2/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm
Pull device mapper updates from Mike Snitzer: - Fix use-after-free races due to missing resource cleanup during DM target destruction in DM targets: thin-pool, cache, integrity and clone. - Fix ABBA deadlocks in DM thin-pool and cache targets due to their use of a bufio client (that has a shrinker whose locking can cause the incorrect locking order). - Fix DM cache target to set its needs_check flag after first aborting the metadata (whereby using reset persistent-data objects to update the superblock with, otherwise the superblock update could be dropped due to aborting metadata). This was found with code-inspection when comparing with the equivalent in DM thinp code. - Fix DM thin-pool's presume to continue resuming the device even if the pool in is fail mode -- otherwise bios may never be failed up the IO stack (which will prevent resetting the thin-pool target via table reload) - Fix DM thin-pool's metadata to use proper btree root (from previous transaction) if metadata commit failed. - Add 'waitfor' module param to DM module (dm_mod) to allow dm-init to wait for the specified device before continuing with its DM target initialization. * tag 'for-6.2/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: dm thin: Use last transaction's pmd->root when commit failed dm init: add dm-mod.waitfor to wait for asynchronously probed block devices dm ioctl: fix a couple ioctl codes dm ioctl: a small code cleanup in list_version_get_info dm thin: resume even if in FAIL mode dm cache: set needs_check flag after aborting metadata dm cache: Fix ABBA deadlock between shrink_slab and dm_cache_metadata_abort dm thin: Fix ABBA deadlock between shrink_slab and dm_pool_abort_metadata dm integrity: Fix UAF in dm_integrity_dtr() dm cache: Fix UAF in destroy() dm clone: Fix UAF in clone_dtr() dm thin: Fix UAF in run_timer_softirq()
2 parents 8ecd28b + 7991dbf commit 8715c6d

File tree

9 files changed

+154
-28
lines changed

9 files changed

+154
-28
lines changed

Documentation/admin-guide/device-mapper/dm-init.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,11 @@ Other examples (per target):
123123
0 1638400 verity 1 8:1 8:2 4096 4096 204800 1 sha256
124124
fb1a5a0f00deb908d8b53cb270858975e76cf64105d412ce764225d53b8f3cfd
125125
51934789604d1b92399c52e7cb149d1b3a1b74bbbcb103b2a0aaacbed5c08584
126+
127+
For setups using device-mapper on top of asynchronously probed block
128+
devices (MMC, USB, ..), it may be necessary to tell dm-init to
129+
explicitly wait for them to become available before setting up the
130+
device-mapper tables. This can be done with the "dm-mod.waitfor="
131+
module parameter, which takes a list of devices to wait for::
132+
133+
dm-mod.waitfor=<device1>[,..,<deviceN>]

drivers/md/dm-cache-metadata.c

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -551,11 +551,13 @@ static int __create_persistent_data_objects(struct dm_cache_metadata *cmd,
551551
return r;
552552
}
553553

554-
static void __destroy_persistent_data_objects(struct dm_cache_metadata *cmd)
554+
static void __destroy_persistent_data_objects(struct dm_cache_metadata *cmd,
555+
bool destroy_bm)
555556
{
556557
dm_sm_destroy(cmd->metadata_sm);
557558
dm_tm_destroy(cmd->tm);
558-
dm_block_manager_destroy(cmd->bm);
559+
if (destroy_bm)
560+
dm_block_manager_destroy(cmd->bm);
559561
}
560562

561563
typedef unsigned long (*flags_mutator)(unsigned long);
@@ -826,7 +828,7 @@ static struct dm_cache_metadata *lookup_or_open(struct block_device *bdev,
826828
cmd2 = lookup(bdev);
827829
if (cmd2) {
828830
mutex_unlock(&table_lock);
829-
__destroy_persistent_data_objects(cmd);
831+
__destroy_persistent_data_objects(cmd, true);
830832
kfree(cmd);
831833
return cmd2;
832834
}
@@ -874,7 +876,7 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd)
874876
mutex_unlock(&table_lock);
875877

876878
if (!cmd->fail_io)
877-
__destroy_persistent_data_objects(cmd);
879+
__destroy_persistent_data_objects(cmd, true);
878880
kfree(cmd);
879881
}
880882
}
@@ -1807,14 +1809,52 @@ int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result)
18071809

18081810
int dm_cache_metadata_abort(struct dm_cache_metadata *cmd)
18091811
{
1810-
int r;
1812+
int r = -EINVAL;
1813+
struct dm_block_manager *old_bm = NULL, *new_bm = NULL;
1814+
1815+
/* fail_io is double-checked with cmd->root_lock held below */
1816+
if (unlikely(cmd->fail_io))
1817+
return r;
1818+
1819+
/*
1820+
* Replacement block manager (new_bm) is created and old_bm destroyed outside of
1821+
* cmd root_lock to avoid ABBA deadlock that would result (due to life-cycle of
1822+
* shrinker associated with the block manager's bufio client vs cmd root_lock).
1823+
* - must take shrinker_rwsem without holding cmd->root_lock
1824+
*/
1825+
new_bm = dm_block_manager_create(cmd->bdev, DM_CACHE_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
1826+
CACHE_MAX_CONCURRENT_LOCKS);
18111827

18121828
WRITE_LOCK(cmd);
1813-
__destroy_persistent_data_objects(cmd);
1814-
r = __create_persistent_data_objects(cmd, false);
1829+
if (cmd->fail_io) {
1830+
WRITE_UNLOCK(cmd);
1831+
goto out;
1832+
}
1833+
1834+
__destroy_persistent_data_objects(cmd, false);
1835+
old_bm = cmd->bm;
1836+
if (IS_ERR(new_bm)) {
1837+
DMERR("could not create block manager during abort");
1838+
cmd->bm = NULL;
1839+
r = PTR_ERR(new_bm);
1840+
goto out_unlock;
1841+
}
1842+
1843+
cmd->bm = new_bm;
1844+
r = __open_or_format_metadata(cmd, false);
1845+
if (r) {
1846+
cmd->bm = NULL;
1847+
goto out_unlock;
1848+
}
1849+
new_bm = NULL;
1850+
out_unlock:
18151851
if (r)
18161852
cmd->fail_io = true;
18171853
WRITE_UNLOCK(cmd);
1854+
dm_block_manager_destroy(old_bm);
1855+
out:
1856+
if (new_bm && !IS_ERR(new_bm))
1857+
dm_block_manager_destroy(new_bm);
18181858

18191859
return r;
18201860
}

drivers/md/dm-cache-target.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -907,16 +907,16 @@ static void abort_transaction(struct cache *cache)
907907
if (get_cache_mode(cache) >= CM_READ_ONLY)
908908
return;
909909

910-
if (dm_cache_metadata_set_needs_check(cache->cmd)) {
911-
DMERR("%s: failed to set 'needs_check' flag in metadata", dev_name);
912-
set_cache_mode(cache, CM_FAIL);
913-
}
914-
915910
DMERR_LIMIT("%s: aborting current metadata transaction", dev_name);
916911
if (dm_cache_metadata_abort(cache->cmd)) {
917912
DMERR("%s: failed to abort metadata transaction", dev_name);
918913
set_cache_mode(cache, CM_FAIL);
919914
}
915+
916+
if (dm_cache_metadata_set_needs_check(cache->cmd)) {
917+
DMERR("%s: failed to set 'needs_check' flag in metadata", dev_name);
918+
set_cache_mode(cache, CM_FAIL);
919+
}
920920
}
921921

922922
static void metadata_operation_failed(struct cache *cache, const char *op, int r)
@@ -1887,6 +1887,7 @@ static void destroy(struct cache *cache)
18871887
if (cache->prison)
18881888
dm_bio_prison_destroy_v2(cache->prison);
18891889

1890+
cancel_delayed_work_sync(&cache->waker);
18901891
if (cache->wq)
18911892
destroy_workqueue(cache->wq);
18921893

drivers/md/dm-clone-target.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,7 @@ static void clone_dtr(struct dm_target *ti)
19581958

19591959
mempool_exit(&clone->hydration_pool);
19601960
dm_kcopyd_client_destroy(clone->kcopyd_client);
1961+
cancel_delayed_work_sync(&clone->waker);
19611962
destroy_workqueue(clone->wq);
19621963
hash_table_exit(clone);
19631964
dm_clone_metadata_close(clone->cmd);

drivers/md/dm-init.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include <linux/ctype.h>
11+
#include <linux/delay.h>
1112
#include <linux/device.h>
1213
#include <linux/device-mapper.h>
1314
#include <linux/init.h>
@@ -18,12 +19,17 @@
1819
#define DM_MAX_DEVICES 256
1920
#define DM_MAX_TARGETS 256
2021
#define DM_MAX_STR_SIZE 4096
22+
#define DM_MAX_WAITFOR 256
2123

2224
static char *create;
2325

26+
static char *waitfor[DM_MAX_WAITFOR];
27+
2428
/*
2529
* Format: dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
2630
* Table format: <start_sector> <num_sectors> <target_type> <target_args>
31+
* Block devices to wait for to become available before setting up tables:
32+
* dm-mod.waitfor=<device1>[,..,<deviceN>]
2733
*
2834
* See Documentation/admin-guide/device-mapper/dm-init.rst for dm-mod.create="..." format
2935
* details.
@@ -266,7 +272,7 @@ static int __init dm_init_init(void)
266272
struct dm_device *dev;
267273
LIST_HEAD(devices);
268274
char *str;
269-
int r;
275+
int i, r;
270276

271277
if (!create)
272278
return 0;
@@ -286,6 +292,17 @@ static int __init dm_init_init(void)
286292
DMINFO("waiting for all devices to be available before creating mapped devices");
287293
wait_for_device_probe();
288294

295+
for (i = 0; i < ARRAY_SIZE(waitfor); i++) {
296+
if (waitfor[i]) {
297+
DMINFO("waiting for device %s ...", waitfor[i]);
298+
while (!dm_get_dev_t(waitfor[i]))
299+
msleep(5);
300+
}
301+
}
302+
303+
if (waitfor[0])
304+
DMINFO("all devices available");
305+
289306
list_for_each_entry(dev, &devices, list) {
290307
if (dm_early_create(&dev->dmi, dev->table,
291308
dev->target_args_array))
@@ -301,3 +318,6 @@ late_initcall(dm_init_init);
301318

302319
module_param(create, charp, 0);
303320
MODULE_PARM_DESC(create, "Create a mapped device in early boot");
321+
322+
module_param_array(waitfor, charp, NULL, 0);
323+
MODULE_PARM_DESC(waitfor, "Devices to wait for before setting up tables");

drivers/md/dm-integrity.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4558,6 +4558,8 @@ static void dm_integrity_dtr(struct dm_target *ti)
45584558
BUG_ON(!RB_EMPTY_ROOT(&ic->in_progress));
45594559
BUG_ON(!list_empty(&ic->wait_list));
45604560

4561+
if (ic->mode == 'B')
4562+
cancel_delayed_work_sync(&ic->bitmap_flush_work);
45614563
if (ic->metadata_wq)
45624564
destroy_workqueue(ic->metadata_wq);
45634565
if (ic->wait_wq)

drivers/md/dm-ioctl.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ static void list_version_get_info(struct target_type *tt, void *param)
681681
strcpy(info->vers->name, tt->name);
682682

683683
info->old_vers = info->vers;
684-
info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1);
684+
info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1);
685685
}
686686

687687
static int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name)
@@ -1788,8 +1788,8 @@ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
17881788

17891789
{DM_TARGET_MSG_CMD, 0, target_message},
17901790
{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry},
1791-
{DM_DEV_ARM_POLL, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll},
1792-
{DM_GET_TARGET_VERSION, 0, get_target_version},
1791+
{DM_DEV_ARM_POLL_CMD, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll},
1792+
{DM_GET_TARGET_VERSION_CMD, 0, get_target_version},
17931793
};
17941794

17951795
if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))

drivers/md/dm-thin-metadata.c

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,15 @@ static int __open_metadata(struct dm_pool_metadata *pmd)
724724
goto bad_cleanup_data_sm;
725725
}
726726

727+
/*
728+
* For pool metadata opening process, root setting is redundant
729+
* because it will be set again in __begin_transaction(). But dm
730+
* pool aborting process really needs to get last transaction's
731+
* root to avoid accessing broken btree.
732+
*/
733+
pmd->root = le64_to_cpu(disk_super->data_mapping_root);
734+
pmd->details_root = le64_to_cpu(disk_super->device_details_root);
735+
727736
__setup_btree_details(pmd);
728737
dm_bm_unlock(sblock);
729738

@@ -776,13 +785,15 @@ static int __create_persistent_data_objects(struct dm_pool_metadata *pmd, bool f
776785
return r;
777786
}
778787

779-
static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd)
788+
static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd,
789+
bool destroy_bm)
780790
{
781791
dm_sm_destroy(pmd->data_sm);
782792
dm_sm_destroy(pmd->metadata_sm);
783793
dm_tm_destroy(pmd->nb_tm);
784794
dm_tm_destroy(pmd->tm);
785-
dm_block_manager_destroy(pmd->bm);
795+
if (destroy_bm)
796+
dm_block_manager_destroy(pmd->bm);
786797
}
787798

788799
static int __begin_transaction(struct dm_pool_metadata *pmd)
@@ -989,7 +1000,7 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
9891000
}
9901001
pmd_write_unlock(pmd);
9911002
if (!pmd->fail_io)
992-
__destroy_persistent_data_objects(pmd);
1003+
__destroy_persistent_data_objects(pmd, true);
9931004

9941005
kfree(pmd);
9951006
return 0;
@@ -1860,19 +1871,52 @@ static void __set_abort_with_changes_flags(struct dm_pool_metadata *pmd)
18601871
int dm_pool_abort_metadata(struct dm_pool_metadata *pmd)
18611872
{
18621873
int r = -EINVAL;
1874+
struct dm_block_manager *old_bm = NULL, *new_bm = NULL;
1875+
1876+
/* fail_io is double-checked with pmd->root_lock held below */
1877+
if (unlikely(pmd->fail_io))
1878+
return r;
1879+
1880+
/*
1881+
* Replacement block manager (new_bm) is created and old_bm destroyed outside of
1882+
* pmd root_lock to avoid ABBA deadlock that would result (due to life-cycle of
1883+
* shrinker associated with the block manager's bufio client vs pmd root_lock).
1884+
* - must take shrinker_rwsem without holding pmd->root_lock
1885+
*/
1886+
new_bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
1887+
THIN_MAX_CONCURRENT_LOCKS);
18631888

18641889
pmd_write_lock(pmd);
1865-
if (pmd->fail_io)
1890+
if (pmd->fail_io) {
1891+
pmd_write_unlock(pmd);
18661892
goto out;
1893+
}
18671894

18681895
__set_abort_with_changes_flags(pmd);
1869-
__destroy_persistent_data_objects(pmd);
1870-
r = __create_persistent_data_objects(pmd, false);
1896+
__destroy_persistent_data_objects(pmd, false);
1897+
old_bm = pmd->bm;
1898+
if (IS_ERR(new_bm)) {
1899+
DMERR("could not create block manager during abort");
1900+
pmd->bm = NULL;
1901+
r = PTR_ERR(new_bm);
1902+
goto out_unlock;
1903+
}
1904+
1905+
pmd->bm = new_bm;
1906+
r = __open_or_format_metadata(pmd, false);
1907+
if (r) {
1908+
pmd->bm = NULL;
1909+
goto out_unlock;
1910+
}
1911+
new_bm = NULL;
1912+
out_unlock:
18711913
if (r)
18721914
pmd->fail_io = true;
1873-
1874-
out:
18751915
pmd_write_unlock(pmd);
1916+
dm_block_manager_destroy(old_bm);
1917+
out:
1918+
if (new_bm && !IS_ERR(new_bm))
1919+
dm_block_manager_destroy(new_bm);
18761920

18771921
return r;
18781922
}

drivers/md/dm-thin.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2889,6 +2889,8 @@ static void __pool_destroy(struct pool *pool)
28892889
dm_bio_prison_destroy(pool->prison);
28902890
dm_kcopyd_client_destroy(pool->copier);
28912891

2892+
cancel_delayed_work_sync(&pool->waker);
2893+
cancel_delayed_work_sync(&pool->no_space_timeout);
28922894
if (pool->wq)
28932895
destroy_workqueue(pool->wq);
28942896

@@ -3540,20 +3542,28 @@ static int pool_preresume(struct dm_target *ti)
35403542
*/
35413543
r = bind_control_target(pool, ti);
35423544
if (r)
3543-
return r;
3545+
goto out;
35443546

35453547
r = maybe_resize_data_dev(ti, &need_commit1);
35463548
if (r)
3547-
return r;
3549+
goto out;
35483550

35493551
r = maybe_resize_metadata_dev(ti, &need_commit2);
35503552
if (r)
3551-
return r;
3553+
goto out;
35523554

35533555
if (need_commit1 || need_commit2)
35543556
(void) commit(pool);
3557+
out:
3558+
/*
3559+
* When a thin-pool is PM_FAIL, it cannot be rebuilt if
3560+
* bio is in deferred list. Therefore need to return 0
3561+
* to allow pool_resume() to flush IO.
3562+
*/
3563+
if (r && get_pool_mode(pool) == PM_FAIL)
3564+
r = 0;
35553565

3556-
return 0;
3566+
return r;
35573567
}
35583568

35593569
static void pool_suspend_active_thins(struct pool *pool)

0 commit comments

Comments
 (0)