Vulnerability: usb_8dev_start_xmit in drivers/net/can/usb/usb_8dev.c in the Linux kernel through 5.17.1 has a double free.
Vulnerability ID: CVE-2022-28388
CWE: CWE-415
Severity score: 6.4
Project: https://github.com/torvalds/linux
Published: 2022-04-03
File: drivers/net/can/usb/usb_8dev.c
Commit message: can: usb_8dev: usb_8dev_start_xmit(): fix double dev_kfree_skb() in error path

There is no need to call dev_kfree_skb() when usb_submit_urb() fails
because can_put_echo_skb() deletes original skb and
can_free_echo_skb() deletes the cloned skb.

Fixes: 0024d8ad1639 ("can: usb_8dev: Add support for USB2CAN interface from 8 devices")
Link: https://lore.kernel.org/all/20220311080614.45229-1-hbh25y@gmail.com
Cc: stable@vger.kernel.org
Signed-off-by: Hangyu Hua <hbh25y@gmail.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Commit author: Hangyu Hua

Vulnerable version (before fix):
 static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb, 	atomic_inc(&priv->active_tx_urbs);  	err = usb_submit_urb(urb, GFP_ATOMIC); 	if (unlikely(err)) 		goto failed; 	else if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS) 		/* Slow down tx path */ 		netif_stop_queue(netdev);  static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb,  	return NETDEV_TX_BUSY;  failed: 	can_free_echo_skb(netdev, context->echo_index, NULL);  	usb_unanchor_urb(urb); 	usb_free_coherent(priv->udev, size, buf, urb->transfer_dma);  	atomic_dec(&priv->active_tx_urbs);  	if (err == -ENODEV) 		netif_device_detach(netdev); 	else 		netdev_warn(netdev, "failed tx_urb %d\n", err);  nomembuf: 	usb_free_urb(urb); 

Fixed version (after fix):
 static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb, 	atomic_inc(&priv->active_tx_urbs);  	err = usb_submit_urb(urb, GFP_ATOMIC); 	if (unlikely(err)) { 		can_free_echo_skb(netdev, context->echo_index, NULL);  		usb_unanchor_urb(urb); 		usb_free_coherent(priv->udev, size, buf, urb->transfer_dma);  		atomic_dec(&priv->active_tx_urbs);  		if (err == -ENODEV) 			netif_device_detach(netdev); 		else 			netdev_warn(netdev, "failed tx_urb %d\n", err); 		stats->tx_dropped++; 	} else if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS) 		/* Slow down tx path */ 		netif_stop_queue(netdev);  static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb,  	return NETDEV_TX_BUSY;  nomembuf: 	usb_free_urb(urb); 

Patch:
patch: @@ -663,9 +663,20 @@ static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb,
 	atomic_inc(&priv->active_tx_urbs);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (unlikely(err))
-		goto failed;
-	else if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+	if (unlikely(err)) {
+		can_free_echo_skb(netdev, context->echo_index, NULL);
+
+		usb_unanchor_urb(urb);
+		usb_free_coherent(priv->udev, size, buf, urb->transfer_dma);
+
+		atomic_dec(&priv->active_tx_urbs);
+
+		if (err == -ENODEV)
+			netif_device_detach(netdev);
+		else
+			netdev_warn(netdev, "failed tx_urb %d\n", err);
+		stats->tx_dropped++;
+	} else if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
 		/* Slow down tx path */
 		netif_stop_queue(netdev);
 
@@ -684,19 +695,6 @@ static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb,
 
 	return NETDEV_TX_BUSY;
 
-failed:
-	can_free_echo_skb(netdev, context->echo_index, NULL);
-
-	usb_unanchor_urb(urb);
-	usb_free_coherent(priv->udev, size, buf, urb->transfer_dma);
-
-	atomic_dec(&priv->active_tx_urbs);
-
-	if (err == -ENODEV)
-		netif_device_detach(netdev);
-	else
-		netdev_warn(netdev, "failed tx_urb %d\n", err);
-
 nomembuf:
 	usb_free_urb(urb);
path: drivers/net/can/usb/usb_8dev.c