Commit 4e096a18 authored by Oleksij Rempel's avatar Oleksij Rempel Committed by Jakub Kicinski
Browse files

net: introduce CAN specific pointer in the struct net_device



Since 20dd3850 ("can: Speed up CAN frame receiption by using
ml_priv") the CAN framework uses per device specific data in the AF_CAN
protocol. For this purpose the struct net_device->ml_priv is used. Later
the ml_priv usage in CAN was extended for other users, one of them being
CAN_J1939.

Later in the kernel ml_priv was converted to an union, used by other
drivers. E.g. the tun driver started storing it's stats pointer.

Since tun devices can claim to be a CAN device, CAN specific protocols
will wrongly interpret this pointer, which will cause system crashes.
Mostly this issue is visible in the CAN_J1939 stack.

To fix this issue, we request a dedicated CAN pointer within the
net_device struct.

Reported-by: default avatar <syzbot+5138c4dd15a0401bec7b@syzkaller.appspotmail.com>
Fixes: 20dd3850 ("can: Speed up CAN frame receiption by using ml_priv")
Fixes: ffd956ee ("can: introduce CAN midlayer private and allocate it automatically")
Fixes: 9d71dd0c ("can: add support of SAE J1939 protocol")
Fixes: 497a5757 ("tun: switch to net core provided statistics counters")
Signed-off-by: default avatarOleksij Rempel <o.rempel@pengutronix.de>
Link: https://lore.kernel.org/r/20210223070127.4538-1-o.rempel@pengutronix.de


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 88eee9b7
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -239,6 +239,7 @@ void can_setup(struct net_device *dev)
struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
				    unsigned int txqs, unsigned int rxqs)
{
	struct can_ml_priv *can_ml;
	struct net_device *dev;
	struct can_priv *priv;
	int size;
@@ -270,7 +271,8 @@ struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
	priv = netdev_priv(dev);
	priv->dev = dev;

	dev->ml_priv = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN);
	can_ml = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN);
	can_set_ml_priv(dev, can_ml);

	if (echo_skb_max) {
		priv->echo_skb_max = echo_skb_max;
+3 −1
Original line number Diff line number Diff line
@@ -516,6 +516,7 @@ static struct slcan *slc_alloc(void)
	int i;
	char name[IFNAMSIZ];
	struct net_device *dev = NULL;
	struct can_ml_priv *can_ml;
	struct slcan       *sl;
	int size;

@@ -538,7 +539,8 @@ static struct slcan *slc_alloc(void)

	dev->base_addr  = i;
	sl = netdev_priv(dev);
	dev->ml_priv = (void *)sl + ALIGN(sizeof(*sl), NETDEV_ALIGN);
	can_ml = (void *)sl + ALIGN(sizeof(*sl), NETDEV_ALIGN);
	can_set_ml_priv(dev, can_ml);

	/* Initialize channel control data */
	sl->magic = SLCAN_MAGIC;
+1 −1
Original line number Diff line number Diff line
@@ -153,7 +153,7 @@ static void vcan_setup(struct net_device *dev)
	dev->addr_len		= 0;
	dev->tx_queue_len	= 0;
	dev->flags		= IFF_NOARP;
	dev->ml_priv		= netdev_priv(dev);
	can_set_ml_priv(dev, netdev_priv(dev));

	/* set flags according to driver capabilities */
	if (echo)
+5 −1
Original line number Diff line number Diff line
@@ -141,6 +141,8 @@ static const struct net_device_ops vxcan_netdev_ops = {

static void vxcan_setup(struct net_device *dev)
{
	struct can_ml_priv *can_ml;

	dev->type		= ARPHRD_CAN;
	dev->mtu		= CANFD_MTU;
	dev->hard_header_len	= 0;
@@ -149,7 +151,9 @@ static void vxcan_setup(struct net_device *dev)
	dev->flags		= (IFF_NOARP|IFF_ECHO);
	dev->netdev_ops		= &vxcan_netdev_ops;
	dev->needs_free_netdev	= true;
	dev->ml_priv		= netdev_priv(dev) + ALIGN(sizeof(struct vxcan_priv), NETDEV_ALIGN);

	can_ml = netdev_priv(dev) + ALIGN(sizeof(struct vxcan_priv), NETDEV_ALIGN);
	can_set_ml_priv(dev, can_ml);
}

/* forward declaration for rtnl_create_link() */
+12 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@

#include <linux/can.h>
#include <linux/list.h>
#include <linux/netdevice.h>

#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS)
#define CAN_EFF_RCV_HASH_BITS 10
@@ -65,4 +66,15 @@ struct can_ml_priv {
#endif
};

static inline struct can_ml_priv *can_get_ml_priv(struct net_device *dev)
{
	return netdev_get_ml_priv(dev, ML_PRIV_CAN);
}

static inline void can_set_ml_priv(struct net_device *dev,
				   struct can_ml_priv *ml_priv)
{
	netdev_set_ml_priv(dev, ml_priv, ML_PRIV_CAN);
}

#endif /* CAN_ML_H */
Loading