staging: r8712: Fix memory leak in _r8712_init_xmit_priv()
authorLarry Finger <Larry.Finger@lwfinger.net>
Fri, 14 Jul 2023 17:54:17 +0000 (12:54 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 Jul 2023 07:50:57 +0000 (09:50 +0200)
In the above mentioned routine, memory is allocated in several places.
If the first succeeds and a later one fails, the routine will leak memory.
This patch fixes commit 2865d42c78a9 ("staging: r8712u: Add the new driver
to the mainline kernel"). A potential memory leak in
r8712_xmit_resource_alloc() is also addressed.

Fixes: 2865d42c78a9 ("staging: r8712u: Add the new driver to the mainline kernel")
Reported-by: syzbot+cf71097ffb6755df8251@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/x/log.txt?x=11ac3fa0a80000
Cc: stable@vger.kernel.org
Cc: Nam Cao <namcaov@gmail.com>
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Reviewed-by: Nam Cao <namcaov@gmail.com>
Link: https://lore.kernel.org/r/20230714175417.18578-1-Larry.Finger@lwfinger.net
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/rtl8712/rtl871x_xmit.c
drivers/staging/rtl8712/xmit_linux.c

index 090345b..6353dbe 100644 (file)
@@ -21,6 +21,7 @@
 #include "osdep_intf.h"
 #include "usb_ops.h"
 
+#include <linux/usb.h>
 #include <linux/ieee80211.h>
 
 static const u8 P802_1H_OUI[P80211_OUI_LEN] = {0x00, 0x00, 0xf8};
@@ -55,6 +56,7 @@ int _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv,
        sint i;
        struct xmit_buf *pxmitbuf;
        struct xmit_frame *pxframe;
+       int j;
 
        memset((unsigned char *)pxmitpriv, 0, sizeof(struct xmit_priv));
        spin_lock_init(&pxmitpriv->lock);
@@ -117,11 +119,8 @@ int _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv,
        _init_queue(&pxmitpriv->pending_xmitbuf_queue);
        pxmitpriv->pallocated_xmitbuf =
                kmalloc(NR_XMITBUFF * sizeof(struct xmit_buf) + 4, GFP_ATOMIC);
-       if (!pxmitpriv->pallocated_xmitbuf) {
-               kfree(pxmitpriv->pallocated_frame_buf);
-               pxmitpriv->pallocated_frame_buf = NULL;
-               return -ENOMEM;
-       }
+       if (!pxmitpriv->pallocated_xmitbuf)
+               goto clean_up_frame_buf;
        pxmitpriv->pxmitbuf = pxmitpriv->pallocated_xmitbuf + 4 -
                              ((addr_t)(pxmitpriv->pallocated_xmitbuf) & 3);
        pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf;
@@ -129,13 +128,17 @@ int _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv,
                INIT_LIST_HEAD(&pxmitbuf->list);
                pxmitbuf->pallocated_buf =
                        kmalloc(MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ, GFP_ATOMIC);
-               if (!pxmitbuf->pallocated_buf)
-                       return -ENOMEM;
+               if (!pxmitbuf->pallocated_buf) {
+                       j = 0;
+                       goto clean_up_alloc_buf;
+               }
                pxmitbuf->pbuf = pxmitbuf->pallocated_buf + XMITBUF_ALIGN_SZ -
                                 ((addr_t) (pxmitbuf->pallocated_buf) &
                                 (XMITBUF_ALIGN_SZ - 1));
-               if (r8712_xmit_resource_alloc(padapter, pxmitbuf))
-                       return -ENOMEM;
+               if (r8712_xmit_resource_alloc(padapter, pxmitbuf)) {
+                       j = 1;
+                       goto clean_up_alloc_buf;
+               }
                list_add_tail(&pxmitbuf->list,
                                 &(pxmitpriv->free_xmitbuf_queue.queue));
                pxmitbuf++;
@@ -146,6 +149,28 @@ int _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv,
        init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
        tasklet_setup(&pxmitpriv->xmit_tasklet, r8712_xmit_bh);
        return 0;
+
+clean_up_alloc_buf:
+       if (j) {
+               /* failure happened in r8712_xmit_resource_alloc()
+                * delete extra pxmitbuf->pallocated_buf
+                */
+               kfree(pxmitbuf->pallocated_buf);
+       }
+       for (j = 0; j < i; j++) {
+               int k;
+
+               pxmitbuf--;                     /* reset pointer */
+               kfree(pxmitbuf->pallocated_buf);
+               for (k = 0; k < 8; k++)         /* delete xmit urb's */
+                       usb_free_urb(pxmitbuf->pxmit_urb[k]);
+       }
+       kfree(pxmitpriv->pallocated_xmitbuf);
+       pxmitpriv->pallocated_xmitbuf = NULL;
+clean_up_frame_buf:
+       kfree(pxmitpriv->pallocated_frame_buf);
+       pxmitpriv->pallocated_frame_buf = NULL;
+       return -ENOMEM;
 }
 
 void _free_xmit_priv(struct xmit_priv *pxmitpriv)
index 132afbf..ceb6b59 100644 (file)
@@ -112,6 +112,12 @@ int r8712_xmit_resource_alloc(struct _adapter *padapter,
        for (i = 0; i < 8; i++) {
                pxmitbuf->pxmit_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
                if (!pxmitbuf->pxmit_urb[i]) {
+                       int k;
+
+                       for (k = i - 1; k >= 0; k--) {
+                               /* handle allocation errors part way through loop */
+                               usb_free_urb(pxmitbuf->pxmit_urb[k]);
+                       }
                        netdev_err(padapter->pnetdev, "pxmitbuf->pxmit_urb[i] == NULL\n");
                        return -ENOMEM;
                }