mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Joas Antonio dos Santos via B4 Relay <devnull+joasantonio108.gmail.com@kernel.org>
To: mkl@pengutronix.de, mailhol@kernel.org, joasantonio108@gmail.com
Cc: linux-can@vger.kernel.org, linux-kernel@vger.kernel.org,
	 stable@vger.kernel.org
Subject: [PATCH v3 3/4] can: kvaser_usb: hydra: do not read the command length before it is received
Date: Thu, 12 Nov 2026 17:27:57 +0000	[thread overview]
Message-ID: <20261112-kvaser-usb-validate-v3-3-61587f639508@gmail.com> (raw)
In-Reply-To: <20261112-kvaser-usb-validate-v3-0-61587f639508@gmail.com>

From: Joas Antonio dos Santos <joasantonio108@gmail.com>

kvaser_usb_hydra_cmd_size() reads the 16 bit length of an extended
command from bytes 4 and 5 of the command.  The two command parsers call
it without checking that those bytes have been received:

  - kvaser_usb_hydra_wait_cmd() only requires pos < actual_len.  If the
    transfer fills the whole KVASER_USB_RX_BUFFER_SIZE buffer and the
    last command starts in its last five bytes, the length is read from
    beyond the end of the kzalloc()ed buffer.

  - kvaser_usb_hydra_read_bulk_callback() has the same problem at the
    end of the receive buffer.  In addition, an extended command that is
    split so that fewer than six bytes arrive in the first transfer is
    parsed with a length taken from stale buffer content.  That either
    fails the length check or stores the fragment in usb_rx_leftover,
    where the next callback computes the length again before the rest of
    the header has been copied in, and without checking it against
    sizeof(struct kvaser_cmd).

Every command is at least sizeof(struct kvaser_cmd) long and
kvaser_usb_hydra_wait_cmd() does not handle partial commands, so require
that many bytes there before looking at the command.

In kvaser_usb_hydra_read_bulk_callback(), buffer the fragment when the
length field is not complete, and complete the length field in
usb_rx_leftover before using it.  Apply the minimum length check to the
buffered command too.  Drop the buffered fragment when that check or
the existing check against the size of usb_rx_leftover fails; the
latter kept the fragment, so that the same error was hit again on the
following transfers.

Found by the Sashiko AI review of the previous version of this series.

Fixes: aec5fb2268b7 ("can: kvaser_usb: Add support for Kvaser USB hydra family")
Cc: stable@vger.kernel.org
Signed-off-by: Joas Antonio dos Santos <joasantonio108@gmail.com>
---
 drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 50 +++++++++++++++++++----
 1 file changed, 43 insertions(+), 7 deletions(-)

diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
index 9adb4eb36e39..6b68e0714ae3 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
@@ -675,6 +675,12 @@ static int kvaser_usb_hydra_wait_cmd(const struct kvaser_usb *dev, u8 cmd_no,
 			size_t cmd_len;
 
 			tmp_cmd = buf + pos;
+			if (actual_len - pos < sizeof(struct kvaser_cmd)) {
+				dev_err_ratelimited(&dev->intf->dev,
+						    "Format error\n");
+				break;
+			}
+
 			cmd_len = kvaser_usb_hydra_cmd_size(tmp_cmd);
 			if (cmd_len < sizeof(struct kvaser_cmd) ||
 			    pos + cmd_len > actual_len) {
@@ -2117,6 +2123,8 @@ static void kvaser_usb_hydra_read_bulk_callback(struct kvaser_usb *dev,
 							&dev->card_data.hydra;
 	int usb_rx_leftover_len;
 	spinlock_t *usb_rx_leftover_lock = &card_data->usb_rx_leftover_lock;
+	/* Number of bytes needed to tell the length of any command */
+	const int len_end = offsetofend(struct kvaser_cmd_ext, len);
 
 	spin_lock_irqsave(usb_rx_leftover_lock, irq_flags);
 	usb_rx_leftover_len = card_data->usb_rx_leftover_len;
@@ -2125,20 +2133,43 @@ static void kvaser_usb_hydra_read_bulk_callback(struct kvaser_usb *dev,
 
 		cmd = (struct kvaser_cmd *)card_data->usb_rx_leftover;
 
+		if (usb_rx_leftover_len < len_end) {
+			/* Get the rest of the length field first */
+			remaining_bytes = min(len,
+					      len_end - usb_rx_leftover_len);
+			memcpy(card_data->usb_rx_leftover + usb_rx_leftover_len,
+			       buf, remaining_bytes);
+			pos += remaining_bytes;
+			usb_rx_leftover_len += remaining_bytes;
+			card_data->usb_rx_leftover_len = usb_rx_leftover_len;
+			if (usb_rx_leftover_len < len_end) {
+				spin_unlock_irqrestore(usb_rx_leftover_lock,
+						       irq_flags);
+				return;
+			}
+		}
+
 		cmd_len = kvaser_usb_hydra_cmd_size(cmd);
+		if (cmd_len < sizeof(struct kvaser_cmd)) {
+			dev_err(&dev->intf->dev, "Format error\n");
+			card_data->usb_rx_leftover_len = 0;
+			spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags);
+			return;
+		}
 
-		remaining_bytes = min_t(unsigned int, len,
+		remaining_bytes = min_t(unsigned int, len - pos,
 					cmd_len - usb_rx_leftover_len);
 		/* Make sure we do not overflow usb_rx_leftover */
 		if (remaining_bytes + usb_rx_leftover_len >
 						KVASER_USB_HYDRA_MAX_CMD_LEN) {
 			dev_err(&dev->intf->dev, "Format error\n");
+			card_data->usb_rx_leftover_len = 0;
 			spin_unlock_irqrestore(usb_rx_leftover_lock, irq_flags);
 			return;
 		}
 
-		memcpy(card_data->usb_rx_leftover + usb_rx_leftover_len, buf,
-		       remaining_bytes);
+		memcpy(card_data->usb_rx_leftover + usb_rx_leftover_len,
+		       buf + pos, remaining_bytes);
 		pos += remaining_bytes;
 
 		if (remaining_bytes + usb_rx_leftover_len == cmd_len) {
@@ -2155,11 +2186,16 @@ static void kvaser_usb_hydra_read_bulk_callback(struct kvaser_usb *dev,
 	while (pos < len) {
 		cmd = buf + pos;
 
-		cmd_len = kvaser_usb_hydra_cmd_size(cmd);
+		if (len - pos < len_end) {
+			/* The length is not known yet, buffer what we got */
+			cmd_len = sizeof(struct kvaser_cmd);
+		} else {
+			cmd_len = kvaser_usb_hydra_cmd_size(cmd);
 
-		if (cmd_len < sizeof(struct kvaser_cmd)) {
-			dev_err(&dev->intf->dev, "Format error\n");
-			return;
+			if (cmd_len < sizeof(struct kvaser_cmd)) {
+				dev_err(&dev->intf->dev, "Format error\n");
+				return;
+			}
 		}
 
 		if (pos + cmd_len > len) {

-- 
2.43.0



  parent reply	other threads:[~2026-11-12 17:27 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 17:27 [PATCH v3 0/4] can: kvaser_usb: validate values received from the device Joas Antonio dos Santos via B4 Relay
2026-11-12 17:27 ` [PATCH v3 1/4] can: kvaser_usb: hydra: reject commands shorter than a standard command Joas Antonio dos Santos via B4 Relay
2026-11-12 17:27 ` [PATCH v3 2/4] can: kvaser_usb: reject devices reporting zero max outstanding tx Joas Antonio dos Santos via B4 Relay
2026-11-12 17:27 ` Joas Antonio dos Santos via B4 Relay [this message]
2026-11-12 17:27 ` [PATCH v3 4/4] can: kvaser_usb: hydra: check the payload of extended rx commands Joas Antonio dos Santos via B4 Relay

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=20261112-kvaser-usb-validate-v3-3-61587f639508@gmail.com \
    --to=devnull+joasantonio108.gmail.com@kernel.org \
    --cc=joasantonio108@gmail.com \
    --cc=linux-can@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mailhol@kernel.org \
    --cc=mkl@pengutronix.de \
    --cc=stable@vger.kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®