mwifiex: Fix heap overflow in mwifiex_uap_parse_tail_ies() [Linux 4.4.186]

This Linux kernel change "mwifiex: Fix heap overflow in mwifiex_uap_parse_tail_ies()" is included in the Linux 4.4.186 release. This change is authored by Takashi Iwai <tiwai [at] suse.de> on Fri May 31 15:18:41 2019 +0200. The commit for this change in Linux stable tree is 3a611df (patch) which is from upstream commit 69ae4f6. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 69ae4f6.

mwifiex: Fix heap overflow in mwifiex_uap_parse_tail_ies()

commit 69ae4f6aac1578575126319d3f55550e7e440449 upstream.

A few places in mwifiex_uap_parse_tail_ies() perform memcpy()
unconditionally, which may lead to either buffer overflow or read over
boundary.

This patch addresses the issues by checking the read size and the
destination size at each place more properly.  Along with the fixes,
the patch cleans up the code slightly by introducing a temporary
variable for the token size, and unifies the error path with the
standard goto statement.

Reported-by: huangwen <huangwen@venustech.com.cn>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

There are 45 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/net/wireless/mwifiex/ie.c | 45 ++++++++++++++++++++++++++-------------
 1 file changed, 30 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c
index abf52d2..de84357 100644
--- a/drivers/net/wireless/mwifiex/ie.c
+++ b/drivers/net/wireless/mwifiex/ie.c
@@ -328,6 +328,8 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
    struct ieee80211_vendor_ie *vendorhdr;
    u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
    int left_len, parsed_len = 0;
+   unsigned int token_len;
+   int err = 0;

    if (!info->tail || !info->tail_len)
        return 0;
@@ -343,6 +345,12 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
     */
    while (left_len > sizeof(struct ieee_types_header)) {
        hdr = (void *)(info->tail + parsed_len);
+       token_len = hdr->len + sizeof(struct ieee_types_header);
+       if (token_len > left_len) {
+           err = -EINVAL;
+           goto out;
+       }
+
        switch (hdr->element_id) {
        case WLAN_EID_SSID:
        case WLAN_EID_SUPP_RATES:
@@ -356,13 +364,16 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
        case WLAN_EID_VENDOR_SPECIFIC:
            break;
        default:
-           memcpy(gen_ie->ie_buffer + ie_len, hdr,
-                  hdr->len + sizeof(struct ieee_types_header));
-           ie_len += hdr->len + sizeof(struct ieee_types_header);
+           if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
+               err = -EINVAL;
+               goto out;
+           }
+           memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len);
+           ie_len += token_len;
            break;
        }
-       left_len -= hdr->len + sizeof(struct ieee_types_header);
-       parsed_len += hdr->len + sizeof(struct ieee_types_header);
+       left_len -= token_len;
+       parsed_len += token_len;
    }

    /* parse only WPA vendor IE from tail, WMM IE is configured by
@@ -372,15 +383,17 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
                            WLAN_OUI_TYPE_MICROSOFT_WPA,
                            info->tail, info->tail_len);
    if (vendorhdr) {
-       memcpy(gen_ie->ie_buffer + ie_len, vendorhdr,
-              vendorhdr->len + sizeof(struct ieee_types_header));
-       ie_len += vendorhdr->len + sizeof(struct ieee_types_header);
+       token_len = vendorhdr->len + sizeof(struct ieee_types_header);
+       if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
+           err = -EINVAL;
+           goto out;
+       }
+       memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
+       ie_len += token_len;
    }

-   if (!ie_len) {
-       kfree(gen_ie);
-       return 0;
-   }
+   if (!ie_len)
+       goto out;

    gen_ie->ie_index = cpu_to_le16(gen_idx);
    gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
@@ -390,13 +403,15 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,

    if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
                     NULL, NULL)) {
-       kfree(gen_ie);
-       return -1;
+       err = -EINVAL;
+       goto out;
    }

    priv->gen_idx = gen_idx;
+
+ out:
    kfree(gen_ie);
-   return 0;
+   return err;
 }

 /* This function parses different IEs-head & tail IEs, beacon IEs,

Leave a Reply

Your email address will not be published. Required fields are marked *