[PATCH v1 08/11] leds: st1202: Take a reference on the LED firmware node

From: Manuel Fombuena

Date: Sat Sep 12 2026 - 18:37:49 EST


st1202_dt_init() stores each LED's firmware node while walking the
device tree with for_each_available_child_of_node_scoped(), which drops
its reference to the node at the end of every iteration. The stored
pointer is not used until st1202_probe() registers the LED class devices
in a separate loop, by which point the driver holds no reference to the
node at all.

With CONFIG_OF_DYNAMIC the node can be freed in between, leaving
led->fwnode dangling and causing a use-after-free when it is handed to
devm_led_classdev_register_ext(). The LED class stores the pointer with
device_set_node() and does not take a reference of its own, so the node
has to stay alive for as long as the LED device does.

Take a reference when the node is stored and release it through a devm
action. The action is registered before the LED class devices are, so it
runs after they have been unregistered.

Fixes: c72e455b89f2 ("leds: leds-st1202: Fix NULL pointer access on race condition")
Signed-off-by: Manuel Fombuena <fombuena@xxxxxxxxxxx>
Assisted-by: LLM
---
drivers/leds/leds-st1202.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c
index 5042fe88fbff..600e004944a6 100644
--- a/drivers/leds/leds-st1202.c
+++ b/drivers/leds/leds-st1202.c
@@ -12,6 +12,7 @@
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string.h>

@@ -395,6 +396,11 @@ static int st1202_blink_set(struct led_classdev *led_cdev,
return 0;
}

+static void st1202_fwnode_put(void *data)
+{
+ fwnode_handle_put(data);
+}
+
static int st1202_dt_init(struct st1202_chip *chip)
{
struct device *dev = &chip->client->dev;
@@ -414,7 +420,11 @@ static int st1202_dt_init(struct st1202_chip *chip)

led = &chip->leds[reg];
led->is_active = true;
- led->fwnode = of_fwnode_handle(child);
+ led->fwnode = fwnode_handle_get(of_fwnode_handle(child));
+
+ err = devm_add_action_or_reset(dev, st1202_fwnode_put, led->fwnode);
+ if (err)
+ return err;

led->led_cdev.max_brightness = U8_MAX;
led->led_cdev.brightness_set_blocking = st1202_led_set;
--
2.55.0