oss-sec mailing list archives

Marvell Wifi Driver mwifiex_uap_parse_tail_ies Heap Overflow


From: "huangwen" <huangwen () venusgroup com cn>
Date: Sat, 1 Jun 2019 18:07:57 +0800

Hi,

There is heap-based buffer overflow in marvell wifi chip driver in Linux
kernel,allows local users to cause a denial of service(system crash) or
possibly execute arbitrary code.

I provided a patch in mail attachment for reference only. 

 

 

Description

==========

The problem is inside mwifiex_uap_parse_tail_ies function in
drivers/net/wireless/marvell/mwifiex/ie.c. 

There are two memcpy in this function.The memcpy in while loop will be
called when element_id is not equal to WLAN_EID_SSID,WLAN_EID_SUPP_RATES
etc.

The copy dst buffer gen_ie->ie_buffer is a array with size
IEEE_MAX_IE_SIZE(256), the src buffer is element in cfg80211_beacon_data
from user space. 

There is not len check for two memcpy in this function.

If special elements are constructed (E.g.
WLAN_EID_SUPPORTED_OPERATING_CLASSES) to make memcpy called repeatedly, will
finally trigger the overflow.

 

 

struct mwifiex_ie {

         __le16 ie_index;

         __le16 mgmt_subtype_mask;

         __le16 ie_length;

         u8 ie_buffer[IEEE_MAX_IE_SIZE];

} __packed;

 

#define IEEE_MAX_IE_SIZE              256

 

static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,

                                           struct cfg80211_beacon_data
*info)

{

         struct mwifiex_ie *gen_ie;

         struct ieee_types_header *hdr;

         struct ieee80211_vendor_ie *vendorhdr;

         u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;

         int left_len, parsed_len = 0;

 

         if (!info->tail || !info->tail_len)

                   return 0;

 

         gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);

         if (!gen_ie)

                   return -ENOMEM;

 

         left_len = info->tail_len;

 

         /* Many IEs are generated in FW by parsing bss configuration.

          * Let's not add them here; else we may end up duplicating these
IEs

          */

         while (left_len > sizeof(struct ieee_types_header)) {

                   hdr = (void *)(info->tail + parsed_len);

                   switch (hdr->element_id) {

                   case WLAN_EID_SSID:

                   case WLAN_EID_SUPP_RATES:

                   case WLAN_EID_COUNTRY:

                   case WLAN_EID_PWR_CONSTRAINT:

                   case WLAN_EID_ERP_INFO:

                   case WLAN_EID_EXT_SUPP_RATES:

                   case WLAN_EID_HT_CAPABILITY:

                   case WLAN_EID_HT_OPERATION:

                   case WLAN_EID_VHT_CAPABILITY:

                   case WLAN_EID_VHT_OPERATION:

                            break;

                   case WLAN_EID_VENDOR_SPECIFIC:

                            /* Skip only Microsoft WMM IE */

                            if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,

 
WLAN_OUI_TYPE_MICROSOFT_WMM,

                                                            (const u8 *)hdr,

                                                            hdr->len +
sizeof(struct ieee_types_header)))

                                     break;

                            /* fall through */

                   default:

                            memcpy(gen_ie->ie_buffer + ie_len, hdr,
//!!!!!!overflow

                                   hdr->len + sizeof(struct
ieee_types_header));

                            ie_len += hdr->len + sizeof(struct
ieee_types_header);

                            break;

                   }

                   left_len -= hdr->len + sizeof(struct ieee_types_header);

                   parsed_len += hdr->len + sizeof(struct
ieee_types_header);

         }

 

         /* parse only WPA vendor IE from tail, WMM IE is configured by

          * bss_config command

          */

         vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,

 
WLAN_OUI_TYPE_MICROSOFT_WPA,

                                                            info->tail,
info->tail_len);

         if (vendorhdr) {

                   memcpy(gen_ie->ie_buffer + ie_len, vendorhdr,
//!!!!!!overflow

                          vendorhdr->len + sizeof(struct
ieee_types_header));

                   ie_len += vendorhdr->len + sizeof(struct
ieee_types_header);

         }

         .....

}

 

 

Credit

==========

This issue was discovered by huangwen of ADLab of Venustech

 

 

Patch

=====

https://lore.kernel.org/linux-wireless/20190531131841.7552-1-tiwai () suse de

 


Current thread: