can: kvaser_usb_leaf: Fix overread with an invalid command
authorAnssi Hannula <anssi.hannula@bitwise.fi>
Mon, 10 Oct 2022 15:08:26 +0000 (17:08 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Oct 2022 10:34:20 +0000 (12:34 +0200)
commit 1499ecaea9d2ba68d5e18d80573b4561a8dc4ee7 upstream.

For command events read from the device,
kvaser_usb_leaf_read_bulk_callback() verifies that cmd->len does not
exceed the size of the received data, but the actual kvaser_cmd handlers
will happily read any kvaser_cmd fields without checking for cmd->len.

This can cause an overread if the last cmd in the buffer is shorter than
expected for the command type (with cmd->len showing the actual short
size).

Maximum overread seems to be 22 bytes (CMD_LEAF_LOG_MESSAGE), some of
which are delivered to userspace as-is.

Fix that by verifying the length of command before handling it.

This issue can only occur after RX URBs have been set up, i.e. the
interface has been opened at least once.

Cc: stable@vger.kernel.org
Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices")
Tested-by: Jimmy Assarsson <extja@kvaser.com>
Signed-off-by: Anssi Hannula <anssi.hannula@bitwise.fi>
Signed-off-by: Jimmy Assarsson <extja@kvaser.com>
Link: https://lore.kernel.org/all/20221010150829.199676-2-extja@kvaser.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c

index 05d54c4..b065f24 100644 (file)
@@ -309,6 +309,38 @@ struct kvaser_cmd {
        } u;
 } __packed;
 
+#define CMD_SIZE_ANY 0xff
+#define kvaser_fsize(field) sizeof_field(struct kvaser_cmd, field)
+
+static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = {
+       [CMD_START_CHIP_REPLY]          = kvaser_fsize(u.simple),
+       [CMD_STOP_CHIP_REPLY]           = kvaser_fsize(u.simple),
+       [CMD_GET_CARD_INFO_REPLY]       = kvaser_fsize(u.cardinfo),
+       [CMD_TX_ACKNOWLEDGE]            = kvaser_fsize(u.tx_acknowledge_header),
+       [CMD_GET_SOFTWARE_INFO_REPLY]   = kvaser_fsize(u.leaf.softinfo),
+       [CMD_RX_STD_MESSAGE]            = kvaser_fsize(u.leaf.rx_can),
+       [CMD_RX_EXT_MESSAGE]            = kvaser_fsize(u.leaf.rx_can),
+       [CMD_LEAF_LOG_MESSAGE]          = kvaser_fsize(u.leaf.log_message),
+       [CMD_CHIP_STATE_EVENT]          = kvaser_fsize(u.leaf.chip_state_event),
+       [CMD_CAN_ERROR_EVENT]           = kvaser_fsize(u.leaf.error_event),
+       /* ignored events: */
+       [CMD_FLUSH_QUEUE_REPLY]         = CMD_SIZE_ANY,
+};
+
+static const u8 kvaser_usb_leaf_cmd_sizes_usbcan[] = {
+       [CMD_START_CHIP_REPLY]          = kvaser_fsize(u.simple),
+       [CMD_STOP_CHIP_REPLY]           = kvaser_fsize(u.simple),
+       [CMD_GET_CARD_INFO_REPLY]       = kvaser_fsize(u.cardinfo),
+       [CMD_TX_ACKNOWLEDGE]            = kvaser_fsize(u.tx_acknowledge_header),
+       [CMD_GET_SOFTWARE_INFO_REPLY]   = kvaser_fsize(u.usbcan.softinfo),
+       [CMD_RX_STD_MESSAGE]            = kvaser_fsize(u.usbcan.rx_can),
+       [CMD_RX_EXT_MESSAGE]            = kvaser_fsize(u.usbcan.rx_can),
+       [CMD_CHIP_STATE_EVENT]          = kvaser_fsize(u.usbcan.chip_state_event),
+       [CMD_CAN_ERROR_EVENT]           = kvaser_fsize(u.usbcan.error_event),
+       /* ignored events: */
+       [CMD_USBCAN_CLOCK_OVERFLOW_EVENT] = CMD_SIZE_ANY,
+};
+
 /* Summary of a kvaser error event, for a unified Leaf/Usbcan error
  * handling. Some discrepancies between the two families exist:
  *
@@ -396,6 +428,43 @@ static const struct kvaser_usb_dev_cfg kvaser_usb_leaf_imx_dev_cfg_32mhz = {
        .bittiming_const = &kvaser_usb_flexc_bittiming_const,
 };
 
+static int kvaser_usb_leaf_verify_size(const struct kvaser_usb *dev,
+                                      const struct kvaser_cmd *cmd)
+{
+       /* buffer size >= cmd->len ensured by caller */
+       u8 min_size = 0;
+
+       switch (dev->driver_info->family) {
+       case KVASER_LEAF:
+               if (cmd->id < ARRAY_SIZE(kvaser_usb_leaf_cmd_sizes_leaf))
+                       min_size = kvaser_usb_leaf_cmd_sizes_leaf[cmd->id];
+               break;
+       case KVASER_USBCAN:
+               if (cmd->id < ARRAY_SIZE(kvaser_usb_leaf_cmd_sizes_usbcan))
+                       min_size = kvaser_usb_leaf_cmd_sizes_usbcan[cmd->id];
+               break;
+       }
+
+       if (min_size == CMD_SIZE_ANY)
+               return 0;
+
+       if (min_size) {
+               min_size += CMD_HEADER_LEN;
+               if (cmd->len >= min_size)
+                       return 0;
+
+               dev_err_ratelimited(&dev->intf->dev,
+                                   "Received command %u too short (size %u, needed %u)",
+                                   cmd->id, cmd->len, min_size);
+               return -EIO;
+       }
+
+       dev_warn_ratelimited(&dev->intf->dev,
+                            "Unhandled command (%d, size %d)\n",
+                            cmd->id, cmd->len);
+       return -EINVAL;
+}
+
 static void *
 kvaser_usb_leaf_frame_to_cmd(const struct kvaser_usb_net_priv *priv,
                             const struct sk_buff *skb, int *frame_len,
@@ -503,6 +572,9 @@ static int kvaser_usb_leaf_wait_cmd(const struct kvaser_usb *dev, u8 id,
 end:
        kfree(buf);
 
+       if (err == 0)
+               err = kvaser_usb_leaf_verify_size(dev, cmd);
+
        return err;
 }
 
@@ -1137,6 +1209,9 @@ static void kvaser_usb_leaf_stop_chip_reply(const struct kvaser_usb *dev,
 static void kvaser_usb_leaf_handle_command(const struct kvaser_usb *dev,
                                           const struct kvaser_cmd *cmd)
 {
+       if (kvaser_usb_leaf_verify_size(dev, cmd) < 0)
+               return;
+
        switch (cmd->id) {
        case CMD_START_CHIP_REPLY:
                kvaser_usb_leaf_start_chip_reply(dev, cmd);