aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiangliang Lu <luliang@codeaurora.org>2017-05-05 08:50:32 +0800
committerdoc HD <doc.divxm@gmail.com>2017-11-20 09:27:08 +0300
commit32535f7b4fc37ab9b6f122639f014ea8b0e292a7 (patch)
tree3c9fa49878dc166e1031b9c1a59d9c8d6a2a35ab
parent69a1f06fe3abe25e81935f8bb9be4c7c26825dab (diff)
net: usb: rmnet_usb_ctrl:Make sure list_head operate atomicallyn7.1
Get and delete operation on variables "list_elem" are not atomic. Multiple threads may get the same "list_elem", may lead to race conditions. Add mutex in rmnet_ctl_open to resolve current potential race condition between test_bit and set_bit. Bug: 64441352 Change-Id: I00c4e2fd4854ee17a13a0757da98c46a78eee4cb Signed-off-by: Liangliang Lu <luliang@codeaurora.org>
-rw-r--r--drivers/net/usb/rmnet_usb_ctrl.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index f44d4dcb907..826b508d079 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -514,8 +514,13 @@ static int rmnet_ctl_open(struct inode *inode, struct file *file)
if (!dev)
return -ENODEV;
- if (test_bit(RMNET_CTRL_DEV_OPEN, &dev->status))
+ mutex_lock(&dev->dev_lock);
+ if (test_bit(RMNET_CTRL_DEV_OPEN, &dev->status)) {
+ mutex_unlock(&dev->dev_lock);
goto already_opened;
+ }
+ set_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
+ mutex_unlock(&dev->dev_lock);
if (dev->mdm_wait_timeout &&
!test_bit(RMNET_CTRL_DEV_READY, &dev->cudev->status)) {
@@ -527,10 +532,15 @@ static int rmnet_ctl_open(struct inode *inode, struct file *file)
if (retval == 0) {
dev_err(dev->devicep, "%s: Timeout opening %s\n",
__func__, dev->name);
- return -ETIMEDOUT;
- } else if (retval < 0) {
+ retval = -ETIMEDOUT;
+ } else if (retval < 0)
dev_err(dev->devicep, "%s: Error waiting for %s\n",
__func__, dev->name);
+
+ if (retval < 0) {
+ mutex_lock(&dev->dev_lock);
+ clear_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
+ mutex_unlock(&dev->dev_lock);
return retval;
}
}
@@ -538,14 +548,15 @@ static int rmnet_ctl_open(struct inode *inode, struct file *file)
if (!test_bit(RMNET_CTRL_DEV_READY, &dev->cudev->status)) {
dev_dbg(dev->devicep, "%s: Connection timedout opening %s\n",
__func__, dev->name);
+ mutex_lock(&dev->dev_lock);
+ clear_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
+ mutex_unlock(&dev->dev_lock);
return -ETIMEDOUT;
}
/* clear stale data if device close called but channel was ready */
rmnet_usb_ctrl_free_rx_list(dev);
- set_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
-
file->private_data = dev;
already_opened:
@@ -564,7 +575,9 @@ static int rmnet_ctl_release(struct inode *inode, struct file *file)
DBG("%s Called on %s device\n", __func__, dev->name);
+ mutex_lock(&dev->dev_lock);
clear_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
+ mutex_unlock(&dev->dev_lock);
file->private_data = NULL;
@@ -638,6 +651,7 @@ ctrl_read:
list_elem = list_first_entry(&dev->rx_list,
struct ctrl_pkt_list_elem, list);
+ list_del(&list_elem->list);
bytes_to_read = (uint32_t)(list_elem->cpkt.data_size);
if (bytes_to_read > count) {
spin_unlock_irqrestore(&dev->rx_lock, flags);
@@ -654,11 +668,11 @@ ctrl_read:
dev_err(dev->devicep,
"%s: copy_to_user failed for %s\n",
__func__, dev->name);
+ spin_lock_irqsave(&dev->rx_lock, flags);
+ list_add(&list_elem->list, &dev->rx_list);
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
return -EFAULT;
}
- spin_lock_irqsave(&dev->rx_lock, flags);
- list_del(&list_elem->list);
- spin_unlock_irqrestore(&dev->rx_lock, flags);
kfree(list_elem->cpkt.data);
kfree(list_elem);