nl80211: Do not return incomplete hw capability info
authorJouni Malinen <jouni@qca.qualcomm.com>
Tue, 17 Nov 2015 17:01:15 +0000 (19:01 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 17 Nov 2015 17:50:34 +0000 (19:50 +0200)
If a memory allocation fails while parsing driver capabilities, drop all
mode/channel/rate information instead of returning possibly partial
information.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
src/drivers/driver_nl80211_capa.c

index 59a8efb..25fed11 100644 (file)
@@ -966,6 +966,7 @@ struct phy_info_arg {
        u16 *num_modes;
        struct hostapd_hw_modes *modes;
        int last_mode, last_chan_idx;
+       int failed;
 };
 
 static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1083,7 +1084,7 @@ static int phy_info_freqs(struct phy_info_arg *phy_info,
                                   mode->num_channels + new_channels,
                                   sizeof(struct hostapd_channel_data));
        if (!channel)
-               return NL_SKIP;
+               return NL_STOP;
 
        mode->channels = channel;
        mode->num_channels += new_channels;
@@ -1129,7 +1130,7 @@ static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
 
        mode->rates = os_calloc(mode->num_rates, sizeof(int));
        if (!mode->rates)
-               return NL_SKIP;
+               return NL_STOP;
 
        idx = 0;
 
@@ -1158,8 +1159,10 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
                mode = os_realloc_array(phy_info->modes,
                                        *phy_info->num_modes + 1,
                                        sizeof(*mode));
-               if (!mode)
-                       return NL_SKIP;
+               if (!mode) {
+                       phy_info->failed = 1;
+                       return NL_STOP;
+               }
                phy_info->modes = mode;
 
                mode = &phy_info->modes[*(phy_info->num_modes)];
@@ -1195,11 +1198,12 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
        phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
                          tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
        ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
-       if (ret != NL_OK)
-               return ret;
-       ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
-       if (ret != NL_OK)
+       if (ret == NL_OK)
+               ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+       if (ret != NL_OK) {
+               phy_info->failed = 1;
                return ret;
+       }
 
        return NL_OK;
 }
@@ -1599,6 +1603,7 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
                .num_modes = num_modes,
                .modes = NULL,
                .last_mode = -1,
+               .failed = 0,
        };
 
        *num_modes = 0;
@@ -1615,6 +1620,16 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
 
        if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
                nl80211_set_regulatory_flags(drv, &result);
+               if (result.failed) {
+                       int i;
+
+                       for (i = 0; result.modes && i < *num_modes; i++) {
+                               os_free(result.modes[i].channels);
+                               os_free(result.modes[i].rates);
+                       }
+                       os_free(result.modes);
+                       return NULL;
+               }
                return wpa_driver_nl80211_postprocess_modes(result.modes,
                                                            num_modes);
        }