[PATCH v4 3/3] uio: fix crash after the device is unregistered

2018-07-06 Thread xiubli
From: Xiubo Li 

For the target_core_user use case, after the device is unregistered
it maybe still opened in user space, then the kernel will crash, like:

[  251.163692] BUG: unable to handle kernel NULL pointer dereference at 
0008
[  251.163820] IP: [] show_name+0x23/0x40 [uio]
[  251.163965] PGD 800062694067 PUD 62696067 PMD 0
[  251.164097] Oops:  [#1] SMP
...
[  251.165605]  e1000 mptscsih mptbase drm_panel_orientation_quirks dm_mirror 
dm_region_hash dm_log dm_mod
[  251.166014] CPU: 0 PID: 13380 Comm: tcmu-runner Kdump: loaded Not tainted 
3.10.0-916.el7.test.x86_64 #1
[  251.166381] Hardware name: VMware, Inc. VMware Virtual Platform/440BX 
Desktop Reference Platform, BIOS 6.00 05/19/2017
[  251.166747] task: 971eb91db0c0 ti: 971e9e384000 task.ti: 
971e9e384000
[  251.167137] RIP: 0010:[]  [] 
show_name+0x23/0x40 [uio]
[  251.167563] RSP: 0018:971e9e387dc8  EFLAGS: 00010282
[  251.167978] RAX:  RBX: 971e9e3f8000 RCX: 971eb8368d98
[  251.168408] RDX: 971e9e3f8000 RSI: c0738084 RDI: 971e9e3f8000
[  251.168856] RBP: 971e9e387dd0 R08: 971eb8bc0018 R09: 
[  251.169296] R10: 1000 R11: a09d444d R12: a1076e80
[  251.169750] R13: 971e9e387f18 R14: 0001 R15: 971e9cfb1c80
[  251.170213] FS:  7ff37d175880() GS:971ebb60() 
knlGS:
[  251.170693] CS:  0010 DS:  ES:  CR0: 80050033
[  251.171248] CR2: 0008 CR3: 001f6000 CR4: 003607f0
[  251.172071] DR0:  DR1:  DR2: 
[  251.172640] DR3:  DR6: fffe0ff0 DR7: 0400
[  251.173236] Call Trace:
[  251.173789]  [] dev_attr_show+0x23/0x60
[  251.174356]  [] ? mutex_lock+0x12/0x2f
[  251.174892]  [] sysfs_kf_seq_show+0xcf/0x1f0
[  251.175433]  [] kernfs_seq_show+0x26/0x30
[  251.175981]  [] seq_read+0x110/0x3f0
[  251.176609]  [] kernfs_fop_read+0xf5/0x160
[  251.177158]  [] vfs_read+0x9f/0x170
[  251.177707]  [] SyS_read+0x7f/0xf0
[  251.178268]  [] system_call_fastpath+0x1c/0x21
[  251.178823] Code: 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 55 48 89 e5 53 48 
89 d3 e8 7e 96 56 e0 48 8b 80 d8 02 00 00 48 89 df 48 c7 c6 84 80 73 c0 <48> 8b 
50 08 31 c0 e8 e2 67 44 e0 5b 48 98 5d c3 0f 1f 00 66 2e
[  251.180115] RIP  [] show_name+0x23/0x40 [uio]
[  251.180820]  RSP 
[  251.181473] CR2: 0008

CC: Hamish Martin 
CC: Mike Christie 
Reviewed-by: Hamish Martin 
Signed-off-by: Xiubo Li 
---
 drivers/uio/uio.c | 104 +-
 1 file changed, 88 insertions(+), 16 deletions(-)

diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 655ade4..5d421d7 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -215,7 +215,20 @@ static ssize_t name_show(struct device *dev,
 struct device_attribute *attr, char *buf)
 {
struct uio_device *idev = dev_get_drvdata(dev);
-   return sprintf(buf, "%s\n", idev->info->name);
+   int ret;
+
+   mutex_lock(>info_lock);
+   if (!idev->info) {
+   ret = -EINVAL;
+   dev_err(dev, "the device has been unregistered\n");
+   goto out;
+   }
+
+   ret = sprintf(buf, "%s\n", idev->info->name);
+
+out:
+   mutex_unlock(>info_lock);
+   return ret;
 }
 static DEVICE_ATTR_RO(name);
 
@@ -223,7 +236,20 @@ static ssize_t version_show(struct device *dev,
struct device_attribute *attr, char *buf)
 {
struct uio_device *idev = dev_get_drvdata(dev);
-   return sprintf(buf, "%s\n", idev->info->version);
+   int ret;
+
+   mutex_lock(>info_lock);
+   if (!idev->info) {
+   ret = -EINVAL;
+   dev_err(dev, "the device has been unregistered\n");
+   goto out;
+   }
+
+   ret = sprintf(buf, "%s\n", idev->info->version);
+
+out:
+   mutex_unlock(>info_lock);
+   return ret;
 }
 static DEVICE_ATTR_RO(version);
 
@@ -415,11 +441,15 @@ void uio_event_notify(struct uio_info *info)
 static irqreturn_t uio_interrupt(int irq, void *dev_id)
 {
struct uio_device *idev = (struct uio_device *)dev_id;
-   irqreturn_t ret = idev->info->handler(irq, idev->info);
+   irqreturn_t ret;
+
+   mutex_lock(>info_lock);
 
+   ret = idev->info->handler(irq, idev->info);
if (ret == IRQ_HANDLED)
uio_event_notify(idev->info);
 
+   mutex_unlock(>info_lock);
return ret;
 }
 
@@ -460,6 +490,12 @@ static int uio_open(struct inode *inode, struct file 
*filep)
filep->private_data = listener;
 
mutex_lock(>info_lock);
+   if (!idev->info) {
+   mutex_unlock(>info_lock);
+   ret = -EINVAL;
+   goto err_alloc_listener;
+   }
+
if (idev->info && idev->info->open)
ret = idev->info->open(idev->info, inode);
 

[PATCH v4 3/3] uio: fix crash after the device is unregistered

2018-07-06 Thread xiubli
From: Xiubo Li 

For the target_core_user use case, after the device is unregistered
it maybe still opened in user space, then the kernel will crash, like:

[  251.163692] BUG: unable to handle kernel NULL pointer dereference at 
0008
[  251.163820] IP: [] show_name+0x23/0x40 [uio]
[  251.163965] PGD 800062694067 PUD 62696067 PMD 0
[  251.164097] Oops:  [#1] SMP
...
[  251.165605]  e1000 mptscsih mptbase drm_panel_orientation_quirks dm_mirror 
dm_region_hash dm_log dm_mod
[  251.166014] CPU: 0 PID: 13380 Comm: tcmu-runner Kdump: loaded Not tainted 
3.10.0-916.el7.test.x86_64 #1
[  251.166381] Hardware name: VMware, Inc. VMware Virtual Platform/440BX 
Desktop Reference Platform, BIOS 6.00 05/19/2017
[  251.166747] task: 971eb91db0c0 ti: 971e9e384000 task.ti: 
971e9e384000
[  251.167137] RIP: 0010:[]  [] 
show_name+0x23/0x40 [uio]
[  251.167563] RSP: 0018:971e9e387dc8  EFLAGS: 00010282
[  251.167978] RAX:  RBX: 971e9e3f8000 RCX: 971eb8368d98
[  251.168408] RDX: 971e9e3f8000 RSI: c0738084 RDI: 971e9e3f8000
[  251.168856] RBP: 971e9e387dd0 R08: 971eb8bc0018 R09: 
[  251.169296] R10: 1000 R11: a09d444d R12: a1076e80
[  251.169750] R13: 971e9e387f18 R14: 0001 R15: 971e9cfb1c80
[  251.170213] FS:  7ff37d175880() GS:971ebb60() 
knlGS:
[  251.170693] CS:  0010 DS:  ES:  CR0: 80050033
[  251.171248] CR2: 0008 CR3: 001f6000 CR4: 003607f0
[  251.172071] DR0:  DR1:  DR2: 
[  251.172640] DR3:  DR6: fffe0ff0 DR7: 0400
[  251.173236] Call Trace:
[  251.173789]  [] dev_attr_show+0x23/0x60
[  251.174356]  [] ? mutex_lock+0x12/0x2f
[  251.174892]  [] sysfs_kf_seq_show+0xcf/0x1f0
[  251.175433]  [] kernfs_seq_show+0x26/0x30
[  251.175981]  [] seq_read+0x110/0x3f0
[  251.176609]  [] kernfs_fop_read+0xf5/0x160
[  251.177158]  [] vfs_read+0x9f/0x170
[  251.177707]  [] SyS_read+0x7f/0xf0
[  251.178268]  [] system_call_fastpath+0x1c/0x21
[  251.178823] Code: 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 55 48 89 e5 53 48 
89 d3 e8 7e 96 56 e0 48 8b 80 d8 02 00 00 48 89 df 48 c7 c6 84 80 73 c0 <48> 8b 
50 08 31 c0 e8 e2 67 44 e0 5b 48 98 5d c3 0f 1f 00 66 2e
[  251.180115] RIP  [] show_name+0x23/0x40 [uio]
[  251.180820]  RSP 
[  251.181473] CR2: 0008

CC: Hamish Martin 
CC: Mike Christie 
Reviewed-by: Hamish Martin 
Signed-off-by: Xiubo Li 
---
 drivers/uio/uio.c | 104 +-
 1 file changed, 88 insertions(+), 16 deletions(-)

diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 655ade4..5d421d7 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -215,7 +215,20 @@ static ssize_t name_show(struct device *dev,
 struct device_attribute *attr, char *buf)
 {
struct uio_device *idev = dev_get_drvdata(dev);
-   return sprintf(buf, "%s\n", idev->info->name);
+   int ret;
+
+   mutex_lock(>info_lock);
+   if (!idev->info) {
+   ret = -EINVAL;
+   dev_err(dev, "the device has been unregistered\n");
+   goto out;
+   }
+
+   ret = sprintf(buf, "%s\n", idev->info->name);
+
+out:
+   mutex_unlock(>info_lock);
+   return ret;
 }
 static DEVICE_ATTR_RO(name);
 
@@ -223,7 +236,20 @@ static ssize_t version_show(struct device *dev,
struct device_attribute *attr, char *buf)
 {
struct uio_device *idev = dev_get_drvdata(dev);
-   return sprintf(buf, "%s\n", idev->info->version);
+   int ret;
+
+   mutex_lock(>info_lock);
+   if (!idev->info) {
+   ret = -EINVAL;
+   dev_err(dev, "the device has been unregistered\n");
+   goto out;
+   }
+
+   ret = sprintf(buf, "%s\n", idev->info->version);
+
+out:
+   mutex_unlock(>info_lock);
+   return ret;
 }
 static DEVICE_ATTR_RO(version);
 
@@ -415,11 +441,15 @@ void uio_event_notify(struct uio_info *info)
 static irqreturn_t uio_interrupt(int irq, void *dev_id)
 {
struct uio_device *idev = (struct uio_device *)dev_id;
-   irqreturn_t ret = idev->info->handler(irq, idev->info);
+   irqreturn_t ret;
+
+   mutex_lock(>info_lock);
 
+   ret = idev->info->handler(irq, idev->info);
if (ret == IRQ_HANDLED)
uio_event_notify(idev->info);
 
+   mutex_unlock(>info_lock);
return ret;
 }
 
@@ -460,6 +490,12 @@ static int uio_open(struct inode *inode, struct file 
*filep)
filep->private_data = listener;
 
mutex_lock(>info_lock);
+   if (!idev->info) {
+   mutex_unlock(>info_lock);
+   ret = -EINVAL;
+   goto err_alloc_listener;
+   }
+
if (idev->info && idev->info->open)
ret = idev->info->open(idev->info, inode);