Commit 1dc1501c authored by Ahmed S. Darwish's avatar Ahmed S. Darwish Committed by Luis Henriques

can: kvaser_usb: Don't free packets when tight on URBs

commit b442723f upstream.

Flooding the Kvaser CAN to USB dongle with multiple reads and
writes in high frequency caused seemingly-random panics in the
kernel.

On further inspection, it seems the driver erroneously freed the
to-be-transmitted packet upon getting tight on URBs and returning
NETDEV_TX_BUSY, leading to invalid memory writes and double frees
at a later point in time.

Note:

Finding no more URBs/transmit-contexts and returning NETDEV_TX_BUSY
is a driver bug in and out of itself: it means that our start/stop
queue flow control is broken.

This patch only fixes the (buggy) error handling code; the root
cause shall be fixed in a later commit.
Acked-by: default avatarOlivier Sobrie <olivier@sobrie.be>
Signed-off-by: default avatarAhmed S. Darwish <ahmed.darwish@valeo.com>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 479cd5d6
...@@ -1294,12 +1294,14 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, ...@@ -1294,12 +1294,14 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
if (!urb) { if (!urb) {
netdev_err(netdev, "No memory left for URBs\n"); netdev_err(netdev, "No memory left for URBs\n");
stats->tx_dropped++; stats->tx_dropped++;
goto nourbmem; dev_kfree_skb(skb);
return NETDEV_TX_OK;
} }
buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC); buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
if (!buf) { if (!buf) {
stats->tx_dropped++; stats->tx_dropped++;
dev_kfree_skb(skb);
goto nobufmem; goto nobufmem;
} }
...@@ -1334,6 +1336,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, ...@@ -1334,6 +1336,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
} }
} }
/* This should never happen; it implies a flow control bug */
if (!context) { if (!context) {
netdev_warn(netdev, "cannot find free context\n"); netdev_warn(netdev, "cannot find free context\n");
ret = NETDEV_TX_BUSY; ret = NETDEV_TX_BUSY;
...@@ -1364,9 +1367,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, ...@@ -1364,9 +1367,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
if (unlikely(err)) { if (unlikely(err)) {
can_free_echo_skb(netdev, context->echo_index); can_free_echo_skb(netdev, context->echo_index);
skb = NULL; /* set to NULL to avoid double free in
* dev_kfree_skb(skb) */
atomic_dec(&priv->active_tx_urbs); atomic_dec(&priv->active_tx_urbs);
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
...@@ -1388,8 +1388,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, ...@@ -1388,8 +1388,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
kfree(buf); kfree(buf);
nobufmem: nobufmem:
usb_free_urb(urb); usb_free_urb(urb);
nourbmem:
dev_kfree_skb(skb);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment