rx_urb_complete() was computing the received data length from a device-supplied field using le32_to_cpu(), but the protocol actually uses big-endian byte order. More critically, this device-derived length was passed to plfxlc_mac_rx() without any validation against the actual USB transfer size (urb->actual_length), allowing a malicious or malfunctioning device to cause out-of-bounds memory access. Fix this by: 1. Using urb->actual_length in rx_urb_complete() instead of parsing a length field from the device-supplied buffer with the wrong endianness. 2. Adding minimum length validation in plfxlc_mac_rx() before accessing the rx_status header and payload_length field. 3. Validating payload_length against the actual received length to prevent buffer over-reads. 4. Moving the plfxlc_filter_ack() call to after the buffer pointer has been advanced past the rx_status header and length field, so it receives a pointer to the actual 802.11 frame header instead of the rx_status struct. The old code passed the raw buffer (pointing to struct rx_status) cast to struct ieee80211_hdr, which is a type confusion bug. Fixes: 68d57a07bfe5 ("wireless: add plfxlc driver for pureLiFi X, XL, XC devices") Signed-off-by: Aamir Ahmed --- drivers/net/wireless/purelifi/plfxlc/mac.c | 17 ++++++++++------- drivers/net/wireless/purelifi/plfxlc/usb.c | 5 ++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c index a900421753ac..d2690cce796a 100644 --- a/drivers/net/wireless/purelifi/plfxlc/mac.c +++ b/drivers/net/wireless/purelifi/plfxlc/mac.c @@ -407,6 +407,9 @@ int plfxlc_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, if (!mac->vif) return 0; + if (length < sizeof(struct rx_status) + sizeof(u32)) + return -EINVAL; + status = (struct rx_status *)buffer; memset(&stats, 0, sizeof(stats)); @@ -425,19 +428,19 @@ int plfxlc_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, mac->crc_errors = be64_to_cpu(status->crc_error_count); - /* TODO bad frame check for CRC error*/ - if (plfxlc_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) && - !mac->pass_ctrl) - return 0; - buffer += sizeof(struct rx_status); payload_length = get_unaligned_be32(buffer); + buffer += sizeof(u32); - if (payload_length > 1560) { + if (payload_length > 1560 || + payload_length + sizeof(struct rx_status) + sizeof(u32) > length) { dev_err(plfxlc_mac_dev(mac), " > MTU %u\n", payload_length); return 0; } - buffer += sizeof(u32); + + if (plfxlc_filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) && + !mac->pass_ctrl) + return 0; fc = get_unaligned((__le16 *)buffer); need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc); diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c index 6d24086eb8b7..c33b9131bfdd 100644 --- a/drivers/net/wireless/purelifi/plfxlc/usb.c +++ b/drivers/net/wireless/purelifi/plfxlc/usb.c @@ -117,10 +117,9 @@ static void rx_urb_complete(struct urb *urb) } buffer = urb->transfer_buffer; - length = le32_to_cpu(*(__le32 *)(buffer + sizeof(struct rx_status))) - + sizeof(u32); + length = urb->actual_length; - if (urb->actual_length != (PLF_MSG_STATUS_OFFSET + 1)) { + if (length != (PLF_MSG_STATUS_OFFSET + 1)) { if (usb->initialized && usb->link_up) handle_rx_packet(usb, buffer, length); goto resubmit; -- 2.43.0