openbsd_wlan: Refactor implementation of IEEE80211_IOC_SCAN_RESULTS.

* Do not allocate an equally-sized buffer for all scan results,
   but just alloca() a buffer for a single result, and copy this
   back to userland at the end of each iteration.

 * Check remaining space correctly with respect to IE data.

May fix incorrect scan results or userland memory corruptions
seen with the previous code. Also should be faster since it
does not need to allocate large kernel-side temporary buffers.
This commit is contained in:
Augustin Cavalier 2022-06-09 13:35:48 -04:00
parent 5a4ad3a00d
commit 0bee9ee711

View File

@ -181,20 +181,15 @@ wlan_control(void* cookie, uint32 op, void* arg, size_t length)
if (op != SIOCG80211)
return B_BAD_VALUE;
const int32 count = ireq.i_len / sizeof(struct ieee80211req_scan_result);
// ieee80211req_scan_result is variable-length, this will get us the max count
void* req_scan_result = calloc(1, ireq.i_len);
MemoryDeleter resultDeleter(req_scan_result);
if (!resultDeleter.IsSet())
return B_NO_MEMORY;
struct ieee80211_nodereq nodereq;
struct ieee80211_nodereq_all nodereq_all = {};
uint8* out = (uint8*)req_scan_result;
uint16 remaining = ireq.i_len;
for (int i = 0; ; i++) {
// We need a scan_result of maximum possible size to work with.
struct ieee80211req_scan_result* sr = (struct ieee80211req_scan_result*)
alloca(sizeof(struct ieee80211req_scan_result) + IEEE80211_NWID_LEN + 257);
uint16 remaining = ireq.i_len, offset = 0;
for (int i = 0; remaining > 0; i++) {
nodereq_all.na_node = &nodereq;
nodereq_all.na_size = sizeof(struct ieee80211_nodereq);
nodereq_all.na_startnode = i;
@ -207,16 +202,16 @@ wlan_control(void* cookie, uint32 op, void* arg, size_t length)
if (nodereq_all.na_nodes == 0)
break;
int32 required = sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len;
int32 size = sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len;
uint16_t ieLen = 0;
if (nodereq.nr_rsnie[1] != 0) {
ieLen = 2 + nodereq.nr_rsnie[1];
required += ieLen;
size += ieLen;
}
if (remaining < (sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len))
const int32 roundedSize = roundup(size, 4);
if (remaining < roundedSize)
break;
struct ieee80211req_scan_result* sr = (struct ieee80211req_scan_result*)out;
sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
sr->isr_ie_len = ieLen;
sr->isr_freq = ieee80211_ieee2mhz(nodereq.nr_channel, nodereq.nr_chan_flags);
@ -231,15 +226,17 @@ wlan_control(void* cookie, uint32 op, void* arg, size_t length)
memcpy(sr->isr_rates, nodereq.nr_rates, IEEE80211_RATE_MAXSIZE);
sr->isr_ssid_len = nodereq.nr_nwid_len;
sr->isr_meshid_len = 0;
memcpy(out + sr->isr_ie_off, nodereq.nr_nwid, sr->isr_ssid_len);
memcpy(out + sr->isr_ie_off + sr->isr_ssid_len,
nodereq.nr_rsnie, ieLen);
memcpy((uint8*)sr + sr->isr_ie_off, nodereq.nr_nwid, sr->isr_ssid_len);
memcpy((uint8*)sr + sr->isr_ie_off + sr->isr_ssid_len, nodereq.nr_rsnie, ieLen);
sr->isr_len = roundup(sr->isr_ie_off + sr->isr_ssid_len + sr->isr_ie_len, 4);
out += sr->isr_len;
sr->isr_len = roundedSize;
if (user_memcpy((uint8*)ireq.i_data + offset, sr, size) != B_OK)
return B_BAD_ADDRESS;
offset += sr->isr_len;
remaining -= sr->isr_len;
}
ireq.i_len = (out - (uint8*)req_scan_result);
ireq.i_len = offset;
IFF_LOCKGIANT(ifp);
const bigtime_t RAISE_INACT_INTERVAL = 5 * 1000 * 1000 /* 5s */;
@ -255,9 +252,6 @@ wlan_control(void* cookie, uint32 op, void* arg, size_t length)
}
IFF_UNLOCKGIANT(ifp);
if (user_memcpy(ireq.i_data, req_scan_result, ireq.i_len) != B_OK)
return B_BAD_ADDRESS;
break;
}