]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
nl80211: Fix crash by setting the drv->ctx properly
authorDhanavandhana Kannan <dhanavandhana.kannan@oss.qualcomm.com>
Fri, 22 Aug 2025 11:01:39 +0000 (16:31 +0530)
committerJouni Malinen <j@w1.fi>
Tue, 26 Aug 2025 09:40:14 +0000 (12:40 +0300)
During nl80211_stop_ap(), the active_links bitmap is updated, and the
flink is reassigned to the next available active link in an MLD.
active_links becomes zero when nl80211_stop_ap() is called for the last
remaining link.

For example:

Consider a 3-link MLD (2.4 GHz, 5 GHz, and 6 GHz), with flink initially
pointing to 2.4 GHz:

nl80211_stop_ap() is called for 2.4 GHz -> flink is updated to 5 GHz.
Then for 5 GHz -> flink is updated to 6 GHz.
Finally for 6 GHz -> all links are stopped, and active_links becomes
zero.

Following this, when driver_nl80211_link_remove() is called and links
are removed in a specific order (say 2.4 GHz first, then 6 GHz), the
flink (which was pointing to 6 GHz) gets removed. However, the driver
context (drv->ctx) still points to bss->ctx, which was associated with
the now-deleted flink.

If an event arrives from the driver after this removal, it tries to
access drv->ctx, which now points to freed memory. This results in a
crash due to invalid memory access.

Fix by ensuring that flink is updated only during nl80211_remove_link()
and not during nl80211_stop_ap(). Also ensure that drv->ctx is properly
updated with flink->ctx during link removal and ensure that eloop timers
are properly cancelled before changing the drv->ctx.

Fixes: 2fe31050c248 ("nl80211: Use active_links to notify start/stop state of links")
Signed-off-by: Dhanavandhana Kannan <dhanavandhana.kannan@oss.qualcomm.com>
src/drivers/driver_nl80211.c

index 693d846fe3f6eb3dfd61bfc599a51cb4f1284866..5fb4529e56a507091ebeb5a41b0ba5b6009e83a3 100644 (file)
@@ -9705,9 +9705,6 @@ fail:
 
 void nl80211_update_active_links(struct i802_bss *bss, int link_id)
 {
-       struct i802_link *link = &bss->links[link_id];
-       size_t i;
-
        wpa_printf(MSG_DEBUG, "nl80211: Update link (ifindex=%d link_id=%u)",
                   bss->ifindex, link_id);
 
@@ -9720,14 +9717,6 @@ void nl80211_update_active_links(struct i802_bss *bss, int link_id)
        wpa_driver_nl80211_del_beacon(bss, link_id);
 
        bss->active_links &= ~BIT(link_id);
-
-       /* Choose new deflink if we are removing that link */
-       if (bss->flink == link) {
-               for_each_link(bss->active_links, i) {
-                       bss->flink = &bss->links[i];
-                       break;
-               }
-       }
 }
 
 
@@ -9738,6 +9727,7 @@ int nl80211_remove_link(struct i802_bss *bss, int link_id)
        struct nl_msg *msg;
        int ret;
        u8 link_addr[ETH_ALEN];
+       size_t i;
 
        wpa_printf(MSG_DEBUG, "nl80211: Remove link (ifindex=%d link_id=%u)",
                   bss->ifindex, link_id);
@@ -9763,6 +9753,14 @@ int nl80211_remove_link(struct i802_bss *bss, int link_id)
        if (bss->scan_link == link)
                bss->scan_link = NULL;
 
+       /* Choose new deflink if we are removing that link */
+       if (bss->flink == link) {
+               for_each_link(bss->valid_links, i) {
+                       bss->flink = &bss->links[i];
+                       break;
+               }
+       }
+
        /* If this was the last link, reset default link */
        if (!bss->valid_links) {
                /* TODO: Does keeping freq/bandwidth make sense? */
@@ -11095,10 +11093,14 @@ static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
 
        nl80211_remove_link(bss, link_id);
 
-       bss->ctx = bss->flink->ctx;
-
-       if (drv->first_bss == bss && bss->valid_links)
+       if (drv->ctx == bss->ctx) {
+               eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill,
+                                    drv, drv->ctx);
+               bss->ctx = bss->flink->ctx;
                drv->ctx = bss->ctx;
+       } else {
+               bss->ctx = bss->flink->ctx;
+       }
 
        if (!bss->valid_links) {
                void *ctx = bss->ctx;