Takashi Iwai 9e9ebe
From 1499ecaea9d2ba68d5e18d80573b4561a8dc4ee7 Mon Sep 17 00:00:00 2001
Takashi Iwai 9e9ebe
From: Anssi Hannula <anssi.hannula@bitwise.fi>
Takashi Iwai 9e9ebe
Date: Mon, 10 Oct 2022 17:08:26 +0200
Takashi Iwai 9e9ebe
Subject: [PATCH] can: kvaser_usb_leaf: Fix overread with an invalid command
Takashi Iwai 9e9ebe
Git-commit: 1499ecaea9d2ba68d5e18d80573b4561a8dc4ee7
Takashi Iwai 9e9ebe
Patch-mainline: v6.1-rc1
Takashi Iwai 9e9ebe
References: git-fixes
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
For command events read from the device,
Takashi Iwai 9e9ebe
kvaser_usb_leaf_read_bulk_callback() verifies that cmd->len does not
Takashi Iwai 9e9ebe
exceed the size of the received data, but the actual kvaser_cmd handlers
Takashi Iwai 9e9ebe
will happily read any kvaser_cmd fields without checking for cmd->len.
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
This can cause an overread if the last cmd in the buffer is shorter than
Takashi Iwai 9e9ebe
expected for the command type (with cmd->len showing the actual short
Takashi Iwai 9e9ebe
size).
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
Maximum overread seems to be 22 bytes (CMD_LEAF_LOG_MESSAGE), some of
Takashi Iwai 9e9ebe
which are delivered to userspace as-is.
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
Fix that by verifying the length of command before handling it.
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
This issue can only occur after RX URBs have been set up, i.e. the
Takashi Iwai 9e9ebe
interface has been opened at least once.
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
Cc: stable@vger.kernel.org
Takashi Iwai 9e9ebe
Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices")
Takashi Iwai 9e9ebe
Tested-by: Jimmy Assarsson <extja@kvaser.com>
Takashi Iwai 9e9ebe
Signed-off-by: Anssi Hannula <anssi.hannula@bitwise.fi>
Takashi Iwai 9e9ebe
Signed-off-by: Jimmy Assarsson <extja@kvaser.com>
Takashi Iwai 9e9ebe
Link: https://lore.kernel.org/all/20221010150829.199676-2-extja@kvaser.com
Takashi Iwai 9e9ebe
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Takashi Iwai 9e9ebe
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
---
Takashi Iwai 9e9ebe
 drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c |   75 +++++++++++++++++++++++
Takashi Iwai 9e9ebe
 1 file changed, 75 insertions(+)
Takashi Iwai 9e9ebe
Takashi Iwai 9e9ebe
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
Takashi Iwai 9e9ebe
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
Takashi Iwai 9e9ebe
@@ -310,6 +310,38 @@ struct kvaser_cmd {
Takashi Iwai 9e9ebe
 	} u;
Takashi Iwai 9e9ebe
 } __packed;
Takashi Iwai 9e9ebe
 
Takashi Iwai 9e9ebe
+#define CMD_SIZE_ANY 0xff
Takashi Iwai 9e9ebe
+#define kvaser_fsize(field) sizeof_field(struct kvaser_cmd, field)
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
+static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = {
Takashi Iwai 9e9ebe
+	[CMD_START_CHIP_REPLY]		= kvaser_fsize(u.simple),
Takashi Iwai 9e9ebe
+	[CMD_STOP_CHIP_REPLY]		= kvaser_fsize(u.simple),
Takashi Iwai 9e9ebe
+	[CMD_GET_CARD_INFO_REPLY]	= kvaser_fsize(u.cardinfo),
Takashi Iwai 9e9ebe
+	[CMD_TX_ACKNOWLEDGE]		= kvaser_fsize(u.tx_acknowledge_header),
Takashi Iwai 9e9ebe
+	[CMD_GET_SOFTWARE_INFO_REPLY]	= kvaser_fsize(u.leaf.softinfo),
Takashi Iwai 9e9ebe
+	[CMD_RX_STD_MESSAGE]		= kvaser_fsize(u.leaf.rx_can),
Takashi Iwai 9e9ebe
+	[CMD_RX_EXT_MESSAGE]		= kvaser_fsize(u.leaf.rx_can),
Takashi Iwai 9e9ebe
+	[CMD_LEAF_LOG_MESSAGE]		= kvaser_fsize(u.leaf.log_message),
Takashi Iwai 9e9ebe
+	[CMD_CHIP_STATE_EVENT]		= kvaser_fsize(u.leaf.chip_state_event),
Takashi Iwai 9e9ebe
+	[CMD_CAN_ERROR_EVENT]		= kvaser_fsize(u.leaf.error_event),
Takashi Iwai 9e9ebe
+	/* ignored events: */
Takashi Iwai 9e9ebe
+	[CMD_FLUSH_QUEUE_REPLY]		= CMD_SIZE_ANY,
Takashi Iwai 9e9ebe
+};
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
+static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = {
Takashi Iwai 9e9ebe
+	[CMD_START_CHIP_REPLY]		= kvaser_fsize(u.simple),
Takashi Iwai 9e9ebe
+	[CMD_STOP_CHIP_REPLY]		= kvaser_fsize(u.simple),
Takashi Iwai 9e9ebe
+	[CMD_GET_CARD_INFO_REPLY]	= kvaser_fsize(u.cardinfo),
Takashi Iwai 9e9ebe
+	[CMD_TX_ACKNOWLEDGE]		= kvaser_fsize(u.tx_acknowledge_header),
Takashi Iwai 9e9ebe
+	[CMD_GET_SOFTWARE_INFO_REPLY]	= kvaser_fsize(u.usbcan.softinfo),
Takashi Iwai 9e9ebe
+	[CMD_RX_STD_MESSAGE]		= kvaser_fsize(u.usbcan.rx_can),
Takashi Iwai 9e9ebe
+	[CMD_RX_EXT_MESSAGE]		= kvaser_fsize(u.usbcan.rx_can),
Takashi Iwai 9e9ebe
+	[CMD_CHIP_STATE_EVENT]		= kvaser_fsize(u.usbcan.chip_state_event),
Takashi Iwai 9e9ebe
+	[CMD_CAN_ERROR_EVENT]		= kvaser_fsize(u.usbcan.error_event),
Takashi Iwai 9e9ebe
+	/* ignored events: */
Takashi Iwai 9e9ebe
+	[CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = CMD_SIZE_ANY,
Takashi Iwai 9e9ebe
+};
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
 /* Summary of a kvaser error event, for a unified Leaf/Usbcan error
Takashi Iwai 9e9ebe
  * handling. Some discrepancies between the two families exist:
Takashi Iwai 9e9ebe
  *
Takashi Iwai 9e9ebe
@@ -397,6 +429,43 @@ static const struct kvaser_usb_dev_cfg k
Takashi Iwai 9e9ebe
 	.bittiming_const = &kvaser_usb_flexc_bittiming_const,
Takashi Iwai 9e9ebe
 };
Takashi Iwai 9e9ebe
 
Takashi Iwai 9e9ebe
+static int kvaser_usb_leaf_verify_size(const struct kvaser_usb *dev,
Takashi Iwai 9e9ebe
+				       const struct kvaser_cmd *cmd)
Takashi Iwai 9e9ebe
+{
Takashi Iwai 9e9ebe
+	/* buffer size >= cmd->len ensured by caller */
Takashi Iwai 9e9ebe
+	u8 min_size = 0;
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
+	switch (dev->driver_info->family) {
Takashi Iwai 9e9ebe
+	case KVASER_LEAF:
Takashi Iwai 9e9ebe
+		if (cmd->id < ARRAY_SIZE(kvaser_usb_leaf_cmd_sizes_leaf))
Takashi Iwai 9e9ebe
+			min_size = kvaser_usb_leaf_cmd_sizes_leaf[cmd->id];
Takashi Iwai 9e9ebe
+		break;
Takashi Iwai 9e9ebe
+	case KVASER_USBCAN:
Takashi Iwai 9e9ebe
+		if (cmd->id < ARRAY_SIZE(kvaser_usb_leaf_cmd_sizes_usbcan))
Takashi Iwai 9e9ebe
+			min_size = kvaser_usb_leaf_cmd_sizes_usbcan[cmd->id];
Takashi Iwai 9e9ebe
+		break;
Takashi Iwai 9e9ebe
+	}
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
+	if (min_size == CMD_SIZE_ANY)
Takashi Iwai 9e9ebe
+		return 0;
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
+	if (min_size) {
Takashi Iwai 9e9ebe
+		min_size += CMD_HEADER_LEN;
Takashi Iwai 9e9ebe
+		if (cmd->len >= min_size)
Takashi Iwai 9e9ebe
+			return 0;
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
+		dev_err_ratelimited(&dev->intf->dev,
Takashi Iwai 9e9ebe
+				    "Received command %u too short (size %u, needed %u)",
Takashi Iwai 9e9ebe
+				    cmd->id, cmd->len, min_size);
Takashi Iwai 9e9ebe
+		return -EIO;
Takashi Iwai 9e9ebe
+	}
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
+	dev_warn_ratelimited(&dev->intf->dev,
Takashi Iwai 9e9ebe
+			     "Unhandled command (%d, size %d)\n",
Takashi Iwai 9e9ebe
+			     cmd->id, cmd->len);
Takashi Iwai 9e9ebe
+	return -EINVAL;
Takashi Iwai 9e9ebe
+}
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
 static void *
Takashi Iwai 9e9ebe
 kvaser_usb_leaf_frame_to_cmd(const struct kvaser_usb_net_priv *priv,
Takashi Iwai 9e9ebe
 			     const struct sk_buff *skb, int *frame_len,
Takashi Iwai 9e9ebe
@@ -504,6 +573,9 @@ static int kvaser_usb_leaf_wait_cmd(cons
Takashi Iwai 9e9ebe
 end:
Takashi Iwai 9e9ebe
 	kfree(buf);
Takashi Iwai 9e9ebe
 
Takashi Iwai 9e9ebe
+	if (err == 0)
Takashi Iwai 9e9ebe
+		err = kvaser_usb_leaf_verify_size(dev, cmd);
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
 	return err;
Takashi Iwai 9e9ebe
 }
Takashi Iwai 9e9ebe
 
Takashi Iwai 9e9ebe
@@ -1135,6 +1207,9 @@ static void kvaser_usb_leaf_stop_chip_re
Takashi Iwai 9e9ebe
 static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev,
Takashi Iwai 9e9ebe
 					   const struct kvaser_cmd *cmd)
Takashi Iwai 9e9ebe
 {
Takashi Iwai 9e9ebe
+	if (kvaser_usb_leaf_verify_size(dev, cmd) < 0)
Takashi Iwai 9e9ebe
+		return;
Takashi Iwai 9e9ebe
+
Takashi Iwai 9e9ebe
 	switch (cmd->id) {
Takashi Iwai 9e9ebe
 	case CMD_START_CHIP_REPLY:
Takashi Iwai 9e9ebe
 		kvaser_usb_leaf_start_chip_reply(dev, cmd);