forked from mirrors/linux
		
	can: dev: fix skb drop check
In commita6d190f8c7("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:a6d190f8c7("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>
This commit is contained in:
		
							parent
							
								
									3eb3d283e8
								
							
						
					
					
						commit
						ae64438be1
					
				
					 36 changed files with 51 additions and 43 deletions
				
			
		|  | @ -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); | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
|  | @ -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:
 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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))) { | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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(®s->cantier, 0); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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++) | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Oliver Hartkopp
						Oliver Hartkopp