From: Andreas Claesson <redacted>
Commit eb51c9f8cb4f ("HID: corsair: cancel worker before unregistering
LED to fix use-after-free") moved cancel_work_sync() in front of
led_classdev_unregister() in the removal paths, because
k90_record_led_work() checks led->removed only once and then
dereferences led->cdev.dev:
if (led->removed)
return;
dev = led->cdev.dev->parent;
The fail_sysfs error path in k90_init_macro_functions() was not
converted and still unregisters the LED before cancelling the work.
The record LED is registered, and its brightness attribute writable,
before sysfs_create_group() runs. If a brightness write queues the work
and sysfs_create_group() then fails, a work item that has already
passed the removed check can dereference led->cdev.dev after
led_classdev_unregister() has freed it:
CPU0 CPU1
k90_init_macro_functions()
led_classdev_register()
k90_brightness_set()
schedule_work()
k90_record_led_work()
if (led->removed) return;
sysfs_create_group() fails
record_led.removed = true
led_classdev_unregister()
device_unregister(cdev.dev)
led->cdev.dev->parent <-- UAF
cancel_work_sync()
Cancel the work before unregistering, as the removal paths do. Keep the
cancel after led_classdev_unregister() as well, since unregistering
turns the LED off and k90_brightness_set() queues the work again.
The problem was pointed out by the Sashiko AI review of a related
patch. It was confirmed on a KASAN kernel with a debug-only change that
forces the sysfs_create_group() failure and widens the race window:
without this patch KASAN reports a slab-use-after-free in
k90_record_led_work(), with it the error path completes cleanly.
Fixes: 6f78193ee9ea ("HID: corsair: Add Corsair Vengeance K90 driver")
Cc: <redacted>
Link: https://sashiko.dev/#/bug/linux-2f786541-d064-428e-a1ab-d203d4112002
Signed-off-by: Andreas Claesson <redacted>
---
The Sashiko AI review of my earlier hid-corsair patch [1] pointed out
this pre-existing race in the probe error path. This patch is
independent of that one and applies on its own; the two touch
different functions.
[1] https://lore.kernel.org/r/20261109-hid-corsair-k90-uaf-v1-1-0621c2c5c451@aclaesson.com (local)
---
drivers/hid/hid-corsair.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index 278c6efb565d..768fbcafb3ad 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -507,6 +507,7 @@ static int k90_init_macro_functions(struct hid_device *dev)
fail_sysfs:
k90->record_led.removed = true;
+ cancel_work_sync(&k90->record_led.work);
led_classdev_unregister(&k90->record_led.cdev);
cancel_work_sync(&k90->record_led.work);
fail_record_led:
---
base-commit: f66469d9905b39d2c260a15d7d6db450f4db67c1
change-id: 20261111-hid-corsair-k90-fail-sysfs-0069f00de809
Best regards,
--
Andreas Claesson [off-list ref]