Commit 76ca8da9 authored by Pietro Borrello's avatar Pietro Borrello Committed by Benjamin Tissoires

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: 4eb1b01d ("HID: hid-bigbenff: fix race condition for scheduled work during removal")
Signed-off-by: default avatarPietro Borrello <borrello@diag.uniroma1.it>
Link: https://lore.kernel.org/r/20230125-hid-unregister-leds-v4-3-7860c5763c38@diag.uniroma1.itSigned-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
parent 27d2a2fd
...@@ -185,6 +185,15 @@ struct bigben_device { ...@@ -185,6 +185,15 @@ struct bigben_device {
struct work_struct worker; 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) static void bigben_worker(struct work_struct *work)
{ {
...@@ -197,9 +206,6 @@ static void bigben_worker(struct work_struct *work) ...@@ -197,9 +206,6 @@ static void bigben_worker(struct work_struct *work)
u32 len; u32 len;
unsigned long flags; unsigned long flags;
if (bigben->removed)
return;
buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL); buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);
if (!buf) if (!buf)
return; return;
...@@ -285,7 +291,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data, ...@@ -285,7 +291,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
bigben->work_ff = true; bigben->work_ff = true;
spin_unlock_irqrestore(&bigben->lock, flags); spin_unlock_irqrestore(&bigben->lock, flags);
schedule_work(&bigben->worker); bigben_schedule_work(bigben);
} }
return 0; return 0;
...@@ -320,7 +326,7 @@ static void bigben_set_led(struct led_classdev *led, ...@@ -320,7 +326,7 @@ static void bigben_set_led(struct led_classdev *led,
if (work) { if (work) {
bigben->work_led = true; bigben->work_led = true;
schedule_work(&bigben->worker); bigben_schedule_work(bigben);
} }
return; return;
} }
...@@ -450,7 +456,7 @@ static int bigben_probe(struct hid_device *hid, ...@@ -450,7 +456,7 @@ static int bigben_probe(struct hid_device *hid,
bigben->left_motor_force = 0; bigben->left_motor_force = 0;
bigben->work_led = true; bigben->work_led = true;
bigben->work_ff = 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"); hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment