| From b9a41d21dceadf8104812626ef85dc56ee8a60ed Mon Sep 17 00:00:00 2001 |
| From: Hou Tao <houtao1@huawei.com> |
| Date: Wed, 1 Nov 2017 15:42:36 +0800 |
| Subject: dm: fix race between dm_get_from_kobject() and __dm_destroy() |
| |
| From: Hou Tao <houtao1@huawei.com> |
| |
| commit b9a41d21dceadf8104812626ef85dc56ee8a60ed upstream. |
| |
| The following BUG_ON was hit when testing repeat 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 an atomic way, so _minor_lock is used. |
| |
| The other callers of dm_get() have also been checked to be 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> |
| Signed-off-by: Mike Snitzer <snitzer@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/md/dm.c | 12 ++++++++---- |
| 1 file changed, 8 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/md/dm.c |
| +++ b/drivers/md/dm.c |
| @@ -3033,11 +3033,15 @@ struct mapped_device *dm_get_from_kobjec |
| |
| md = container_of(kobj, struct mapped_device, kobj_holder.kobj); |
| |
| - if (test_bit(DMF_FREEING, &md->flags) || |
| - dm_deleting_md(md)) |
| - return NULL; |
| - |
| + spin_lock(&_minor_lock); |
| + if (test_bit(DMF_FREEING, &md->flags) || dm_deleting_md(md)) { |
| + md = NULL; |
| + goto out; |
| + } |
| dm_get(md); |
| +out: |
| + spin_unlock(&_minor_lock); |
| + |
| return md; |
| } |
| |