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 <j@w1.fi>
This commit is contained in:
Jouni Malinen 2020-01-02 23:16:22 +02:00
parent 696acdf333
commit 16a2667203
2 changed files with 55 additions and 1 deletions

View 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;

View 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;