All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Yiran Qiu" <eritque-arcus@ikuyo.dev>
Cc: "Vincent Mailhol" <mailhol@kernel.org>,
	"Marc Kleine-Budde" <mkl@pengutronix.de>,
	"Oleksij Rempel" <o.rempel@pengutronix.de>,
	linux-can@vger.kernel.org,
	"Oliver Hartkopp" <socketcan@hartkopp.net>
Subject: Re: [PATCH 1/2] can: esd_usb: validate received message length before use
Date: Fri, 14 Aug 2026 18:16:16 +0000	[thread overview]
Message-ID: <20260814181617.4E6B61F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260815-can-esd-hydra-fixes-v1-1-de644cbeaec2@ikuyo.dev>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Unvalidated interface count from device causes heap buffer overflow during probe.
- [High] Dereferencing uninitialized network interfaces from USB messages causes kernel panic.
- [High] Spurious TX_DONE messages decrement inactive contexts causing active_tx_jobs underflow.
--

--- Patch [1]: [PATCH 1/2] can: esd_usb: validate received message length before use ---
commit 38efd7f38912ccfd78688896fcdf77da350528fd
Author: Yiran Qiu <eritque-arcus@ikuyo.dev>
Date:   Fri Aug 14 18:05:46 2026 +0000

    can: esd_usb: validate received message length before use
    
    esd_usb_read_bulk_callback() walks a sequence of variable-length
    messages out of the RX URB buffer. The only length check,
    "pos > urb->actual_length", runs *after* the current message has been
    dispatched and after @pos has been advanced, so it can neither protect
    the message being processed nor stop the loop:
    
    Validate the header and the declared message length before dispatch:
    reject a message whose header is not fully present, whose length is
    zero, or which extends past the received data, and advance @pos by the
    validated length. Pass the validated length to the message handlers so
    they can confirm that the fields they read (and the payload they copy)
    were actually received.

> diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c
> index f41d4a0d140f7..13356e68f3f60 100644
> --- a/drivers/net/can/usb/esd_usb.c
> +++ b/drivers/net/can/usb/esd_usb.c

[ ... ]

> @@ -455,7 +479,7 @@ static void esd_usb_rx_can_msg(struct esd_usb_net_priv *priv,
>  }
>  
>  static void esd_usb_tx_done_msg(struct esd_usb_net_priv *priv,
> -				union esd_usb_msg *msg)
> +				union esd_usb_msg *msg, unsigned int msg_len)
>  {
>  	struct net_device_stats *stats = &priv->netdev->stats;
>  	struct net_device *netdev = priv->netdev;
> @@ -464,6 +488,9 @@ static void esd_usb_tx_done_msg(struct esd_usb_net_priv *priv,
>  	if (!netif_device_present(netdev))
>  		return;
>  
> +	if (msg_len < offsetofend(struct esd_usb_tx_done_msg, hnd))
> +		return;
> +
>  	context = &priv->tx_contexts[msg->txdone.hnd & (ESD_USB_MAX_TX_URBS - 1)];
>  
>  	if (!msg->txdone.status) {

[Severity: High]
This is a pre-existing issue, but can spurious TX_DONE messages decrement
inactive contexts and cause the active_tx_jobs counter to underflow?

In esd_usb_tx_done_msg(), the context identified by msg->txdone.hnd is accessed
without checking if it is actually active (e.g. echo_index !=
ESD_USB_MAX_TX_URBS). If a malicious device sends spurious TX_DONE messages,
this will unconditionally execute atomic_dec(&priv->active_tx_jobs). 

When the queue eventually fills up with real packets, esd_usb_start_xmit()
won't stop the queue because active_tx_jobs is negative, returning
NETDEV_TX_BUSY. Returning NETDEV_TX_BUSY without stopping the queue causes the
network stack to infinitely retry, softlocking the CPU.

[ ... ]

> @@ -507,8 +534,27 @@ static void esd_usb_read_bulk_callback(struct urb *urb)
>  
>  	while (pos < urb->actual_length) {
>  		union esd_usb_msg *msg;
> +		unsigned int msg_len;
> +
> +		/* The header must be fully present before hdr.len / hdr.cmd
> +		 * (and the net index below) are read.
> +		 */
> +		if (pos + sizeof(struct esd_usb_header_msg) > urb->actual_length) {
> +			dev_err(dev->udev->dev.parent, "format error\n");
> +			break;
> +		}
>  
>  		msg = (union esd_usb_msg *)(urb->transfer_buffer + pos);
> +		msg_len = msg->hdr.len * sizeof(u32); /* convert to # of bytes */
> +
> +		/* A zero-length message would never advance @pos and would
> +		 * spin this URB-completion softirq forever; a message must
> +		 * also fit within the received data.
> +		 */
> +		if (msg->hdr.len == 0 || msg_len > urb->actual_length - pos) {
> +			dev_err(dev->udev->dev.parent, "format error\n");
> +			break;
> +		}
>  
>  		switch (msg->hdr.cmd) {
>  		case ESD_USB_CMD_CAN_RX:
> @@ -517,7 +563,7 @@ static void esd_usb_read_bulk_callback(struct urb *urb)
>  				break;
>  			}
>  
> -			esd_usb_rx_can_msg(dev->nets[msg->rx.net], msg);
> +			esd_usb_rx_can_msg(dev->nets[msg->rx.net], msg, msg_len);

[Severity: High]
This isn't a bug introduced by this patch, but does this loop properly handle
uninitialized network interfaces?

If esd_usb_probe_one_net() fails for one of the interfaces during
esd_usb_probe(), it leaves dev->nets[index] as NULL. A malicious USB device can
then send ESD_USB_CMD_CAN_RX messages with the index of the failed interface,
which is passed unconditionally to esd_usb_rx_can_msg() resulting in a NULL
pointer dereference.

[Severity: High]
This is also a pre-existing issue, but does msg->rx.net >= dev->net_count
properly protect against out-of-bounds array access for dev->nets[]?

A malicious USB device can return an arbitrarily large number of interfaces in
the version reply message. In esd_usb_probe(), this is directly assigned to
dev->net_count without validating it against ESD_USB_MAX_NETS (2), which can
corrupt adjacent heap memory when writing to dev->nets[index]. Furthermore,
since dev->net_count can be larger than ESD_USB_MAX_NETS, the check
msg->rx.net >= dev->net_count above does not prevent dev->nets[msg->rx.net]
from going out of bounds here.

>  			break;
>  
>  		case ESD_USB_CMD_CAN_TX:
> @@ -527,16 +573,11 @@ static void esd_usb_read_bulk_callback(struct urb *urb)
>  			}
>  
>  			esd_usb_tx_done_msg(dev->nets[msg->txdone.net],
> -					    msg);
> +					    msg, msg_len);
>  			break;
>  		}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260815-can-esd-hydra-fixes-v1-0-de644cbeaec2@ikuyo.dev?part=1

  reply	other threads:[~2026-08-14 18:16 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-14 18:05 [PATCH 0/2] can: fix two missed siblings of the kvaser_usb_leaf receive-walk fix Yiran Qiu
2026-08-14 18:05 ` [PATCH 1/2] can: esd_usb: validate received message length before use Yiran Qiu
2026-08-14 18:16   ` sashiko-bot [this message]
2026-08-14 18:05 ` [PATCH 2/2] can: kvaser_usb_hydra: reject too-short commands in the receive path Yiran Qiu
2026-08-14 18:21   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260814181617.4E6B61F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=eritque-arcus@ikuyo.dev \
    --cc=linux-can@vger.kernel.org \
    --cc=mailhol@kernel.org \
    --cc=mkl@pengutronix.de \
    --cc=o.rempel@pengutronix.de \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=socketcan@hartkopp.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.