]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
nl80211: Don't accept interrupted dump responses
authorJouni Malinen <j@w1.fi>
Thu, 2 Jan 2020 21:16:22 +0000 (23:16 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 2 Jan 2020 21:34:53 +0000 (23:34 +0200)
Netlink dump message may be interrupted if an internal inconsistency is
detected in the kernel code. This can happen, e.g., if a Beacon frame
from the current AP is received while NL80211_CMD_GET_SCAN is used to
fetch scan results. Previously, such cases would end up not reporting an
error and that could result in processing partial data.

Modify this by detecting this special interruption case and converting
it to an error. For the NL80211_CMD_GET_SCAN, try again up to 10 times
to get the full response. For other commands (which are not yet known to
fail in similar manner frequently), report an error to the caller.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211_scan.c

index c6eca92ca3a48e9b20c25edf058b22332166bce2..998b3948b4e3f954f430189b6cde6525a7c377c8 100644 (file)
@@ -398,7 +398,21 @@ static int send_and_recv(struct nl80211_global *global,
 
        while (err > 0) {
                int res = nl_recvmsgs(nl_handle, cb);
-               if (res < 0) {
+
+               if (res == -NLE_DUMP_INTR) {
+                       /* Most likely one of the nl80211 dump routines hit a
+                        * case where internal results changed while the dump
+                        * was being sent. The most common known case for this
+                        * is scan results fetching while associated were every
+                        * received Beacon frame from the AP may end up
+                        * incrementing bss_generation. This
+                        * NL80211_CMD_GET_SCAN case tries again in the caller;
+                        * other cases (of which there are no known common ones)
+                        * will stop and return an error. */
+                       wpa_printf(MSG_DEBUG, "nl80211: %s; convert to -EAGAIN",
+                                  nl_geterror(res));
+                       err = -EAGAIN;
+               } else if (res < 0) {
                        wpa_printf(MSG_INFO,
                                   "nl80211: %s->nl_recvmsgs failed: %d (%s)",
                                   __func__, res, nl_geterror(res));
@@ -1336,12 +1350,25 @@ int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid)
        struct nl_msg *msg;
        int ret;
        struct nl80211_get_assoc_freq_arg arg;
+       int count = 0;
 
+try_again:
        msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
        os_memset(&arg, 0, sizeof(arg));
        arg.drv = drv;
        ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
                                 &arg);
+       if (ret == -EAGAIN) {
+               count++;
+               if (count >= 10) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Failed to receive consistent scan result dump for get_assoc_ssid");
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Failed to receive consistent scan result dump for get_assoc_ssid - try again");
+                       goto try_again;
+               }
+       }
        if (ret == 0) {
                os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len);
                return arg.assoc_ssid_len;
@@ -1357,12 +1384,25 @@ unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
        struct nl_msg *msg;
        int ret;
        struct nl80211_get_assoc_freq_arg arg;
+       int count = 0;
 
+try_again:
        msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
        os_memset(&arg, 0, sizeof(arg));
        arg.drv = drv;
        ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
                                 &arg);
+       if (ret == -EAGAIN) {
+               count++;
+               if (count >= 10) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Failed to receive consistent scan result dump for get_assoc_freq");
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Failed to receive consistent scan result dump for get_assoc_freq - try again");
+                       goto try_again;
+               }
+       }
        if (ret == 0) {
                unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
                        arg.ibss_freq : arg.assoc_freq;
index 9afa5b304cf87642d321731e7b6fd0afedb8bd83..413d6f75777b75c72da3dc38542e668f25ef4585 100644 (file)
@@ -928,7 +928,9 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
        struct wpa_scan_results *res;
        int ret;
        struct nl80211_bss_info_arg arg;
+       int count = 0;
 
+try_again:
        res = os_zalloc(sizeof(*res));
        if (res == NULL)
                return NULL;
@@ -941,6 +943,18 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
        arg.drv = drv;
        arg.res = res;
        ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+       if (ret == -EAGAIN) {
+               count++;
+               if (count >= 10) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Failed to receive consistent scan result dump");
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Failed to receive consistent scan result dump - try again");
+                       wpa_scan_results_free(res);
+                       goto try_again;
+               }
+       }
        if (ret == 0) {
                struct nl80211_noise_info info;