diff mbox

dm: fix race between dm_get_from_kobject() and __dm_destroy()

Message ID 20171101074236.21901-1-houtao1@huawei.com (mailing list archive)
State Accepted, archived
Delegated to: Mike Snitzer
Headers show

Commit Message

Hou Tao Nov. 1, 2017, 7:42 a.m. UTC
We got a BUG_ON when testing the repeating creation and removal
of dm devices:

    kernel BUG at drivers/md/dm.c:2919!
    CPU: 7 PID: 750 Comm: systemd-udevd Not tainted 4.1.44
    Call Trace:
     [<ffffffff81649e8b>] dm_get_from_kobject+0x34/0x3a
     [<ffffffff81650ef1>] dm_attr_show+0x2b/0x5e
     [<ffffffff817b46d1>] ? mutex_lock+0x26/0x44
     [<ffffffff811df7f5>] sysfs_kf_seq_show+0x83/0xcf
     [<ffffffff811de257>] kernfs_seq_show+0x23/0x25
     [<ffffffff81199118>] seq_read+0x16f/0x325
     [<ffffffff811de994>] kernfs_fop_read+0x3a/0x13f
     [<ffffffff8117b625>] __vfs_read+0x26/0x9d
     [<ffffffff8130eb59>] ? security_file_permission+0x3c/0x44
     [<ffffffff8117bdb8>] ? rw_verify_area+0x83/0xd9
     [<ffffffff8117be9d>] vfs_read+0x8f/0xcf
     [<ffffffff81193e34>] ? __fdget_pos+0x12/0x41
     [<ffffffff8117c686>] SyS_read+0x4b/0x76
     [<ffffffff817b606e>] system_call_fastpath+0x12/0x71

The bug can be easily triggered, if an extra delay (e.g., 10ms) is added
between the test of DMF_FREEING & DMF_DELETING and dm_get() in
dm_get_from_kobject().

To fix it, we need to ensure the test of DMF_FREEING & DMF_DELETING and
dm_get() are done in a atomical way, so _minor_lock is used.

The other callers of dm_get() have also been checked. Their usages of
dm_get() are OK: some callers invoke dm_get() under _minor_lock,
some callers invoke it under _hash_lock, and dm_start_request() invoke
it after increasing md->open_count.

Signed-off-by: Hou Tao <houtao1@huawei.com>
---
 drivers/md/dm.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 4be8532..97d383b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2709,11 +2709,14 @@  struct mapped_device *dm_get_from_kobject(struct kobject *kobj)
 
 	md = container_of(kobj, struct mapped_device, kobj_holder.kobj);
 
+	spin_lock(&_minor_lock);
 	if (test_bit(DMF_FREEING, &md->flags) ||
 	    dm_deleting_md(md))
-		return NULL;
+		md = NULL;
+	else
+		dm_get(md);
+	spin_unlock(&_minor_lock);
 
-	dm_get(md);
 	return md;
 }