[PATCHv2 wireless] mac80211: stop tpt LED trigger timer before freeing trigger

Rosen Penev <[email protected]>
Newsgroups org.kernel.vger.linux-kernel,org.kernel.vger.linux-wireless
Message-ID <[email protected]>
The throughput (tpt) LED trigger timer is only stopped in
ieee80211_stop_tpt_led_trig(), which is reached via ieee80211_stop_device()
when the interface is brought down. On a hot unplug, the device is torn down
through ieee80211_unregister_hw() without ever going through
ieee80211_do_stop(), so the timer is never stopped.

ieee80211_led_exit() then frees local->tpt_led_trigger (which embeds the
timer) without cancelling it. The still-armed timer keeps firing and
dereferences the freed tpt_trig, walking local->tpt_led's LED list in
led_trigger_blink() -> led_blink_set_nosleep() on the freed rt2x00 LED class
device, causing a use-after-free page fault in interrupt context.

Call ieee80211_stop_tpt_led_trig() in ieee80211_led_exit(), before
unregistering the trigger and freeing the struct, so the timer is always
stopped and can never run after the trigger/LEDs are torn down.

Fixes: e1e540685437 ("mac80211: add throughput based LED blink trigger")
Assisted-by: opencode:hy3-free
Signed-off-by: Rosen Penev <[email protected]>
---
 v2: move static function instead of forward declaration
 net/mac80211/led.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/net/mac80211/led.c b/net/mac80211/led.c
index b5600d223452..42dcd56eb983 100644
--- a/net/mac80211/led.c
+++ b/net/mac80211/led.c
@@ -194,6 +194,19 @@ void ieee80211_led_init(struct ieee80211_local *local)
 	}
 }
 
+static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+
+	if (!tpt_trig->running)
+		return;
+
+	tpt_trig->running = false;
+	timer_delete_sync(&tpt_trig->timer);
+
+	led_trigger_event(&local->tpt_led, LED_OFF);
+}
+
 void ieee80211_led_exit(struct ieee80211_local *local)
 {
 	if (local->radio_led.name)
@@ -206,6 +219,7 @@ void ieee80211_led_exit(struct ieee80211_local *local)
 		led_trigger_unregister(&local->rx_led);
 
 	if (local->tpt_led_trigger) {
+		ieee80211_stop_tpt_led_trig(local);
 		led_trigger_unregister(&local->tpt_led);
 		kfree(local->tpt_led_trigger);
 	}
@@ -335,19 +349,6 @@ static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
 	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
 }
 
-static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
-{
-	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
-
-	if (!tpt_trig->running)
-		return;
-
-	tpt_trig->running = false;
-	timer_delete_sync(&tpt_trig->timer);
-
-	led_trigger_event(&local->tpt_led, LED_OFF);
-}
-
 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
 				unsigned int types_on, unsigned int types_off)
 {
-- 
2.55.0
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.