summaryrefslogtreecommitdiff
path: root/drivers/hid/hid-bigbenff.c
diff options
context:
space:
mode:
authorPietro Borrello <borrello@diag.uniroma1.it>2023-02-12 19:00:01 +0000
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>2023-02-15 18:18:48 +0100
commit76ca8da989c7d97a7f76c75d475fe95a584439d7 (patch)
tree4e31fbde4f4424f3c2bb39c3fc1225b06e9b881b /drivers/hid/hid-bigbenff.c
parent27d2a2fd844ec7da70d19fabb482304fd1e0595b (diff)
HID: bigben: use spinlock to safely schedule workers
Use spinlocks to deal with workers introducing a wrapper bigben_schedule_work(), and several spinlock checks. Otherwise, bigben_set_led() may schedule bigben->worker after the structure has been freed, causing a use-after-free. Fixes: 4eb1b01de5b9 ("HID: hid-bigbenff: fix race condition for scheduled work during removal") Signed-off-by: Pietro Borrello <borrello@diag.uniroma1.it> Link: https://lore.kernel.org/r/20230125-hid-unregister-leds-v4-3-7860c5763c38@diag.uniroma1.it Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Diffstat (limited to 'drivers/hid/hid-bigbenff.c')
-rw-r--r--drivers/hid/hid-bigbenff.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c
index b98c5f31c184..9d6560db762b 100644
--- a/drivers/hid/hid-bigbenff.c
+++ b/drivers/hid/hid-bigbenff.c
@@ -185,6 +185,15 @@ struct bigben_device {
struct work_struct worker;
};
+static inline void bigben_schedule_work(struct bigben_device *bigben)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&bigben->lock, flags);
+ if (!bigben->removed)
+ schedule_work(&bigben->worker);
+ spin_unlock_irqrestore(&bigben->lock, flags);
+}
static void bigben_worker(struct work_struct *work)
{
@@ -197,9 +206,6 @@ static void bigben_worker(struct work_struct *work)
u32 len;
unsigned long flags;
- if (bigben->removed)
- return;
-
buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);
if (!buf)
return;
@@ -285,7 +291,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
bigben->work_ff = true;
spin_unlock_irqrestore(&bigben->lock, flags);
- schedule_work(&bigben->worker);
+ bigben_schedule_work(bigben);
}
return 0;
@@ -320,7 +326,7 @@ static void bigben_set_led(struct led_classdev *led,
if (work) {
bigben->work_led = true;
- schedule_work(&bigben->worker);
+ bigben_schedule_work(bigben);
}
return;
}
@@ -450,7 +456,7 @@ static int bigben_probe(struct hid_device *hid,
bigben->left_motor_force = 0;
bigben->work_led = true;
bigben->work_ff = true;
- schedule_work(&bigben->worker);
+ bigben_schedule_work(bigben);
hid_info(hid, "LED and force feedback support for BigBen gamepad\n");