From 0bee9ee7118f724ad210230e065d822fb24ba420 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Thu, 9 Jun 2022 13:35:48 -0400 Subject: [PATCH] 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. --- .../openbsd_wlan/net80211/ieee80211_haiku.cpp | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp b/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp index 266e99151c..7906c5a6be 100644 --- a/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp +++ b/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp @@ -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; }