* [PATCH 01/21] leds: lm3532: Propagate error from devm_regulator_get()
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 02/21] leds: lm3532: Use dev_err_probe() when lm3532_parse_node() fails Lee Jones
` (19 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Dan Murphy, Jacek Anaszewski, linux-leds,
linux-kernel
Cc: stable
lm3532_parse_node() catches errors from devm_regulator_get() and sets
priv->regulator to NULL. Because devm_regulator_get() already returns a
dummy regulator when the supply is omitted in Device Tree, an error
pointer is only returned on a genuine failure such as -EPROBE_DEFER.
Masking that error as NULL both breaks probe deferral when a supply is
specified and causes a NULL pointer dereference in regulator_enable()
and regulator_disable() when the LED brightness is later modified via
lm3532_led_enable() or lm3532_led_disable().
Propagate the error code from devm_regulator_get() instead.
Fixes: bc1b8492c764 ("leds: lm3532: Introduce the lm3532 LED driver")
Closes: https://sashiko.dev/#/bug/linux-8836989c-356e-4601-ac4c-6db97861cb00
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-lm3532.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c
index b51496910b08..7f849a166a92 100644
--- a/drivers/leds/leds-lm3532.c
+++ b/drivers/leds/leds-lm3532.c
@@ -570,7 +570,7 @@ static int lm3532_parse_node(struct lm3532_data *priv)
priv->regulator = devm_regulator_get(&priv->client->dev, "vin");
if (IS_ERR(priv->regulator))
- priv->regulator = NULL;
+ return PTR_ERR(priv->regulator);
ret = device_property_read_u32(&priv->client->dev, "ramp-up-us",
&ramp_time);
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 02/21] leds: lm3532: Use dev_err_probe() when lm3532_parse_node() fails
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
2026-10-21 12:32 ` [PATCH 01/21] leds: lm3532: Propagate error from devm_regulator_get() Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 03/21] leds: trigger: netdev: Hold RTNL lock across hw_control_get_device() in activate Lee Jones
` (18 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Dan Murphy, linux-leds, linux-kernel
Now that lm3532_parse_node() propagates errors from devm_regulator_get(),
it can return -EPROBE_DEFER when the supply regulator has not probed yet.
Replace the unconditional dev_err() in lm3532_probe() with
dev_err_probe() so that probe deferral does not log a false-positive
error message.
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-lm3532.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c
index 7f849a166a92..cfad4e791e4b 100644
--- a/drivers/leds/leds-lm3532.c
+++ b/drivers/leds/leds-lm3532.c
@@ -707,12 +707,10 @@ static int lm3532_probe(struct i2c_client *client)
i2c_set_clientdata(client, drvdata);
ret = lm3532_parse_node(drvdata);
- if (ret) {
- dev_err(&client->dev, "Failed to parse node\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "Failed to parse node\n");
- return ret;
+ return 0;
}
static const struct of_device_id of_lm3532_leds_match[] = {
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 03/21] leds: trigger: netdev: Hold RTNL lock across hw_control_get_device() in activate
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
2026-10-21 12:32 ` [PATCH 01/21] leds: lm3532: Propagate error from devm_regulator_get() Lee Jones
2026-10-21 12:32 ` [PATCH 02/21] leds: lm3532: Use dev_err_probe() when lm3532_parse_node() fails Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 04/21] leds: aw2013: Guard LED array index in aw2013_probe_dt() Lee Jones
` (17 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Ben Whitten, Oliver Jowett, Richard Purdie,
Christian Marangi, David S. Miller, Andrew Lunn, linux-leds,
linux-kernel
Cc: stable
In netdev_trig_activate(), hw_control_get_device() is invoked without
holding rtnl_lock, and the returned struct device pointer is then
dereferenced via dev_name() and passed into set_device_name().
Because implementations such as phy_led_hw_control_get_device() and DSA
port LED callbacks return the embedded struct device of an attached
net_device without incrementing its reference count, a concurrent
network device unregistration or rename can detach and free the
net_device (or its kobject name) before set_device_name() copies the
name and acquires rtnl_lock, leading to a use-after-free.
Move rtnl_lock acquisition to the callers of set_device_name() so that
netdev_trig_activate() holds rtnl_lock across hw_control_get_device(),
dev_name(), and set_device_name().
Fixes: 0316cc5629d1 ("leds: trigger: netdev: init mode if hw control already active")
Closes: https://sashiko.dev/#/bug/linux-ef006cc6-9a69-46b2-ae9f-8de934d37aff
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/trigger/ledtrig-netdev.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 5b0132484594..d69031bbd2b8 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -279,6 +279,8 @@ static int set_device_name(struct led_netdev_data *trigger_data,
struct net_device *new_dev = NULL;
char device_name[IFNAMSIZ];
+ ASSERT_RTNL();
+
if (size >= IFNAMSIZ)
return -EINVAL;
@@ -292,7 +294,6 @@ static int set_device_name(struct led_netdev_data *trigger_data,
/*
* Lock order: rtnl_lock -> netdev instance lock -> trigger_data lock.
*/
- rtnl_lock();
if (device_name[0]) {
new_dev = dev_get_by_name(&init_net, device_name);
if (new_dev)
@@ -319,7 +320,6 @@ static int set_device_name(struct led_netdev_data *trigger_data,
mutex_unlock(&trigger_data->lock);
if (new_dev)
netdev_unlock_ops(new_dev);
- rtnl_unlock();
return 0;
}
@@ -331,7 +331,9 @@ static ssize_t device_name_store(struct device *dev,
struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
int ret;
+ rtnl_lock();
ret = set_device_name(trigger_data, buf, size);
+ rtnl_unlock();
if (ret < 0)
return ret;
@@ -760,6 +762,7 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
* Init already enabled mode in hw control.
*/
if (supports_hw_control(led_cdev)) {
+ rtnl_lock();
dev = led_cdev->hw_control_get_device(led_cdev);
if (dev) {
const char *name = dev_name(dev);
@@ -771,6 +774,7 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
if (!rc)
trigger_data->mode = mode;
}
+ rtnl_unlock();
}
led_set_trigger_data(led_cdev, trigger_data);
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 04/21] leds: aw2013: Guard LED array index in aw2013_probe_dt()
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (2 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 03/21] leds: trigger: netdev: Hold RTNL lock across hw_control_get_device() in activate Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 05/21] leds: pca955x: Take a reference on the child firmware node Lee Jones
` (16 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Nikita Travkin, linux-leds, linux-kernel; +Cc: stable
aw2013_probe_dt() validates the initial child node count against
AW2013_MAX_LEDS via of_get_available_child_count() before iterating over
available child nodes with for_each_available_child_of_node_scoped().
Add an explicit bounds check on the loop index before indexing into
chip->leds[] to ensure the statically sized array cannot be overrun if
additional child nodes become available during iteration.
Fixes: 59ea3c9faf32 ("leds: add aw2013 driver")
Closes: https://sashiko.dev/#/bug/linux-577a1fa0-4e80-42cc-8c7f-34c43c131d95
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-aw2013.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c
index 216755d6010f..dfeac8b2993f 100644
--- a/drivers/leds/leds-aw2013.c
+++ b/drivers/leds/leds-aw2013.c
@@ -286,6 +286,9 @@ static int aw2013_probe_dt(struct aw2013 *chip)
continue;
}
+ if (i >= AW2013_MAX_LEDS)
+ return -EINVAL;
+
led = &chip->leds[i];
led->num = source;
led->chip = chip;
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 05/21] leds: pca955x: Take a reference on the child firmware node
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (3 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 04/21] leds: aw2013: Guard LED array index in aw2013_probe_dt() Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 06/21] leds: lp50xx: Guard sub-LED count in lp50xx_probe_dt() Lee Jones
` (15 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Nate Case, Eddie James, linux-leds, linux-kernel
Cc: stable
pca955x_get_pdata() caches each child fwnode in led->fwnode while
iterating over the device's child nodes with
device_for_each_child_node(), which drops its reference on each child
node when advancing to the next iteration or finishing the loop.
Because the LED core stores init_data.fwnode via device_set_node()
without taking an additional reference of its own, dynamically
allocated firmware nodes can be freed before or while the LED class
device uses them.
Acquire a reference with fwnode_handle_get() when caching child in
led->fwnode and register a devm action to release it when the device is
unbound or probe fails.
Fixes: 7c4815929276 ("leds: pca955x: Let the core process the fwnode")
Closes: https://sashiko.dev/#/bug/linux-a78a30d3-e8c1-43eb-af6c-c26279dc21e7
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-pca955x.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 273383351ba0..70043e0e6f08 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -530,12 +530,16 @@ static int pca955x_gpio_direction_output(struct gpio_chip *gc,
}
#endif /* CONFIG_LEDS_PCA955X_GPIO */
+static void pca955x_fwnode_put(void *data)
+{
+ fwnode_handle_put(data);
+}
+
static struct pca955x_platform_data *
pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)
{
struct pca955x_platform_data *pdata;
struct pca955x_led *led;
- struct fwnode_handle *child;
int count;
count = device_get_child_node_count(&client->dev);
@@ -552,7 +556,7 @@ pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)
if (!pdata->leds)
return ERR_PTR(-ENOMEM);
- device_for_each_child_node(&client->dev, child) {
+ device_for_each_child_node_scoped(&client->dev, child) {
u32 reg;
int res;
@@ -561,8 +565,17 @@ pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip)
continue;
led = &pdata->leds[reg];
+ if (led->fwnode)
+ devm_release_action(&client->dev, pca955x_fwnode_put,
+ led->fwnode);
led->type = PCA955X_TYPE_LED;
- led->fwnode = child;
+ led->fwnode = fwnode_handle_get(child);
+
+ res = devm_add_action_or_reset(&client->dev, pca955x_fwnode_put,
+ led->fwnode);
+ if (res)
+ return ERR_PTR(res);
+
led->default_state = led_init_default_state_get(child);
fwnode_property_read_u32(child, "type", &led->type);
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 06/21] leds: lp50xx: Guard sub-LED count in lp50xx_probe_dt()
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (4 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 05/21] leds: pca955x: Take a reference on the child firmware node Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 07/21] leds: trigger: netdev: Serialise work cancellation and state updates Lee Jones
` (14 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Christian Hitz, Dan Murphy, Jacek Anaszewski,
linux-leds, linux-kernel
Cc: stable
lp50xx_probe_dt() allocates mc_led_info with LP50XX_LEDS_PER_MODULE (3)
elements and increments num_colors for each sub-LED child node without
checking whether num_colors has already reached LP50XX_LEDS_PER_MODULE.
If a device tree specifies more than three sub-LED child nodes with
valid reg properties, led->mc_cdev.num_colors exceeds the capacity of
the subled_info array, causing out-of-bounds accesses during brightness
and intensity updates.
Return -EINVAL if num_colors reaches LP50XX_LEDS_PER_MODULE when
iterating over sub-LED child nodes.
Fixes: 242b81170fb8 ("leds: lp50xx: Add the LP50XX family of the RGB LED driver")
Closes: https://sashiko.dev/#/bug/linux-a0c116ab-2f34-4f53-a007-d7a42f28677b
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-lp50xx.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c
index 20bfb315bda1..32b0d802d474 100644
--- a/drivers/leds/leds-lp50xx.c
+++ b/drivers/leds/leds-lp50xx.c
@@ -507,6 +507,14 @@ static int lp50xx_probe_dt(struct lp50xx *priv)
fwnode_for_each_child_node(child, led_node) {
int multi_index;
+
+ if (num_colors >= LP50XX_LEDS_PER_MODULE) {
+ fwnode_handle_put(led_node);
+ return dev_err_probe(priv->dev, -EINVAL,
+ "Too many sub-LEDs (max %d)\n",
+ LP50XX_LEDS_PER_MODULE);
+ }
+
ret = fwnode_property_read_u32(led_node, "color",
&color_id);
if (ret) {
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 07/21] leds: trigger: netdev: Serialise work cancellation and state updates
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (5 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 06/21] leds: lp50xx: Guard sub-LED count in lp50xx_probe_dt() Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 08/21] leds: sgm3140: Replace powerdown timer with delayed work Lee Jones
` (13 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Ben Whitten, Oliver Jowett, Richard Purdie,
Jacek Anaszewski, linux-leds, linux-kernel
Cc: stable
set_device_name() and netdev_trig_notify() cancel trigger_data->work
before acquiring trigger_data->lock, whilst netdev_led_attr_store() and
interval_store() cancel trigger_data->work, update trigger_data state,
and call set_baseline_state() without holding trigger_data->lock at all.
Because set_baseline_state() can re-arm trigger_data->work outside the
lock, a concurrent notifier or sysfs store can re-schedule
netdev_trig_work() after cancel_delayed_work_sync() has returned and
whilst set_device_name() or netdev_trig_notify() is replacing or
dropping trigger_data->net_dev, leading to a use-after-free in
netdev_trig_work().
Move cancel_delayed_work_sync() inside trigger_data->lock in
set_device_name() and netdev_trig_notify(), and hold trigger_data->lock
across work cancellation, state updates, and set_baseline_state() in
netdev_led_attr_store() and interval_store().
Fixes: 06f502f57d0d ("leds: trigger: Introduce a NETDEV trigger")
Closes: https://sashiko.dev/#/bug/linux-b2b6c3bb-8017-4623-94d4-2999efa6a663
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/trigger/ledtrig-netdev.c | 35 +++++++++++++++++++--------
1 file changed, 25 insertions(+), 10 deletions(-)
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index d69031bbd2b8..9fa56ac523c7 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -284,8 +284,6 @@ static int set_device_name(struct led_netdev_data *trigger_data,
if (size >= IFNAMSIZ)
return -EINVAL;
- cancel_delayed_work_sync(&trigger_data->work);
-
memcpy(device_name, name, size);
device_name[size] = 0;
if (size > 0 && device_name[size - 1] == '\n')
@@ -301,6 +299,8 @@ static int set_device_name(struct led_netdev_data *trigger_data,
}
mutex_lock(&trigger_data->lock);
+ cancel_delayed_work_sync(&trigger_data->work);
+
dev_put(trigger_data->net_dev);
trigger_data->net_dev = new_dev;
strscpy(trigger_data->device_name, device_name);
@@ -384,7 +384,7 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
{
struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
struct led_classdev *led_cdev = trigger_data->led_cdev;
- unsigned long state, mode = trigger_data->mode;
+ unsigned long state, mode;
int ret;
int bit;
@@ -416,6 +416,9 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
return -EINVAL;
}
+ mutex_lock(&trigger_data->lock);
+
+ mode = trigger_data->mode;
assign_bit(bit, &mode, state);
if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
@@ -428,8 +431,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
test_bit(TRIGGER_NETDEV_LINK_25000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_40000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_50000, &mode) ||
- test_bit(TRIGGER_NETDEV_LINK_100000, &mode)))
+ test_bit(TRIGGER_NETDEV_LINK_100000, &mode))) {
+ mutex_unlock(&trigger_data->lock);
return -EINVAL;
+ }
cancel_delayed_work_sync(&trigger_data->work);
@@ -437,11 +442,15 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
trigger_data->hw_control = can_hw_control(trigger_data);
if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking &&
- !trigger_data->hw_control)
+ !trigger_data->hw_control) {
+ mutex_unlock(&trigger_data->lock);
return -EOPNOTSUPP;
+ }
set_baseline_state(trigger_data);
+ mutex_unlock(&trigger_data->lock);
+
return size;
}
@@ -493,13 +502,17 @@ static ssize_t interval_store(struct device *dev,
unsigned long value;
int ret;
- if (trigger_data->hw_control)
- return -EINVAL;
-
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
+ mutex_lock(&trigger_data->lock);
+
+ if (trigger_data->hw_control) {
+ mutex_unlock(&trigger_data->lock);
+ return -EINVAL;
+ }
+
/* impose some basic bounds on the timer interval */
if (value >= 5 && value <= 10000) {
cancel_delayed_work_sync(&trigger_data->work);
@@ -508,6 +521,8 @@ static ssize_t interval_store(struct device *dev,
set_baseline_state(trigger_data); /* resets timer */
}
+ mutex_unlock(&trigger_data->lock);
+
return size;
}
@@ -628,10 +643,10 @@ static int netdev_trig_notify(struct notifier_block *nb,
(evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
return NOTIFY_DONE;
- cancel_delayed_work_sync(&trigger_data->work);
-
mutex_lock(&trigger_data->lock);
+ cancel_delayed_work_sync(&trigger_data->work);
+
trigger_data->carrier_link_up = false;
trigger_data->link_speed = SPEED_UNKNOWN;
trigger_data->duplex = DUPLEX_UNKNOWN;
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 08/21] leds: sgm3140: Replace powerdown timer with delayed work
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (6 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 07/21] leds: trigger: netdev: Serialise work cancellation and state updates Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 09/21] leds: sgm3140: Cancel powerdown work via devres after LED unregistration Lee Jones
` (12 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Luca Weiss, linux-leds, linux-kernel; +Cc: stable
sgm3140_powerdown_timer() runs in atomic timer softirq context and calls
regulator_disable(), which acquires sleepable mutexes, as well as
gpiod_set_value() on GPIOs that are otherwise driven via
gpiod_set_value_cansleep().
Replace powerdown_timer with a delayed_work item so that the powerdown
sequence executes in process context and can safely sleep when disabling
the regulator and driving the GPIOs.
Fixes: cef8ec8cbd21 ("leds: add sgm3140 driver")
Closes: https://sashiko.dev/#/bug/linux-8c3788a3-17f9-49dc-89be-c9f5844b0b95
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/flash/leds-sgm3140.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c
index dc6840357370..478573da1d61 100644
--- a/drivers/leds/flash/leds-sgm3140.c
+++ b/drivers/leds/flash/leds-sgm3140.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
+#include <linux/workqueue.h>
#include <media/v4l2-flash-led-class.h>
@@ -16,7 +17,7 @@ struct sgm3140 {
struct led_classdev_flash fled_cdev;
struct v4l2_flash *v4l2_flash;
- struct timer_list powerdown_timer;
+ struct delayed_work powerdown_work;
struct gpio_desc *flash_gpio;
struct gpio_desc *enable_gpio;
@@ -52,10 +53,10 @@ static int sgm3140_strobe_set(struct led_classdev_flash *fled_cdev, bool state)
}
gpiod_set_value_cansleep(priv->flash_gpio, 1);
gpiod_set_value_cansleep(priv->enable_gpio, 1);
- mod_timer(&priv->powerdown_timer,
- jiffies + usecs_to_jiffies(priv->timeout));
+ schedule_delayed_work(&priv->powerdown_work,
+ usecs_to_jiffies(priv->timeout));
} else {
- timer_delete_sync(&priv->powerdown_timer);
+ cancel_delayed_work_sync(&priv->powerdown_work);
gpiod_set_value_cansleep(priv->enable_gpio, 0);
gpiod_set_value_cansleep(priv->flash_gpio, 0);
ret = regulator_disable(priv->vin_regulator);
@@ -75,7 +76,7 @@ static int sgm3140_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
{
struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
- *state = timer_pending(&priv->powerdown_timer);
+ *state = delayed_work_pending(&priv->powerdown_work);
return 0;
}
@@ -117,7 +118,7 @@ static int sgm3140_brightness_set(struct led_classdev *led_cdev,
gpiod_set_value_cansleep(priv->flash_gpio, 0);
gpiod_set_value_cansleep(priv->enable_gpio, 1);
} else {
- timer_delete_sync(&priv->powerdown_timer);
+ cancel_delayed_work_sync(&priv->powerdown_work);
gpiod_set_value_cansleep(priv->flash_gpio, 0);
gpiod_set_value_cansleep(priv->enable_gpio, 0);
ret = regulator_disable(priv->vin_regulator);
@@ -133,12 +134,13 @@ static int sgm3140_brightness_set(struct led_classdev *led_cdev,
return 0;
}
-static void sgm3140_powerdown_timer(struct timer_list *t)
+static void sgm3140_powerdown_work(struct work_struct *work)
{
- struct sgm3140 *priv = timer_container_of(priv, t, powerdown_timer);
+ struct sgm3140 *priv = container_of(work, struct sgm3140,
+ powerdown_work.work);
- gpiod_set_value(priv->enable_gpio, 0);
- gpiod_set_value(priv->flash_gpio, 0);
+ gpiod_set_value_cansleep(priv->enable_gpio, 0);
+ gpiod_set_value_cansleep(priv->flash_gpio, 0);
regulator_disable(priv->vin_regulator);
priv->enabled = false;
@@ -235,7 +237,7 @@ static int sgm3140_probe(struct platform_device *pdev)
*/
priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT);
- timer_setup(&priv->powerdown_timer, sgm3140_powerdown_timer, 0);
+ INIT_DELAYED_WORK(&priv->powerdown_work, sgm3140_powerdown_work);
fled_cdev = &priv->fled_cdev;
led_cdev = &fled_cdev->led_cdev;
@@ -284,7 +286,7 @@ static void sgm3140_remove(struct platform_device *pdev)
{
struct sgm3140 *priv = platform_get_drvdata(pdev);
- timer_delete_sync(&priv->powerdown_timer);
+ cancel_delayed_work_sync(&priv->powerdown_work);
v4l2_flash_release(priv->v4l2_flash);
}
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 09/21] leds: sgm3140: Cancel powerdown work via devres after LED unregistration
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (7 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 08/21] leds: sgm3140: Replace powerdown timer with delayed work Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 10/21] leds: class: Initialise trigger_lock before exposing sysfs attributes Lee Jones
` (11 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Luca Weiss, linux-leds, linux-kernel; +Cc: stable
Because devm_led_classdev_flash_register_ext() unregisters the flash LED
device via devres after sgm3140_remove() returns, cancelling
powerdown_work manually in sgm3140_remove() leaves a window where a
concurrent sysfs write to flash_strobe can re-arm powerdown_work before
the LED class device is unregistered and priv is freed. A similar
window exists in sgm3140_probe() if v4l2_flash_init() fails after the
LED class device has been registered.
Initialise powerdown_work with devm_delayed_work_autocancel() before
calling devm_led_classdev_flash_register_ext() so that devres cancels
the work after unregistering the LED class device and before releasing
the regulator, GPIOs, and driver data.
Fixes: cef8ec8cbd21 ("leds: add sgm3140 driver")
Closes: https://sashiko.dev/#/bug/linux-81468d52-b80d-46b5-b1b4-29c35e9acb21
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/flash/leds-sgm3140.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c
index 478573da1d61..4e21144900c9 100644
--- a/drivers/leds/flash/leds-sgm3140.c
+++ b/drivers/leds/flash/leds-sgm3140.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 Luca Weiss <luca@z3ntu.xyz>
+#include <linux/devm-helpers.h>
#include <linux/gpio/consumer.h>
#include <linux/led-class-flash.h>
#include <linux/module.h>
@@ -237,7 +238,10 @@ static int sgm3140_probe(struct platform_device *pdev)
*/
priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT);
- INIT_DELAYED_WORK(&priv->powerdown_work, sgm3140_powerdown_work);
+ ret = devm_delayed_work_autocancel(&pdev->dev, &priv->powerdown_work,
+ sgm3140_powerdown_work);
+ if (ret)
+ goto err;
fled_cdev = &priv->fled_cdev;
led_cdev = &fled_cdev->led_cdev;
@@ -286,8 +290,6 @@ static void sgm3140_remove(struct platform_device *pdev)
{
struct sgm3140 *priv = platform_get_drvdata(pdev);
- cancel_delayed_work_sync(&priv->powerdown_work);
-
v4l2_flash_release(priv->v4l2_flash);
}
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 10/21] leds: class: Initialise trigger_lock before exposing sysfs attributes
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (8 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 09/21] leds: sgm3140: Cancel powerdown work via devres after LED unregistration Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 11/21] leds: class: Unlock led_access before unregistering device on error Lee Jones
` (10 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, John Lenz, Richard Purdie, Florian Fainelli,
linux-leds, linux-kernel
Cc: stable
led_classdev_register_ext() calls device_create_with_groups(), which
creates the LED trigger sysfs attributes and emits the KOBJ_ADD uevent,
before initialising led_cdev->trigger_lock with init_rwsem().
Because trigger_read() and trigger_may_offload_to_hw_show() acquire
led_cdev->trigger_lock without holding led_cdev->led_access, a
concurrent sysfs read during registration can call down_read() on an
uninitialised rw_semaphore.
Initialise led_cdev->trigger_lock before calling
device_create_with_groups().
Fixes: 14b5d6dd40b3 ("leds: Fix race between LED device uevent and actual attributes creation")
Closes: https://sashiko.dev/#/bug/linux-376c7db5-ddc1-4faa-9f5b-43d2d3b7cb0d
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/led-class.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 00be3cb96c00..2a3040fa3c4b 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -532,6 +532,9 @@ int led_classdev_register_ext(struct device *parent,
if (led_cdev->color >= LED_COLOR_ID_MAX)
dev_warn(parent, "LED %s color identifier out of range\n", final_name);
+#ifdef CONFIG_LEDS_TRIGGERS
+ init_rwsem(&led_cdev->trigger_lock);
+#endif
mutex_init(&led_cdev->led_access);
mutex_lock(&led_cdev->led_access);
led_cdev->dev = device_create_with_groups(&leds_class, parent, 0,
@@ -554,9 +557,6 @@ int led_classdev_register_ext(struct device *parent,
}
led_cdev->work_flags = 0;
-#ifdef CONFIG_LEDS_TRIGGERS
- init_rwsem(&led_cdev->trigger_lock);
-#endif
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
led_cdev->brightness_hw_changed = -1;
#endif
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 11/21] leds: class: Unlock led_access before unregistering device on error
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (9 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 10/21] leds: class: Initialise trigger_lock before exposing sysfs attributes Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 12/21] leds: aat1290: Retain child device_node reference for device lifetime Lee Jones
` (9 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, John Lenz, Richard Purdie, Jacek Anaszewski,
Luis Henriques, linux-leds, linux-kernel
Cc: stable
In led_classdev_register_ext(), if led_add_brightness_hw_changed() fails
after device_create_with_groups() has exposed the LED sysfs attributes,
calling device_unregister() while holding led_cdev->led_access can
cause an AB-BA deadlock with a concurrent sysfs access that holds an
active kernfs reference and blocks on led_cdev->led_access.
Disable sysfs writes and release led_cdev->led_access before calling
device_unregister() on the error path.
Fixes: 6d71021ab3b0 ("leds: class: ensure workqueue is initialized before setting brightness")
Closes: https://sashiko.dev/#/bug/linux-400d8d29-9e78-4fb7-af37-b07c92bdf4df
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/led-class.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 2a3040fa3c4b..c8529003dbe1 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -549,9 +549,10 @@ int led_classdev_register_ext(struct device *parent,
if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
ret = led_add_brightness_hw_changed(led_cdev);
if (ret) {
+ led_sysfs_disable(led_cdev);
+ mutex_unlock(&led_cdev->led_access);
device_unregister(led_cdev->dev);
led_cdev->dev = NULL;
- mutex_unlock(&led_cdev->led_access);
return ret;
}
}
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 12/21] leds: aat1290: Retain child device_node reference for device lifetime
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (10 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 11/21] leds: class: Unlock led_access before unregistering device on error Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 13/21] leds: aat1290: Prevent out-of-bounds read in aat1290_brightness_to_intensity() Lee Jones
` (8 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Jacek Anaszewski, Bryan Wu, Kyungmin Park,
Krzysztof Kozlowski, linux-leds, linux-kernel
Cc: stable
aat1290_led_parse_dt() acquires a child device_node with the
__free(device_node) cleanup attribute and assigns it directly to the
*sub_node output parameter without consuming the pointer via
no_free_ptr(). Consequently, of_node_put() is called automatically when
aat1290_led_parse_dt() returns, leaving aat1290_led_probe() with a
dangling pointer when it passes the firmware node to
led_classdev_flash_register_ext() and v4l2_flash_init().
Because the LED core stores init_data.fwnode via device_set_node()
without taking an additional reference of its own, consume child_node
with no_free_ptr() and register a devm action to release the reference
when the device is unbound or probe fails.
Fixes: ac69b9038f82 ("leds: aat1290: add support for V4L2 Flash sub-device")
Fixes: 1e63395e58b2 ("leds: aat1290: Use scoped device node handling to simplify error paths")
Closes: https://sashiko.dev/#/bug/linux-2255c65e-840f-49ab-9dc5-227a4eab4c37
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/flash/leds-aat1290.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c
index 4bde0b07fe78..ea9af6c3fbf4 100644
--- a/drivers/leds/flash/leds-aat1290.c
+++ b/drivers/leds/flash/leds-aat1290.c
@@ -211,6 +211,11 @@ static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
return 0;
}
+static void aat1290_of_node_put(void *data)
+{
+ of_node_put(data);
+}
+
static int aat1290_led_parse_dt(struct aat1290_led *led,
struct aat1290_led_config_data *cfg,
struct device_node **sub_node)
@@ -279,9 +284,9 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
return ret;
}
- *sub_node = child_node;
+ *sub_node = no_free_ptr(child_node);
- return 0;
+ return devm_add_action_or_reset(dev, aat1290_of_node_put, *sub_node);
}
static void aat1290_led_validate_mm_current(struct aat1290_led *led,
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 13/21] leds: aat1290: Prevent out-of-bounds read in aat1290_brightness_to_intensity()
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (11 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 12/21] leds: aat1290: Retain child device_node reference for device lifetime Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 14/21] leds: flash: Avoid divide-by-zero in led_clamp_align() Lee Jones
` (7 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Jacek Anaszewski, Kyungmin Park, Bryan Wu,
linux-leds, linux-kernel
Cc: stable
When a flash strobe event or external strobe update resets
led_cdev->brightness to LED_OFF (0), a subsequent V4L2 torch intensity
query passes brightness == 0 to aat1290_brightness_to_intensity().
Subtracting 1 unconditionally indexes led->mm_current_scale[-1] out of
bounds.
Return 0 when brightness is LED_OFF before indexing mm_current_scale.
Fixes: ac69b9038f82 ("leds: aat1290: add support for V4L2 Flash sub-device")
Closes: https://sashiko.dev/#/bug/linux-9e7db3d9-0ea0-42f2-8027-01aa467d3a7d
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/flash/leds-aat1290.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c
index ea9af6c3fbf4..223bbba9114f 100644
--- a/drivers/leds/flash/leds-aat1290.c
+++ b/drivers/leds/flash/leds-aat1290.c
@@ -393,6 +393,9 @@ static s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash,
struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
+ if (brightness == LED_OFF)
+ return 0;
+
return led->mm_current_scale[brightness - 1];
}
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 14/21] leds: flash: Avoid divide-by-zero in led_clamp_align()
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (12 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 13/21] leds: aat1290: Prevent out-of-bounds read in aat1290_brightness_to_intensity() Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 15/21] leds: lm3532: Validate LED string indices in lm3532_parse_node() Lee Jones
` (6 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Dmitry Antipov, Jacek Anaszewski, Bryan Wu,
Kyungmin Park, linux-leds, linux-kernel
Cc: stable
If a flash LED driver leaves a setting's step size as zero, writing to
the corresponding sysfs attribute invokes led_clamp_align() and triggers
an integer division by zero when aligning the offset to s->step.
Clamp s->val to [s->min, s->max] and return early when s->step is zero.
Fixes: 7aea8389a77a ("leds: Add LED Flash class extension to the LED subsystem")
Closes: https://sashiko.dev/#/bug/linux-9d06d7fe-310b-4aee-8b16-0fb49550c287
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/led-class-flash.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c
index 181dfd49c6ad..16b3b6b9365b 100644
--- a/drivers/leds/led-class-flash.c
+++ b/drivers/leds/led-class-flash.c
@@ -375,6 +375,11 @@ static void led_clamp_align(struct led_flash_setting *s)
{
u32 v, offset;
+ if (!s->step) {
+ s->val = clamp(s->val, s->min, s->max);
+ return;
+ }
+
v = s->val + s->step / 2;
v = clamp(v, s->min, s->max);
offset = v - s->min;
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 15/21] leds: lm3532: Validate LED string indices in lm3532_parse_node()
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (13 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 14/21] leds: flash: Avoid divide-by-zero in led_clamp_align() Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 16/21] leds: lm3532: Clamp full-scale current to minimum bound Lee Jones
` (5 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Dan Murphy, Jacek Anaszewski, linux-leds,
linux-kernel
Cc: stable
lm3532_parse_node() reads the led-sources property into led->led_strings
without bounding the individual string indices. If a firmware node
specifies an index of LM3532_MAX_LED_STRINGS or greater,
lm3532_init_registers() shifts beyond the hardware output configuration
fields and, for values of 16 or greater, triggers a 32-bit
shift-out-of-bounds.
Reject LED string indices >= LM3532_MAX_LED_STRINGS with -EINVAL when
parsing the firmware node.
Fixes: bc1b8492c764 ("leds: lm3532: Introduce the lm3532 LED driver")
Closes: https://sashiko.dev/#/bug/linux-f6f7a3f6-4dc4-46b4-84e3-c26fc9099572
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-lm3532.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c
index cfad4e791e4b..6fe752889487 100644
--- a/drivers/leds/leds-lm3532.c
+++ b/drivers/leds/leds-lm3532.c
@@ -649,6 +649,11 @@ static int lm3532_parse_node(struct lm3532_data *priv)
return ret;
}
+ for (int j = 0; j < led->num_leds; j++) {
+ if (led->led_strings[j] >= LM3532_MAX_LED_STRINGS)
+ return -EINVAL;
+ }
+
led->priv = priv;
led->led_dev.brightness_set_blocking = lm3532_brightness_set;
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 16/21] leds: lm3532: Clamp full-scale current to minimum bound
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (14 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 15/21] leds: lm3532: Validate LED string indices in lm3532_parse_node() Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 17/21] leds: aat1290: Avoid leaking devres pinctrl handles on strobe source changes Lee Jones
` (4 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Dan Murphy, Jacek Anaszewski, linux-leds,
linux-kernel
Cc: stable
lm3532_parse_node() bounds led->full_scale_current to LM3532_FS_CURR_MAX
using min(), but does not enforce the lower bound LM3532_FS_CURR_MIN.
If a firmware node specifies led-max-microamp between 1 and
LM3532_FS_CURR_MIN - 1, subtracting LM3532_FS_CURR_MIN in
lm3532_init_registers() underflows and writes a corrupted full-scale
current register value.
Use clamp() when led-max-microamp is present so led->full_scale_current
is bounded to [LM3532_FS_CURR_MIN, LM3532_FS_CURR_MAX].
Fixes: 517ea49a7b23 ("leds: lm3532: Add full scale current configuration")
Closes: https://sashiko.dev/#/bug/linux-114b6c69-6d76-4f4d-bb87-0980b1e15bbd
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-lm3532.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c
index 6fe752889487..327460de2ff8 100644
--- a/drivers/leds/leds-lm3532.c
+++ b/drivers/leds/leds-lm3532.c
@@ -615,14 +615,16 @@ static int lm3532_parse_node(struct lm3532_data *priv)
return ret;
}
- if (fwnode_property_present(child, "led-max-microamp") &&
- fwnode_property_read_u32(child, "led-max-microamp",
- &led->full_scale_current))
- dev_err(&priv->client->dev,
- "Failed getting led-max-microamp\n");
- else
- led->full_scale_current = min(led->full_scale_current,
- LM3532_FS_CURR_MAX);
+ if (fwnode_property_present(child, "led-max-microamp")) {
+ if (fwnode_property_read_u32(child, "led-max-microamp",
+ &led->full_scale_current))
+ dev_err(&priv->client->dev,
+ "Failed getting led-max-microamp\n");
+ else
+ led->full_scale_current = clamp(led->full_scale_current,
+ LM3532_FS_CURR_MIN,
+ LM3532_FS_CURR_MAX);
+ }
if (led->mode == LM3532_BL_MODE_ALS) {
led->mode = LM3532_ALS_CTRL;
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 17/21] leds: aat1290: Avoid leaking devres pinctrl handles on strobe source changes
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (15 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 16/21] leds: lm3532: Clamp full-scale current to minimum bound Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 18/21] leds: aw2013: Unlock chip mutex before registering LEDs Lee Jones
` (3 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Jacek Anaszewski, Kyungmin Park, Bryan Wu,
linux-leds, linux-kernel
Cc: stable
aat1290_led_external_strobe_set() is a runtime V4L2 control callback
that calls devm_pinctrl_get_select() every time the strobe source is
switched. Because devm_pinctrl_get() allocates a new devres entry and
takes an additional pinctrl reference on each call, repeated updates to
V4L2_CID_FLASH_STROBE_SOURCE leak memory and pinctrl references for the
lifetime of the device.
Use pinctrl_get_select() followed by pinctrl_put() instead, as the
device's underlying pinctrl instance is already held across the device
lifetime.
Fixes: ac69b9038f82 ("leds: aat1290: add support for V4L2 Flash sub-device")
Closes: https://sashiko.dev/#/bug/linux-e1e22682-0cb6-4dcc-9a7f-01993691f7b0
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/flash/leds-aat1290.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c
index 223bbba9114f..fc7904966183 100644
--- a/drivers/leds/flash/leds-aat1290.c
+++ b/drivers/leds/flash/leds-aat1290.c
@@ -413,13 +413,15 @@ static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash,
led->movie_mode = false;
led_cdev->brightness = 0;
- pinctrl = devm_pinctrl_get_select(&led->pdev->dev,
- enable ? "isp" : "host");
+ pinctrl = pinctrl_get_select(&led->pdev->dev,
+ enable ? "isp" : "host");
if (IS_ERR(pinctrl)) {
dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n");
return PTR_ERR(pinctrl);
}
+ pinctrl_put(pinctrl);
+
return 0;
}
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 18/21] leds: aw2013: Unlock chip mutex before registering LEDs
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (16 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 17/21] leds: aat1290: Avoid leaking devres pinctrl handles on strobe source changes Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 19/21] leds: is31fl32xx: Clamp channel current limit in is31fl3293_reset() Lee Jones
` (2 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Nikita Travkin, linux-leds, linux-kernel; +Cc: stable
aw2013_probe() holds chip->mutex across aw2013_probe_dt(), which calls
devm_led_classdev_register_ext(). When a default trigger such as
"timer" is configured on an LED in the Device Tree, the LED core
synchronously activates the trigger during registration and invokes
aw2013_blink_set() on the same thread, which attempts to acquire
chip->mutex and deadlocks.
Reset the chip, disable the regulators, and release chip->mutex in
aw2013_probe() before calling aw2013_probe_dt(), and update
chip->num_leds before each LED is registered so trigger callbacks can
enable the chip cleanly during registration.
Fixes: 59ea3c9faf32 ("leds: add aw2013 driver")
Closes: https://sashiko.dev/#/bug/linux-5e2c9083-b95b-4aa4-aa0a-b0be8865ff12
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-aw2013.c | 54 ++++++++++++++++++++++----------------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c
index dfeac8b2993f..c947cd068963 100644
--- a/drivers/leds/leds-aw2013.c
+++ b/drivers/leds/leds-aw2013.c
@@ -271,8 +271,6 @@ static int aw2013_probe_dt(struct aw2013 *chip)
if (!count || count > AW2013_MAX_LEDS)
return -EINVAL;
- regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET);
-
for_each_available_child_of_node_scoped(np, child) {
struct led_init_data init_data = {};
u32 source;
@@ -305,19 +303,29 @@ static int aw2013_probe_dt(struct aw2013 *chip)
led->cdev.brightness_set_blocking = aw2013_brightness_set;
led->cdev.blink_set = aw2013_blink_set;
+ mutex_lock(&chip->mutex);
+ chip->num_leds = ++i;
+ if (chip->enabled) {
+ ret = regmap_update_bits(chip->regmap,
+ AW2013_LCFG(led->num),
+ AW2013_LCFG_IMAX_MASK,
+ led->imax);
+ if (ret) {
+ mutex_unlock(&chip->mutex);
+ return ret;
+ }
+ }
+ mutex_unlock(&chip->mutex);
+
ret = devm_led_classdev_register_ext(&chip->client->dev,
&led->cdev, &init_data);
if (ret < 0)
return ret;
-
- i++;
}
if (!count)
return -EINVAL;
- chip->num_leds = i;
-
return 0;
}
@@ -393,25 +401,27 @@ static int aw2013_probe(struct i2c_client *client)
goto error_reg;
}
+ ret = regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET);
+ if (ret) {
+ dev_err(&client->dev, "Failed to reset chip: %d\n", ret);
+ goto error_reg;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
+ chip->regulators);
+ if (ret) {
+ dev_err(&client->dev,
+ "Failed to disable regulators: %d\n", ret);
+ goto error;
+ }
+
+ mutex_unlock(&chip->mutex);
+
ret = devm_add_action(&client->dev, aw2013_chip_disable_action, chip);
if (ret)
- goto error_reg;
+ return ret;
- ret = aw2013_probe_dt(chip);
- if (ret < 0)
- goto error_reg;
-
- ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
- chip->regulators);
- if (ret) {
- dev_err(&client->dev,
- "Failed to disable regulators: %d\n", ret);
- goto error;
- }
-
- mutex_unlock(&chip->mutex);
-
- return 0;
+ return aw2013_probe_dt(chip);
error_reg:
regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 19/21] leds: is31fl32xx: Clamp channel current limit in is31fl3293_reset()
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (17 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 18/21] leds: aw2013: Unlock chip mutex before registering LEDs Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 20/21] leds: flash: Terminate sysfs_groups array with NULL Lee Jones
2026-10-21 12:32 ` [PATCH 21/21] leds: lm3692x: Use devm_mutex_init() to avoid premature mutex destruction Lee Jones
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Daniel Mack, David Rivshin, linux-leds, linux-kernel
Cc: stable
is31fl3293_reset() uses max() instead of min() when comparing
led_data->max_microamp against IS31FL3293_MAX_MICROAMP, ignoring any
lower led-max-microamp limit configured in the Device Tree and
overflowing the 8-bit current level register if a larger value is
provided.
Clamp led_data->max_microamp to IS31FL3293_MAX_MICROAMP when specified,
falling back to IS31FL3293_MAX_MICROAMP when the optional property is
omitted.
Fixes: a18983b95a61 ("leds: is31f132xx: Add support for is31fl3293")
Closes: https://sashiko.dev/#/bug/linux-878f32ad-ca35-4759-a194-76fe03caee21
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-is31fl32xx.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c
index 285085d27453..aebedf8b2f4b 100644
--- a/drivers/leds/leds-is31fl32xx.c
+++ b/drivers/leds/leds-is31fl32xx.c
@@ -194,7 +194,8 @@ static int is31fl3293_reset(struct is31fl32xx_priv *priv)
for (i = 0; i < priv->num_leds; i++) {
struct is31fl32xx_led_data *led_data = &priv->leds[i];
int current_level_reg = IS31FL3293_CL_REG + led_data->channel - 1;
- int microamp = max(led_data->max_microamp, IS31FL3293_MAX_MICROAMP);
+ u32 microamp = min_not_zero(led_data->max_microamp,
+ IS31FL3293_MAX_MICROAMP);
int current_level = (microamp * 0xff) / IS31FL3293_MAX_MICROAMP;
ret = is31fl32xx_write(priv, current_level_reg, current_level);
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 20/21] leds: flash: Terminate sysfs_groups array with NULL
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (18 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 19/21] leds: is31fl32xx: Clamp channel current limit in is31fl3293_reset() Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
2026-10-21 12:32 ` [PATCH 21/21] leds: lm3692x: Use devm_mutex_init() to avoid premature mutex destruction Lee Jones
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Dmitry Antipov, Jacek Anaszewski, Bryan Wu,
Kyungmin Park, linux-leds, linux-kernel
Cc: stable
led_flash_init_sysfs_groups() populates fled_cdev->sysfs_groups and
assigns it to led_cdev->groups without writing a trailing NULL entry.
If fled_cdev is not zero-initialised by the caller or is re-registered
with fewer operations, device_add_groups() will read past the populated
entries.
Explicitly NULL-terminate flash_groups at num_sysfs_groups.
Fixes: 7aea8389a77a ("leds: Add LED Flash class extension to the LED subsystem")
Closes: https://sashiko.dev/#/bug/linux-ad7bad43-ab12-40c5-addd-7d1f23d83391
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/led-class-flash.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c
index 16b3b6b9365b..90ae93f16193 100644
--- a/drivers/leds/led-class-flash.c
+++ b/drivers/leds/led-class-flash.c
@@ -273,6 +273,8 @@ static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev)
if (ops->fault_get)
flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
+ flash_groups[num_sysfs_groups] = NULL;
+
led_cdev->groups = flash_groups;
}
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 21/21] leds: lm3692x: Use devm_mutex_init() to avoid premature mutex destruction
2026-10-21 12:32 [PATCH 00/21] leds: Assorted bug fixes across core, flash, trigger, and drivers Lee Jones
` (19 preceding siblings ...)
2026-10-21 12:32 ` [PATCH 20/21] leds: flash: Terminate sysfs_groups array with NULL Lee Jones
@ 2026-10-21 12:32 ` Lee Jones
20 siblings, 0 replies; 22+ messages in thread
From: Lee Jones @ 2026-10-21 12:32 UTC (permalink / raw)
To: lee, Pavel Machek, Dan Murphy, Jacek Anaszewski, linux-leds,
linux-kernel
Cc: stable
lm3692x_probe() registers the LED class device using
devm_led_classdev_register_ext(), so the LED is unregistered during
devres release after lm3692x_remove() returns. Calling mutex_destroy()
in lm3692x_remove() destroys led->lock while the LED sysfs interface is
still active and before devres unregistration turns off the LED via
lm3692x_brightness_set().
Initialise led->lock with devm_mutex_init() before registering the LED
class device and drop mutex_destroy() from lm3692x_remove() so the mutex
outlives the LED device.
Fixes: 9699cb6bbef2 ("leds: lm3692x: Introduce LM3692x dual string driver")
Closes: https://sashiko.dev/#/bug/linux-012e3698-2af6-4390-88c9-d8989d2dc2a6
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/leds/leds-lm3692x.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c
index 9c58d54c5783..ec53f9d25654 100644
--- a/drivers/leds/leds-lm3692x.c
+++ b/drivers/leds/leds-lm3692x.c
@@ -470,7 +470,10 @@ static int lm3692x_probe(struct i2c_client *client)
if (!led)
return -ENOMEM;
- mutex_init(&led->lock);
+ ret = devm_mutex_init(&client->dev, &led->lock);
+ if (ret)
+ return ret;
+
led->client = client;
led->led_dev.brightness_set_blocking = lm3692x_brightness_set;
led->model_id = id->driver_data;
@@ -500,7 +503,6 @@ static void lm3692x_remove(struct i2c_client *client)
struct lm3692x_led *led = i2c_get_clientdata(client);
lm3692x_leds_disable(led);
- mutex_destroy(&led->lock);
}
static const struct i2c_device_id lm3692x_id[] = {
--
2.56.0.218.g1ec904b173-goog
^ permalink raw reply [flat|nested] 22+ messages in thread