mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Lee Jones <lee@kernel.org>
To: lee@kernel.org, Pavel Machek <pavel@kernel.org>,
	Ben Whitten <ben.whitten@gmail.com>,
	Oliver Jowett <oliver@opencloud.com>,
	Richard Purdie <rpurdie@openedhand.com>,
	Jacek Anaszewski <jacek.anaszewski@gmail.com>,
	linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org
Subject: [PATCH 07/21] leds: trigger: netdev: Serialise work cancellation and state updates
Date: Wed, 21 Oct 2026 12:32:16 +0000	[thread overview]
Message-ID: <20261021123406.3163547-8-lee@kernel.org> (raw)
In-Reply-To: <20261021123406.3163547-1-lee@kernel.org>

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


  parent reply	other threads:[~2026-10-21 12:34 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` [PATCH 03/21] leds: trigger: netdev: Hold RTNL lock across hw_control_get_device() in activate Lee Jones
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 ` [PATCH 05/21] leds: pca955x: Take a reference on the child firmware node Lee Jones
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 [this message]
2026-10-21 12:32 ` [PATCH 08/21] leds: sgm3140: Replace powerdown timer with delayed work Lee Jones
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 ` [PATCH 10/21] leds: class: Initialise trigger_lock before exposing sysfs attributes Lee Jones
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 ` [PATCH 12/21] leds: aat1290: Retain child device_node reference for device lifetime Lee Jones
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 ` [PATCH 14/21] leds: flash: Avoid divide-by-zero in led_clamp_align() Lee Jones
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 ` [PATCH 16/21] leds: lm3532: Clamp full-scale current to minimum bound Lee Jones
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 ` [PATCH 18/21] leds: aw2013: Unlock chip mutex before registering LEDs Lee Jones
2026-10-21 12:32 ` [PATCH 19/21] leds: is31fl32xx: Clamp channel current limit in is31fl3293_reset() 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261021123406.3163547-8-lee@kernel.org \
    --to=lee@kernel.org \
    --cc=ben.whitten@gmail.com \
    --cc=jacek.anaszewski@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=oliver@opencloud.com \
    --cc=pavel@kernel.org \
    --cc=rpurdie@openedhand.com \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®