diff options
author | Pietro Borrello <borrello@diag.uniroma1.it> | 2023-02-12 22:00:01 +0300 |
---|---|---|
committer | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2023-02-15 20:18:48 +0300 |
commit | 76ca8da989c7d97a7f76c75d475fe95a584439d7 (patch) | |
tree | 4e31fbde4f4424f3c2bb39c3fc1225b06e9b881b /drivers/hid/hid-bigbenff.c | |
parent | 27d2a2fd844ec7da70d19fabb482304fd1e0595b (diff) | |
download | linux-76ca8da989c7d97a7f76c75d475fe95a584439d7.tar.xz |
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.c | 18 |
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"); |