From 16a2667203ea059fb7012ee1def0f4f798711c09 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 2 Jan 2020 23:16:22 +0200 Subject: [PATCH] nl80211: Don't accept interrupted dump responses 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 --- src/drivers/driver_nl80211.c | 42 ++++++++++++++++++++++++++++++- src/drivers/driver_nl80211_scan.c | 14 +++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index c6eca92ca..998b3948b 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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; diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 9afa5b304..413d6f757 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -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;