aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@googlemail.com>2011-08-10 08:02:07 -0400
committerJiri Kosina <jkosina@suse.cz>2011-08-10 08:02:07 -0400
commit4ea5454203d991ec85264f64f89ca8855fce69b0 (patch)
treee0bd6d2f01894f09c7b3d72fd8852afbc8c66d73 /drivers/hid
parent00b15628b0aa73199f74bd6c988b8d2b55f51190 (diff)
HID: Fix race condition between driver core and ll-driver
HID low level drivers register new devices with the HID core which then adds the devices to the HID bus. The HID bus normally immediately probes an appropriate driver which then handles HID input for this device. The ll driver now uses the hid_input_report() function to report input events for a specific device. However, if the HID bus unloads the driver at the same time (for instance via a call to /sys/bus/hid/devices/<dev>/unbind) then the hdev->driver pointer may be used by hid_input_report() and hid_device_remove() at the same time which may cause hdev->driver to point to invalid memory. This fix adds a semaphore to every hid device which protects hdev->driver from asynchronous access. This semaphore is locked during driver *_probe and *_remove and also inside hid_input_report(). The *_probe and *_remove functions may sleep so the semaphore is good here, however, hid_input_report() is in atomic context and hence only uses down_trylock(). If it cannot acquire the lock it simply drops the input package. The low-level drivers report input events synchronously so hid_input_report() should never be entered twice at the same time on the same device. Hence, the lock should always be available. But if the driver is currently probed/removed then the lock is not available and dropping the package should be safe because this is what would have happened if the package arrived some milliseconds earlier/later. This also fixes another race condition while probing drivers: First the *_probe function of the driver is called and only if that succeeds, the related input device of hidinput is registered. If the low level driver reports input events after the *_probe function returned but before the input device is registered, then a NULL pointer dereference will occur. (Equivalently on driver remove function). This is not possible anymore, since the semaphore lock drops all incoming packages until the driver/device is fully initialized. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-core.c41
1 files changed, 34 insertions, 7 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 1a5cf0c9cfca..f9cff9335595 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -29,6 +29,7 @@
29#include <linux/wait.h> 29#include <linux/wait.h>
30#include <linux/vmalloc.h> 30#include <linux/vmalloc.h>
31#include <linux/sched.h> 31#include <linux/sched.h>
32#include <linux/semaphore.h>
32 33
33#include <linux/hid.h> 34#include <linux/hid.h>
34#include <linux/hiddev.h> 35#include <linux/hiddev.h>
@@ -1087,14 +1088,23 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
1087 unsigned int i; 1088 unsigned int i;
1088 int ret; 1089 int ret;
1089 1090
1090 if (!hid || !hid->driver) 1091 if (!hid)
1091 return -ENODEV; 1092 return -ENODEV;
1093
1094 if (down_trylock(&hid->driver_lock))
1095 return -EBUSY;
1096
1097 if (!hid->driver) {
1098 ret = -ENODEV;
1099 goto unlock;
1100 }
1092 report_enum = hid->report_enum + type; 1101 report_enum = hid->report_enum + type;
1093 hdrv = hid->driver; 1102 hdrv = hid->driver;
1094 1103
1095 if (!size) { 1104 if (!size) {
1096 dbg_hid("empty report\n"); 1105 dbg_hid("empty report\n");
1097 return -1; 1106 ret = -1;
1107 goto unlock;
1098 } 1108 }
1099 1109
1100 buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); 1110 buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
@@ -1118,17 +1128,23 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
1118nomem: 1128nomem:
1119 report = hid_get_report(report_enum, data); 1129 report = hid_get_report(report_enum, data);
1120 1130
1121 if (!report) 1131 if (!report) {
1122 return -1; 1132 ret = -1;
1133 goto unlock;
1134 }
1123 1135
1124 if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { 1136 if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
1125 ret = hdrv->raw_event(hid, report, data, size); 1137 ret = hdrv->raw_event(hid, report, data, size);
1126 if (ret != 0) 1138 if (ret != 0) {
1127 return ret < 0 ? ret : 0; 1139 ret = ret < 0 ? ret : 0;
1140 goto unlock;
1141 }
1128 } 1142 }
1129 1143
1130 hid_report_raw_event(hid, type, data, size, interrupt); 1144 hid_report_raw_event(hid, type, data, size, interrupt);
1131 1145
1146unlock:
1147 up(&hid->driver_lock);
1132 return 0; 1148 return 0;
1133} 1149}
1134EXPORT_SYMBOL_GPL(hid_input_report); 1150EXPORT_SYMBOL_GPL(hid_input_report);
@@ -1617,6 +1633,9 @@ static int hid_device_probe(struct device *dev)
1617 const struct hid_device_id *id; 1633 const struct hid_device_id *id;
1618 int ret = 0; 1634 int ret = 0;
1619 1635
1636 if (down_interruptible(&hdev->driver_lock))
1637 return -EINTR;
1638
1620 if (!hdev->driver) { 1639 if (!hdev->driver) {
1621 id = hid_match_device(hdev, hdrv); 1640 id = hid_match_device(hdev, hdrv);
1622 if (id == NULL) 1641 if (id == NULL)
@@ -1633,14 +1652,20 @@ static int hid_device_probe(struct device *dev)
1633 if (ret) 1652 if (ret)
1634 hdev->driver = NULL; 1653 hdev->driver = NULL;
1635 } 1654 }
1655
1656 up(&hdev->driver_lock);
1636 return ret; 1657 return ret;
1637} 1658}
1638 1659
1639static int hid_device_remove(struct device *dev) 1660static int hid_device_remove(struct device *dev)
1640{ 1661{
1641 struct hid_device *hdev = container_of(dev, struct hid_device, dev); 1662 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
1642 struct hid_driver *hdrv = hdev->driver; 1663 struct hid_driver *hdrv;
1664
1665 if (down_interruptible(&hdev->driver_lock))
1666 return -EINTR;
1643 1667
1668 hdrv = hdev->driver;
1644 if (hdrv) { 1669 if (hdrv) {
1645 if (hdrv->remove) 1670 if (hdrv->remove)
1646 hdrv->remove(hdev); 1671 hdrv->remove(hdev);
@@ -1649,6 +1674,7 @@ static int hid_device_remove(struct device *dev)
1649 hdev->driver = NULL; 1674 hdev->driver = NULL;
1650 } 1675 }
1651 1676
1677 up(&hdev->driver_lock);
1652 return 0; 1678 return 0;
1653} 1679}
1654 1680
@@ -1996,6 +2022,7 @@ struct hid_device *hid_allocate_device(void)
1996 2022
1997 init_waitqueue_head(&hdev->debug_wait); 2023 init_waitqueue_head(&hdev->debug_wait);
1998 INIT_LIST_HEAD(&hdev->debug_list); 2024 INIT_LIST_HEAD(&hdev->debug_list);
2025 sema_init(&hdev->driver_lock, 1);
1999 2026
2000 return hdev; 2027 return hdev;
2001err: 2028err: