diff options
| author | Mikulas Patocka <mpatocka@redhat.com> | 2015-02-17 14:30:53 -0500 |
|---|---|---|
| committer | Mike Snitzer <snitzer@redhat.com> | 2015-02-18 09:41:19 -0500 |
| commit | 2bec1f4a8832e74ebbe859f176d8a9cb20dd97f4 (patch) | |
| tree | b9d1da979b02b7f026e4b1db91ccc6022096567b /drivers | |
| parent | b3c5fd3052492f1b8d060799d4f18be5a5438add (diff) | |
dm: fix a race condition in dm_get_md
The function dm_get_md finds a device mapper device with a given dev_t,
increases the reference count and returns the pointer.
dm_get_md calls dm_find_md, dm_find_md takes _minor_lock, finds the
device, tests that the device doesn't have DMF_DELETING or DMF_FREEING
flag, drops _minor_lock and returns pointer to the device. dm_get_md then
calls dm_get. dm_get calls BUG if the device has the DMF_FREEING flag,
otherwise it increments the reference count.
There is a possible race condition - after dm_find_md exits and before
dm_get is called, there are no locks held, so the device may disappear or
DMF_FREEING flag may be set, which results in BUG.
To fix this bug, we need to call dm_get while we hold _minor_lock. This
patch renames dm_find_md to dm_get_md and changes it so that it calls
dm_get while holding the lock.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/md/dm.c | 27 |
1 files changed, 10 insertions, 17 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index ec1444f49de1..73f28802dc7a 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
| @@ -2571,7 +2571,7 @@ int dm_setup_md_queue(struct mapped_device *md) | |||
| 2571 | return 0; | 2571 | return 0; |
| 2572 | } | 2572 | } |
| 2573 | 2573 | ||
| 2574 | static struct mapped_device *dm_find_md(dev_t dev) | 2574 | struct mapped_device *dm_get_md(dev_t dev) |
| 2575 | { | 2575 | { |
| 2576 | struct mapped_device *md; | 2576 | struct mapped_device *md; |
| 2577 | unsigned minor = MINOR(dev); | 2577 | unsigned minor = MINOR(dev); |
| @@ -2582,12 +2582,15 @@ static struct mapped_device *dm_find_md(dev_t dev) | |||
| 2582 | spin_lock(&_minor_lock); | 2582 | spin_lock(&_minor_lock); |
| 2583 | 2583 | ||
| 2584 | md = idr_find(&_minor_idr, minor); | 2584 | md = idr_find(&_minor_idr, minor); |
| 2585 | if (md && (md == MINOR_ALLOCED || | 2585 | if (md) { |
| 2586 | (MINOR(disk_devt(dm_disk(md))) != minor) || | 2586 | if ((md == MINOR_ALLOCED || |
| 2587 | dm_deleting_md(md) || | 2587 | (MINOR(disk_devt(dm_disk(md))) != minor) || |
| 2588 | test_bit(DMF_FREEING, &md->flags))) { | 2588 | dm_deleting_md(md) || |
| 2589 | md = NULL; | 2589 | test_bit(DMF_FREEING, &md->flags))) { |
| 2590 | goto out; | 2590 | md = NULL; |
| 2591 | goto out; | ||
| 2592 | } | ||
| 2593 | dm_get(md); | ||
| 2591 | } | 2594 | } |
| 2592 | 2595 | ||
| 2593 | out: | 2596 | out: |
| @@ -2595,16 +2598,6 @@ out: | |||
| 2595 | 2598 | ||
| 2596 | return md; | 2599 | return md; |
| 2597 | } | 2600 | } |
| 2598 | |||
| 2599 | struct mapped_device *dm_get_md(dev_t dev) | ||
| 2600 | { | ||
| 2601 | struct mapped_device *md = dm_find_md(dev); | ||
| 2602 | |||
| 2603 | if (md) | ||
| 2604 | dm_get(md); | ||
| 2605 | |||
| 2606 | return md; | ||
| 2607 | } | ||
| 2608 | EXPORT_SYMBOL_GPL(dm_get_md); | 2601 | EXPORT_SYMBOL_GPL(dm_get_md); |
| 2609 | 2602 | ||
| 2610 | void *dm_get_mdptr(struct mapped_device *md) | 2603 | void *dm_get_mdptr(struct mapped_device *md) |
