[PATCH 03/21] leds: trigger: netdev: Hold RTNL lock across hw_control_get_device() in activate

From: Lee Jones

Date: Wed Oct 21 2026 - 08:48:13 EST


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@xxxxxxxxxxxxxxx
Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
---
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