can: dev: fix skb drop check
authorOliver Hartkopp <socketcan@hartkopp.net>
Wed, 2 Nov 2022 09:54:31 +0000 (10:54 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Mon, 7 Nov 2022 13:00:27 +0000 (14:00 +0100)
In commit a6d190f8c767 ("can: skb: drop tx skb if in listen only
mode") the priv->ctrlmode element is read even on virtual CAN
interfaces that do not create the struct can_priv at startup. This
out-of-bounds read may lead to CAN frame drops for virtual CAN
interfaces like vcan and vxcan.

This patch mainly reverts the original commit and adds a new helper
for CAN interface drivers that provide the required information in
struct can_priv.

Fixes: a6d190f8c767 ("can: skb: drop tx skb if in listen only mode")
Reported-by: Dariusz Stojaczyk <Dariusz.Stojaczyk@opensynergy.com>
Cc: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Cc: Max Staudt <max@enpas.org>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Acked-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Link: https://lore.kernel.org/all/20221102095431.36831-1-socketcan@hartkopp.net
Cc: stable@vger.kernel.org # 6.0.x
[mkl: patch pch_can, too]
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
36 files changed:
drivers/net/can/at91_can.c
drivers/net/can/c_can/c_can_main.c
drivers/net/can/can327.c
drivers/net/can/cc770/cc770.c
drivers/net/can/ctucanfd/ctucanfd_base.c
drivers/net/can/dev/skb.c
drivers/net/can/flexcan/flexcan-core.c
drivers/net/can/grcan.c
drivers/net/can/ifi_canfd/ifi_canfd.c
drivers/net/can/janz-ican3.c
drivers/net/can/kvaser_pciefd.c
drivers/net/can/m_can/m_can.c
drivers/net/can/mscan/mscan.c
drivers/net/can/pch_can.c
drivers/net/can/peak_canfd/peak_canfd.c
drivers/net/can/rcar/rcar_can.c
drivers/net/can/rcar/rcar_canfd.c
drivers/net/can/sja1000/sja1000.c
drivers/net/can/slcan/slcan-core.c
drivers/net/can/softing/softing_main.c
drivers/net/can/spi/hi311x.c
drivers/net/can/spi/mcp251x.c
drivers/net/can/spi/mcp251xfd/mcp251xfd-tx.c
drivers/net/can/sun4i_can.c
drivers/net/can/ti_hecc.c
drivers/net/can/usb/ems_usb.c
drivers/net/can/usb/esd_usb.c
drivers/net/can/usb/etas_es58x/es58x_core.c
drivers/net/can/usb/gs_usb.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
drivers/net/can/usb/mcba_usb.c
drivers/net/can/usb/peak_usb/pcan_usb_core.c
drivers/net/can/usb/ucan.c
drivers/net/can/usb/usb_8dev.c
drivers/net/can/xilinx_can.c
include/linux/can/dev.h

index 3a2d109..199cb20 100644 (file)
@@ -452,7 +452,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int mb, prio;
        u32 reg_mid, reg_mcr;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        mb = get_tx_next_mb(priv);
index d6605db..c63f7fc 100644 (file)
@@ -457,7 +457,7 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
        struct c_can_tx_ring *tx_ring = &priv->tx;
        u32 idx, obj, cmd = IF_COMM_TX;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        if (c_can_tx_busy(priv, tx_ring))
index 0aa1af3..0941977 100644 (file)
@@ -813,7 +813,7 @@ static netdev_tx_t can327_netdev_start_xmit(struct sk_buff *skb,
        struct can327 *elm = netdev_priv(dev);
        struct can_frame *frame = (struct can_frame *)skb->data;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        /* We shouldn't get here after a hardware fault:
index 0b9dfc7..30909f3 100644 (file)
@@ -429,7 +429,7 @@ static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct cc770_priv *priv = netdev_priv(dev);
        unsigned int mo = obj2msgobj(CC770_OBJ_TX);
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        netif_stop_queue(dev);
index b8da15e..64c349f 100644 (file)
@@ -600,7 +600,7 @@ static netdev_tx_t ctucan_start_xmit(struct sk_buff *skb, struct net_device *nde
        bool ok;
        unsigned long flags;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        if (unlikely(!CTU_CAN_FD_TXTNF(priv))) {
index 791a51e..241ec63 100644 (file)
@@ -5,7 +5,6 @@
  */
 
 #include <linux/can/dev.h>
-#include <linux/can/netlink.h>
 #include <linux/module.h>
 
 #define MOD_DESC "CAN device driver interface"
@@ -337,8 +336,6 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
 /* Drop a given socketbuffer if it does not contain a valid CAN frame. */
 bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
 {
-       struct can_priv *priv = netdev_priv(dev);
-
        switch (ntohs(skb->protocol)) {
        case ETH_P_CAN:
                if (!can_is_can_skb(skb))
@@ -359,13 +356,8 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
                goto inval_skb;
        }
 
-       if (!can_skb_headroom_valid(dev, skb)) {
+       if (!can_skb_headroom_valid(dev, skb))
                goto inval_skb;
-       } else if (priv->ctrlmode & CAN_CTRLMODE_LISTENONLY) {
-               netdev_info_once(dev,
-                                "interface in listen only mode, dropping skb\n");
-               goto inval_skb;
-       }
 
        return false;
 
index 5ee38e5..9bdadd7 100644 (file)
@@ -742,7 +742,7 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
        u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | ((can_fd_len2dlc(cfd->len)) << 16);
        int i;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        netif_stop_queue(dev);
index 6c37aab..4bedcc3 100644 (file)
@@ -1345,7 +1345,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
        unsigned long flags;
        u32 oneshotmode = priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        /* Trying to transmit in silent mode will generate error interrupts, but
index 8d42b7e..07eaf72 100644 (file)
@@ -860,7 +860,7 @@ static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb,
        u32 txst, txid, txdlc;
        int i;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        /* Check if the TX buffer is full */
index 71a2caa..0732a50 100644 (file)
@@ -1693,7 +1693,7 @@ static netdev_tx_t ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
        void __iomem *desc_addr;
        unsigned long flags;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        spin_lock_irqsave(&mod->lock, flags);
index 4e9680c..bcad117 100644 (file)
@@ -772,7 +772,7 @@ static netdev_tx_t kvaser_pciefd_start_xmit(struct sk_buff *skb,
        int nwords;
        u8 count;
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        nwords = kvaser_pciefd_prepare_tx_packet(&packet, can, skb);
index dcb5825..00d11e9 100644 (file)
@@ -1721,7 +1721,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
 {
        struct m_can_classdev *cdev = netdev_priv(dev);
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        if (cdev->is_peripheral) {
index 2119fbb..a6829cd 100644 (file)
@@ -191,7 +191,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int i, rtr, buf_id;
        u32 can_id;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        out_8(&regs->cantier, 0);
index 0558ff6..2a44b28 100644 (file)
@@ -882,7 +882,7 @@ static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev)
        int i;
        u32 id2;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        tx_obj_no = priv->tx_obj;
index f8420cc..31c9c12 100644 (file)
@@ -651,7 +651,7 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
        int room_left;
        u8 len;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        msg_size = ALIGN(sizeof(*msg) + cf->len, 4);
index 6ee968c..cc43c9c 100644 (file)
@@ -590,7 +590,7 @@ static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb,
        struct can_frame *cf = (struct can_frame *)skb->data;
        u32 data, i;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        if (cf->can_id & CAN_EFF_FLAG)  /* Extended frame format */
index 198da64..d530e98 100644 (file)
@@ -1481,7 +1481,7 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb,
        unsigned long flags;
        u32 ch = priv->channel;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        if (cf->can_id & CAN_EFF_FLAG) {
index 1bb1129..aac5956 100644 (file)
@@ -291,7 +291,7 @@ static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb,
        u8 cmd_reg_val = 0x00;
        int i;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        netif_stop_queue(dev);
index 8d13fdf..fbb3413 100644 (file)
@@ -594,7 +594,7 @@ static netdev_tx_t slcan_netdev_xmit(struct sk_buff *skb,
 {
        struct slcan *sl = netdev_priv(dev);
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        spin_lock(&sl->lock);
index a5ef57f..c72f505 100644 (file)
@@ -60,7 +60,7 @@ static netdev_tx_t softing_netdev_start_xmit(struct sk_buff *skb,
        struct can_frame *cf = (struct can_frame *)skb->data;
        uint8_t buf[DPRAM_TX_SIZE];
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        spin_lock(&card->spin);
index b87dc42..e1b8533 100644 (file)
@@ -373,7 +373,7 @@ static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb,
                return NETDEV_TX_BUSY;
        }
 
-       if (can_dropped_invalid_skb(net, skb))
+       if (can_dev_dropped_skb(net, skb))
                return NETDEV_TX_OK;
 
        netif_stop_queue(net);
index 24883a6..79c4bab 100644 (file)
@@ -789,7 +789,7 @@ static netdev_tx_t mcp251x_hard_start_xmit(struct sk_buff *skb,
                return NETDEV_TX_BUSY;
        }
 
-       if (can_dropped_invalid_skb(net, skb))
+       if (can_dev_dropped_skb(net, skb))
                return NETDEV_TX_OK;
 
        netif_stop_queue(net);
index ffb6c36..160528d 100644 (file)
@@ -172,7 +172,7 @@ netdev_tx_t mcp251xfd_start_xmit(struct sk_buff *skb,
        u8 tx_head;
        int err;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        if (mcp251xfd_tx_busy(priv, tx_ring))
index 525309d..2b78f91 100644 (file)
@@ -429,7 +429,7 @@ static netdev_tx_t sun4ican_start_xmit(struct sk_buff *skb, struct net_device *d
        canid_t id;
        int i;
 
-       if (can_dropped_invalid_skb(dev, skb))
+       if (can_dev_dropped_skb(dev, skb))
                return NETDEV_TX_OK;
 
        netif_stop_queue(dev);
index b218fb3..27700f7 100644 (file)
@@ -470,7 +470,7 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev)
        u32 mbxno, mbx_mask, data;
        unsigned long flags;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        mbxno = get_tx_head_mb(priv);
index d311916..050c0b4 100644 (file)
@@ -747,7 +747,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne
        size_t size = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN
                        + sizeof(struct cpc_can_msg);
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        /* create a URB, and a buffer for it, and copy the data to the URB */
index 1bcfad1..81b88e9 100644 (file)
@@ -725,7 +725,7 @@ static netdev_tx_t esd_usb_start_xmit(struct sk_buff *skb,
        int ret = NETDEV_TX_OK;
        size_t size = sizeof(struct esd_usb_msg);
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        /* create a URB, and a buffer for it, and copy the data to the URB */
index 51294b7..25f863b 100644 (file)
@@ -1913,7 +1913,7 @@ static netdev_tx_t es58x_start_xmit(struct sk_buff *skb,
        unsigned int frame_len;
        int ret;
 
-       if (can_dropped_invalid_skb(netdev, skb)) {
+       if (can_dev_dropped_skb(netdev, skb)) {
                if (priv->tx_urb)
                        goto xmit_commit;
                return NETDEV_TX_OK;
index f0065d4..9c2c25f 100644 (file)
@@ -723,7 +723,7 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb,
        unsigned int idx;
        struct gs_tx_context *txc;
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        /* find an empty context to keep track of transmission */
index e91648e..802e27c 100644 (file)
@@ -570,7 +570,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
        unsigned int i;
        unsigned long flags;
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        urb = usb_alloc_urb(0, GFP_ATOMIC);
index 69346c6..218b098 100644 (file)
@@ -311,7 +311,7 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
                .cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV
        };
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        ctx = mcba_usb_get_free_ctx(priv, cf);
index 225697d..1d996d3 100644 (file)
@@ -351,7 +351,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
        int i, err;
        size_t size = dev->adapter->tx_buffer_size;
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++)
index 7c35f50..67c2ff4 100644 (file)
@@ -1120,7 +1120,7 @@ static netdev_tx_t ucan_start_xmit(struct sk_buff *skb,
        struct can_frame *cf = (struct can_frame *)skb->data;
 
        /* check skb */
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        /* allocate a context and slow down tx path, if fifo state is low */
index 64c00ab..8a5596c 100644 (file)
@@ -602,7 +602,7 @@ static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb,
        int i, err;
        size_t size = sizeof(struct usb_8dev_tx_msg);
 
-       if (can_dropped_invalid_skb(netdev, skb))
+       if (can_dev_dropped_skb(netdev, skb))
                return NETDEV_TX_OK;
 
        /* create a URB, and a buffer for it, and copy the data to the URB */
index 5d31727..43c812e 100644 (file)
@@ -743,7 +743,7 @@ static netdev_tx_t xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        struct xcan_priv *priv = netdev_priv(ndev);
        int ret;
 
-       if (can_dropped_invalid_skb(ndev, skb))
+       if (can_dev_dropped_skb(ndev, skb))
                return NETDEV_TX_OK;
 
        if (priv->devtype.flags & XCAN_FLAG_TX_MAILBOXES)
index 58f5431..982ba24 100644 (file)
@@ -152,6 +152,22 @@ static inline bool can_is_canxl_dev_mtu(unsigned int mtu)
        return (mtu >= CANXL_MIN_MTU && mtu <= CANXL_MAX_MTU);
 }
 
+/* drop skb if it does not contain a valid CAN frame for sending */
+static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (priv->ctrlmode & CAN_CTRLMODE_LISTENONLY) {
+               netdev_info_once(dev,
+                                "interface in listen only mode, dropping skb\n");
+               kfree_skb(skb);
+               dev->stats.tx_dropped++;
+               return true;
+       }
+
+       return can_dropped_invalid_skb(dev, skb);
+}
+
 void can_setup(struct net_device *dev);
 
 struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,