]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Update iface->current_mode when fetching new hw_features
authorNijun Gong <quic_ngong@quicinc.com>
Tue, 11 Jul 2023 13:21:21 +0000 (21:21 +0800)
committerJouni Malinen <j@w1.fi>
Tue, 22 Aug 2023 17:56:49 +0000 (20:56 +0300)
When a CHANNEL_LIST_CHANGED event is received, memory of
iface->hw_features is freed and allocated again with
hostapd_get_hw_features(), but iface->current_mode still refer to the
original memory address, which is not correct since that memory has been
freed. This could happen in cases where the driver provides channel list
updates during the lifetime of the started BSS.

Fix this by updated iface->current_mode to point to the new array of hw
features.

Fixes: 0837863fbc62 ("AP: Handle 6 GHz AP state machine with NO_IR flags")
Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
src/ap/hw_features.c

index 5bef2087d2b8aa809932fe7e954b220f981b63b6..2fcc437d3709024e3190479a1c614c24d07ec231 100644 (file)
@@ -79,6 +79,9 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
        u16 num_modes, flags;
        struct hostapd_hw_modes *modes;
        u8 dfs_domain;
+       enum hostapd_hw_mode mode = HOSTAPD_MODE_IEEE80211ANY;
+       bool is_6ghz = false;
+       bool orig_mode_valid = false;
 
        if (hostapd_drv_none(hapd))
                return -1;
@@ -95,6 +98,20 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
        iface->hw_flags = flags;
        iface->dfs_domain = dfs_domain;
 
+       if (iface->current_mode) {
+               /*
+                * Received driver event CHANNEL_LIST_CHANGED when the current
+                * hw mode is valid. Clear iface->current_mode temporarily as
+                * the mode instance will be replaced with a new instance and
+                * the current pointer would be pointing to freed memory.
+                */
+               orig_mode_valid = true;
+               mode = iface->current_mode->mode;
+               is_6ghz = mode == HOSTAPD_MODE_IEEE80211A &&
+                       iface->current_mode->num_channels > 0 &&
+                       is_6ghz_freq(iface->current_mode->channels[0].freq);
+               iface->current_mode = NULL;
+       }
        hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
        iface->hw_features = modes;
        iface->num_hw_features = num_modes;
@@ -104,6 +121,12 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
                int dfs_enabled = hapd->iconf->ieee80211h &&
                        (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
 
+               /* Restore orignal mode if possible */
+               if (orig_mode_valid && feature->mode == mode &&
+                   feature->num_channels > 0 &&
+                   is_6ghz == is_6ghz_freq(feature->channels[0].freq))
+                       iface->current_mode = feature;
+
                /* set flag for channels we can use in current regulatory
                 * domain */
                for (j = 0; j < feature->num_channels; j++) {
@@ -141,6 +164,12 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
                }
        }
 
+       if (orig_mode_valid && !iface->current_mode) {
+               wpa_printf(MSG_ERROR,
+                          "%s: Could not update iface->current_mode",
+                          __func__);
+       }
+
        return 0;
 }