[PATCH] leds: class: disable sysfs before unregistering LED devices

David Lee <[email protected]>
Newsgroups org.kernel.vger.linux-leds,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
Closing a uleds device removes the LED class device and then frees the
struct uleds_device that contains it. The LED class device is also exposed
through sysfs, where attributes such as trigger, brightness, delay_on, and
delay_off can remain open across unregister.

That creates a lifetime race. led_classdev_unregister() tears down the LED
trigger, software blink timer, brightness state, and trigger-specific sysfs
groups, but it does not first block LED sysfs writers. An already-open sysfs
attribute can therefore enter the LED trigger or timer paths while unregister
is tearing down the same embedded struct led_classdev.

For uleds this can become a use-after-free: after unregister returns,
uleds_release() frees the containing struct uleds_device, while stale sysfs
or timer paths may still access led_classdev fields embedded in that freed
object. KASAN reports use-after-free reads and writes in __run_timers(),
led_timer_function(), and uleds_brightness_set().

Fix this by entering the existing LED sysfs exclusion protocol at the start
of unregister. Take led_cdev->led_access, set LED_SYSFS_DISABLE, perform the
trigger, timer, brightness, and trigger-sysfs teardown, then release the
mutex. A racing sysfs writer either completes before unregister starts or
sees the disabled state and returns -EBUSY instead of touching an object that
the driver may free.

Fixes: e381322b0190 ("leds: Introduce userspace LED class driver")
Cc: [email protected]
Signed-off-by: David Lee <[email protected]>
Assisted-by: Codex:gpt-5.5
---
Trail of Bits has a minimal PoC that triggers this crash on a custom
kernel build, which can be shared further if needed.

drivers/leds/led-class.c |    5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index a51b0ed53886..a697d6740011 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -616,6 +616,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
 	if (IS_ERR_OR_NULL(led_cdev->dev))
 		return;
 
+	mutex_lock(&led_cdev->led_access);
+	led_sysfs_disable(led_cdev);
+
 #ifdef CONFIG_LEDS_TRIGGERS
 	down_write(&led_cdev->trigger_lock);
 	if (led_cdev->trigger)
@@ -636,6 +639,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
 	if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
 		led_remove_brightness_hw_changed(led_cdev);
 
+	mutex_unlock(&led_cdev->led_access);
+
 	device_unregister(led_cdev->dev);
 
 	down_write(&leds_list_lock);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.