[PATCH net-next 03/12] can: replace ARPHRD_CAN type checks with can_get_ml_priv()
flat view
COOLING10d
From: Marc Kleine-Budde <mkl@pengutronix.de>
Date: 2026-10-18 16:50:21
Also in:
linux-can
Subsystem:
can network layer, the rest · Maintainers:
Oliver Hartkopp, Marc Kleine-Budde, Linus Torvalds
From: Oliver Hartkopp <socketcan@hartkopp.net>
Commit 4e096a18867a ("net: introduce CAN specific pointer in the struct
net_device") introduced an explicit way to assign the midlayer private
pointer (dev->ml_priv) to named users like ML_PRIV_CAN. A valid pointer
returned by can_get_ml_priv() therefore identifies a real CAN device.
The referenced commit only converted the j1939 and proc code to this
check, while the other parts of the CAN subsystem still rely on
dev->type == ARPHRD_CAN.
The TUNSETLINK ioctl allows a down TUN/TAP device to set its hardware
type to ARPHRD_CAN while dev->ml_priv remains NULL. Such a repurposed
netdevice passes the ARPHRD_CAN checks in the CAN subsystem, which can
lead to a NULL pointer dereference of the CAN specific ml_priv. This
only requires owning a TUN/TAP device or CAP_NET_ADMIN in an
unprivileged user namespace.
can_rx_register() already rejects such a device since commit 8aa59e355949
("can: af_can: fix NULL pointer dereference in can_rx_register()"),
but can_rx_unregister() only checks dev->type and dereferences the NULL
ml_priv in can_dev_rcv_lists_find().
This path is reachable via isotp: isotp_bind() only checked for
ARPHRD_CAN and ignored the -ENODEV from can_rx_register(), so the socket
was marked as bound to the TUN device. A later isotp_release() or
NETDEV_UNREGISTER notification then crashes in can_rx_unregister().
Replace all remaining ARPHRD_CAN checks in net/can with
can_get_ml_priv(). For can_rx_register() and the can*_rcv() functions
this is a simplification without functional change.
Fixes: 4e096a18867a ("net: introduce CAN specific pointer in the struct net_device")
Cc: stable@kernel.org
Cc: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20261016093525.327367-1-socketcan@hartkopp.net (local)
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
net/can/af_can.c | 15 ++++++---------
net/can/bcm.c | 7 ++++---
net/can/gw.c | 7 ++++---
net/can/isotp.c | 5 +++--
net/can/raw.c | 4 ++--
5 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 34fe3b28d576..dc27ace43719 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c@@ -226,7 +226,7 @@ int can_send(struct sk_buff *skb, int loop) goto inval_skb; } - if (unlikely(skb->dev->type != ARPHRD_CAN)) { + if (unlikely(!can_get_ml_priv(skb->dev))) { err = -EPERM; goto inval_skb; }
@@ -452,7 +452,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id, /* insert new receiver (dev,canid,mask) -> (func,data) */ - if (dev && (dev->type != ARPHRD_CAN || !can_get_ml_priv(dev))) + if (dev && !can_get_ml_priv(dev)) return -ENODEV; if (dev && !net_eq(net, dev_net(dev)))
@@ -519,7 +519,7 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id, struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats; struct can_dev_rcv_lists *dev_rcv_lists; - if (dev && dev->type != ARPHRD_CAN) + if (dev && !can_get_ml_priv(dev)) return; if (dev && !net_eq(net, dev_net(dev)))
@@ -684,8 +684,7 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, { struct can_skb_ext *csx = can_skb_ext_find(skb); - if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || - !csx || !can_is_can_skb(skb))) { + if (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_can_skb(skb))) { pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\n", dev->type, skb->len);
@@ -710,8 +709,7 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, { struct can_skb_ext *csx = can_skb_ext_find(skb); - if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || - !csx || !can_is_canfd_skb(skb))) { + if (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_canfd_skb(skb))) { pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\n", dev->type, skb->len);
@@ -736,8 +734,7 @@ static int canxl_rcv(struct sk_buff *skb, struct net_device *dev, { struct can_skb_ext *csx = can_skb_ext_find(skb); - if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || - !csx || !can_is_canxl_skb(skb))) { + if (unlikely(!can_get_ml_priv(dev) || !csx || !can_is_canxl_skb(skb))) { pr_warn_once("PF_CAN: dropped non conform CAN XL skbuff: dev type %d, len %d\n", dev->type, skb->len);
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 3d637a1e0ac1..60406439a13f 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c@@ -54,6 +54,7 @@ #include <linux/if_arp.h> #include <linux/skbuff.h> #include <linux/can.h> +#include <linux/can/can-ml.h> #include <linux/can/core.h> #include <linux/can/skb.h> #include <linux/can/bcm.h>
@@ -1719,7 +1720,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto out_release; } - if (dev->type != ARPHRD_CAN) { + if (!can_get_ml_priv(dev)) { dev_put(dev); ret = -ENODEV; goto out_release;
@@ -1867,7 +1868,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (dev->type != ARPHRD_CAN) + if (!can_get_ml_priv(dev)) return NOTIFY_DONE; if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN) return NOTIFY_DONE;
@@ -2024,7 +2025,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int ret = -ENODEV; goto fail; } - if (dev->type != ARPHRD_CAN) { + if (!can_get_ml_priv(dev)) { dev_put(dev); ret = -ENODEV; goto fail;
diff --git a/net/can/gw.c b/net/can/gw.c
index 5793cb2420ed..54bb5bd3242a 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c@@ -52,6 +52,7 @@ #include <linux/if_arp.h> #include <linux/skbuff.h> #include <linux/can.h> +#include <linux/can/can-ml.h> #include <linux/can/core.h> #include <linux/can/skb.h> #include <linux/can/gw.h>
@@ -612,7 +613,7 @@ static int cgw_notifier(struct notifier_block *nb, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); - if (dev->type != ARPHRD_CAN) + if (!can_get_ml_priv(dev)) return NOTIFY_DONE; if (msg == NETDEV_UNREGISTER) {
@@ -1163,7 +1164,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, if (!gwj->src.dev) goto out; - if (gwj->src.dev->type != ARPHRD_CAN) + if (!can_get_ml_priv(gwj->src.dev)) goto out; gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
@@ -1171,7 +1172,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, if (!gwj->dst.dev) goto out; - if (gwj->dst.dev->type != ARPHRD_CAN) + if (!can_get_ml_priv(gwj->dst.dev)) goto out; /* is sending the skb back to the incoming interface intended? */
diff --git a/net/can/isotp.c b/net/can/isotp.c
index f5dc9d04bd68..6c28802c0605 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c@@ -65,6 +65,7 @@ #include <linux/if_arp.h> #include <linux/skbuff.h> #include <linux/can.h> +#include <linux/can/can-ml.h> #include <linux/can/core.h> #include <linux/can/skb.h> #include <linux/can/isotp.h>
@@ -1610,7 +1611,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l err = -ENODEV; goto out; } - if (dev->type != ARPHRD_CAN) { + if (!can_get_ml_priv(dev)) { err = -ENODEV; goto out_put_dev; }
@@ -1897,7 +1898,7 @@ static int isotp_notifier(struct notifier_block *nb, unsigned long msg, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (dev->type != ARPHRD_CAN) + if (!can_get_ml_priv(dev)) return NOTIFY_DONE; if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN) return NOTIFY_DONE;
diff --git a/net/can/raw.c b/net/can/raw.c
index 6944ebab3feb..ad611906b308 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c@@ -348,7 +348,7 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (dev->type != ARPHRD_CAN) + if (!can_get_ml_priv(dev)) return NOTIFY_DONE; if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN) return NOTIFY_DONE;
@@ -491,7 +491,7 @@ static int raw_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int len err = -ENODEV; goto out; } - if (dev->type != ARPHRD_CAN) { + if (!can_get_ml_priv(dev)) { err = -ENODEV; goto out_put_dev; }
--
2.53.0