Commit f51576b6 authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Luis Henriques

dm: fix a race condition in dm_get_md

commit 2bec1f4a upstream.

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: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 06246b44
...@@ -2341,7 +2341,7 @@ int dm_setup_md_queue(struct mapped_device *md) ...@@ -2341,7 +2341,7 @@ int dm_setup_md_queue(struct mapped_device *md)
return 0; return 0;
} }
static struct mapped_device *dm_find_md(dev_t dev) struct mapped_device *dm_get_md(dev_t dev)
{ {
struct mapped_device *md; struct mapped_device *md;
unsigned minor = MINOR(dev); unsigned minor = MINOR(dev);
...@@ -2352,12 +2352,15 @@ static struct mapped_device *dm_find_md(dev_t dev) ...@@ -2352,12 +2352,15 @@ static struct mapped_device *dm_find_md(dev_t dev)
spin_lock(&_minor_lock); spin_lock(&_minor_lock);
md = idr_find(&_minor_idr, minor); md = idr_find(&_minor_idr, minor);
if (md && (md == MINOR_ALLOCED || if (md) {
(MINOR(disk_devt(dm_disk(md))) != minor) || if ((md == MINOR_ALLOCED ||
dm_deleting_md(md) || (MINOR(disk_devt(dm_disk(md))) != minor) ||
test_bit(DMF_FREEING, &md->flags))) { dm_deleting_md(md) ||
md = NULL; test_bit(DMF_FREEING, &md->flags))) {
goto out; md = NULL;
goto out;
}
dm_get(md);
} }
out: out:
...@@ -2365,16 +2368,6 @@ static struct mapped_device *dm_find_md(dev_t dev) ...@@ -2365,16 +2368,6 @@ static struct mapped_device *dm_find_md(dev_t dev)
return md; return md;
} }
struct mapped_device *dm_get_md(dev_t dev)
{
struct mapped_device *md = dm_find_md(dev);
if (md)
dm_get(md);
return md;
}
EXPORT_SYMBOL_GPL(dm_get_md); EXPORT_SYMBOL_GPL(dm_get_md);
void *dm_get_mdptr(struct mapped_device *md) void *dm_get_mdptr(struct mapped_device *md)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment