can: kvaser_usb_leaf: Fix overread with an invalid command
[platform/kernel/linux-starfive.git] / drivers / net / can / usb / kvaser_usb / kvaser_usb_leaf.c
index 07f687f..8e11cda 100644 (file)
@@ -310,6 +310,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:
  *
@@ -397,6 +429,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 *cmd_len,
@@ -502,6 +571,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;
 }
 
@@ -1133,6 +1205,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);