Linux CAN drivers development
 help / color / mirror / Atom feed
* [PATCH] can: isotp: hold a reference to the bound netdevice
@ 2026-06-16 12:39 Vasileios Almpanis
  2026-06-16 12:53 ` sashiko-bot
  0 siblings, 1 reply; 3+ messages in thread
From: Vasileios Almpanis @ 2026-06-16 12:39 UTC (permalink / raw)
  To: Marc Kleine-Budde, Oliver Hartkopp; +Cc: linux-can, linux-kernel

isotp_release() looked up the bound netdevice with
dev_get_by_index(so->ifindex) and only unregistered the socket's CAN
receivers if that lookup succeeded. This is unreliable while the device
is going away: unregister_netdevice_many() removes the device from the
ifindex hash before the NETDEV_UNREGISTER notifier is fired, with a
synchronize_net() in between.

A close() that runs in that window first removes the socket from
isotp_notifier_list, so the pending notifier no longer sees it, and then
gets NULL back from dev_get_by_index() and skips the unregister. The two
struct receiver entries that were allocated by can_rx_register() in
isotp_bind() (the rx filter and the tx echo filter) are then never freed
and leak once the device is destroyed.

Fix it the same way can-raw already does: keep a tracked reference to the
bound device in so->dev and unregister through it, and take rtnl_lock()
in isotp_bind() and isotp_release() to serialise against the
NETDEV_UNREGISTER notifier. The dev_get_by_index() lookup in the release
path is dropped.

Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol")
Cc: stable@vger.kernel.org
Reported-by: syzbot+24201717ed2da31b8fae@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?id=58676a0f698531996a42612c552e894a55b9732b
Signed-off-by: Vasileios Almpanis <vasilisalmpanis@gmail.com>
---
 net/can/isotp.c | 48 +++++++++++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 21 deletions(-)

diff --git a/net/can/isotp.c b/net/can/isotp.c
index c48b4a818297..308d18040b6d 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -69,6 +69,7 @@
 #include <linux/can/skb.h>
 #include <linux/can/isotp.h>
 #include <linux/slab.h>
+#include <linux/rtnetlink.h>
 #include <net/can.h>
 #include <net/sock.h>
 #include <net/net_namespace.h>
@@ -152,6 +153,8 @@ struct isotp_sock {
 	struct sock sk;
 	int bound;
 	int ifindex;
+	struct net_device *dev;
+	netdevice_tracker dev_tracker;
 	canid_t txid;
 	canid_t rxid;
 	ktime_t tx_gap;
@@ -1219,27 +1222,22 @@ static int isotp_release(struct socket *sock)
 	list_del(&so->notifier);
 	spin_unlock(&isotp_notifier_lock);
 
+	rtnl_lock();
 	lock_sock(sk);
 
 	/* remove current filters & unregister */
-	if (so->bound) {
-		if (so->ifindex) {
-			struct net_device *dev;
-
-			dev = dev_get_by_index(net, so->ifindex);
-			if (dev) {
-				if (isotp_register_rxid(so))
-					can_rx_unregister(net, dev, so->rxid,
-							  SINGLE_MASK(so->rxid),
-							  isotp_rcv, sk);
-
-				can_rx_unregister(net, dev, so->txid,
-						  SINGLE_MASK(so->txid),
-						  isotp_rcv_echo, sk);
-				dev_put(dev);
-				synchronize_rcu();
-			}
-		}
+	if (so->bound && so->dev) {
+		if (isotp_register_rxid(so))
+			can_rx_unregister(net, so->dev, so->rxid,
+					  SINGLE_MASK(so->rxid),
+					  isotp_rcv, sk);
+
+		can_rx_unregister(net, so->dev, so->txid,
+				  SINGLE_MASK(so->txid),
+				  isotp_rcv_echo, sk);
+		netdev_put(so->dev, &so->dev_tracker);
+		so->dev = NULL;
+		synchronize_rcu();
 	}
 
 	hrtimer_cancel(&so->txfrtimer);
@@ -1253,6 +1251,7 @@ static int isotp_release(struct socket *sock)
 	sock->sk = NULL;
 
 	release_sock(sk);
+	rtnl_unlock();
 	sock_prot_inuse_add(net, sk->sk_prot, -1);
 	sock_put(sk);
 
@@ -1303,6 +1302,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
 	if (!addr->can_ifindex)
 		return -ENODEV;
 
+	rtnl_lock();
 	lock_sock(sk);
 
 	if (so->bound) {
@@ -1347,16 +1347,20 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
 	can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
 			isotp_rcv_echo, sk, "isotpe", sk);
 
-	dev_put(dev);
-
 	/* switch to new settings */
 	so->ifindex = ifindex;
 	so->rxid = rx_id;
 	so->txid = tx_id;
 	so->bound = 1;
 
+	so->dev = dev;
+	netdev_hold(dev, &so->dev_tracker, GFP_KERNEL);
+
+	dev_put(dev);
+
 out:
 	release_sock(sk);
+	rtnl_unlock();
 
 	if (notify_enetdown) {
 		sk->sk_err = ENETDOWN;
@@ -1559,7 +1563,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
 	if (!net_eq(dev_net(dev), sock_net(sk)))
 		return;
 
-	if (so->ifindex != dev->ifindex)
+	if (so->dev != dev)
 		return;
 
 	switch (msg) {
@@ -1575,10 +1579,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
 			can_rx_unregister(dev_net(dev), dev, so->txid,
 					  SINGLE_MASK(so->txid),
 					  isotp_rcv_echo, sk);
+			netdev_put(dev, &so->dev_tracker);
 		}
 
 		so->ifindex = 0;
 		so->bound  = 0;
+		so->dev = NULL;
 		release_sock(sk);
 
 		sk->sk_err = ENODEV;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] can: isotp: hold a reference to the bound netdevice
  2026-06-16 12:39 [PATCH] can: isotp: hold a reference to the bound netdevice Vasileios Almpanis
@ 2026-06-16 12:53 ` sashiko-bot
  2026-06-16 14:57   ` Vasileios Almpanis
  0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-06-16 12:53 UTC (permalink / raw)
  To: Vasileios Almpanis
  Cc: Vincent Mailhol, Marc Kleine-Budde, linux-can, Oliver Hartkopp,
	Oleksij Rempel

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] Calling synchronize_rcu() while holding rtnl_lock() introduces severe latency spikes and potential denial of service system-wide.

Pre-existing issues:
- [Critical] Skipping synchronize_rcu() in isotp_release() when the socket is unbound leads to a use-after-free of the socket structure by re-armed hrtimers.
--

commit da93bcd8877d85ffe0b043e3045c3374b10e38e5
Author: Vasileios Almpanis <vasilisalmpanis@gmail.com>

can: isotp: hold a reference to the bound netdevice
    
isotp_release() looked up the bound netdevice with
dev_get_by_index(so->ifindex) and only unregistered the socket's CAN
receivers if that lookup succeeded. This is unreliable while the device
is going away.

Cc: stable@vger.kernel.org
Closes: https://syzkaller.appspot.com/bug?id=58676a0f698531996a42612c552e894a55b9732b

> diff --git a/net/can/isotp.c b/net/can/isotp.c
> index c48b4a818297e..308d18040b6db 100644
> --- a/net/can/isotp.c
> +++ b/net/can/isotp.c
> @@ -1219,27 +1222,22 @@ static int isotp_release(struct socket *sock)
>  	list_del(&so->notifier);
>  	spin_unlock(&isotp_notifier_lock);
>  
> +	rtnl_lock();
>  	lock_sock(sk);
>  
>  	/* remove current filters & unregister */
> -	if (so->bound) {
[ ... ]
> +	if (so->bound && so->dev) {
> +		if (isotp_register_rxid(so))
> +			can_rx_unregister(net, so->dev, so->rxid,
> +					  SINGLE_MASK(so->rxid),
> +					  isotp_rcv, sk);
> +
> +		can_rx_unregister(net, so->dev, so->txid,
> +				  SINGLE_MASK(so->txid),
> +				  isotp_rcv_echo, sk);
> +		netdev_put(so->dev, &so->dev_tracker);
> +		so->dev = NULL;
> +		synchronize_rcu();

[Severity: High]
Does holding rtnl_lock() in isotp_release() (net/can/isotp.c) across
synchronize_rcu() cause severe latency spikes and potential denial of
service system-wide?

synchronize_rcu() blocks until all currently active RCU read-side critical
sections across the system finish, which stalls all other network
configuration operations while rtnl_lock() is held.

[Severity: Critical]
This is a pre-existing issue, but skipping synchronize_rcu() when the socket
is already unbound (e.g., by NETDEV_UNREGISTER where isotp_notify() sets
so->bound = 0) appears to allow a use-after-free.

If synchronize_rcu() is bypassed, isotp_release() proceeds to cancel the
socket's hrtimers and drop the user reference without waiting for concurrent
receive operations to complete.

Could a concurrent CAN receive operation like isotp_rcv_ff() or
isotp_rcv_echo() executing within an RCU read-side critical section
unconditionally re-arm the hrtimer after isotp_release() has cancelled it,
leading to a use-after-free when the timer later fires on the freed socket
memory?

>  	}
>  
>  	hrtimer_cancel(&so->txfrtimer);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616123915.674534-1-vasilisalmpanis@gmail.com?part=1

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] can: isotp: hold a reference to the bound netdevice
  2026-06-16 12:53 ` sashiko-bot
@ 2026-06-16 14:57   ` Vasileios Almpanis
  0 siblings, 0 replies; 3+ messages in thread
From: Vasileios Almpanis @ 2026-06-16 14:57 UTC (permalink / raw)
  To: sashiko-reviews
  Cc: Vincent Mailhol, Marc Kleine-Budde, linux-can, Oliver Hartkopp,
	Oleksij Rempel


On 6/16/26 2:53 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> New issues:
> - [High] Calling synchronize_rcu() while holding rtnl_lock() introduces severe latency spikes and potential denial of service system-wide.
>
> Pre-existing issues:
> - [Critical] Skipping synchronize_rcu() in isotp_release() when the socket is unbound leads to a use-after-free of the socket structure by re-armed hrtimers.
> --
>
> commit da93bcd8877d85ffe0b043e3045c3374b10e38e5
> Author: Vasileios Almpanis<vasilisalmpanis@gmail.com>
>
> can: isotp: hold a reference to the bound netdevice
>      
> isotp_release() looked up the bound netdevice with
> dev_get_by_index(so->ifindex) and only unregistered the socket's CAN
> receivers if that lookup succeeded. This is unreliable while the device
> is going away.
>
> Cc:stable@vger.kernel.org
> Closes:https://syzkaller.appspot.com/bug?id=58676a0f698531996a42612c552e894a55b9732b
>
>> diff --git a/net/can/isotp.c b/net/can/isotp.c
>> index c48b4a818297e..308d18040b6db 100644
>> --- a/net/can/isotp.c
>> +++ b/net/can/isotp.c
>> @@ -1219,27 +1222,22 @@ static int isotp_release(struct socket *sock)
>>   	list_del(&so->notifier);
>>   	spin_unlock(&isotp_notifier_lock);
>>   
>> +	rtnl_lock();
>>   	lock_sock(sk);
>>   
>>   	/* remove current filters & unregister */
>> -	if (so->bound) {
> [ ... ]
>> +	if (so->bound && so->dev) {
>> +		if (isotp_register_rxid(so))
>> +			can_rx_unregister(net, so->dev, so->rxid,
>> +					  SINGLE_MASK(so->rxid),
>> +					  isotp_rcv, sk);
>> +
>> +		can_rx_unregister(net, so->dev, so->txid,
>> +				  SINGLE_MASK(so->txid),
>> +				  isotp_rcv_echo, sk);
>> +		netdev_put(so->dev, &so->dev_tracker);
>> +		so->dev = NULL;
>> +		synchronize_rcu();
> [Severity: High]
> Does holding rtnl_lock() in isotp_release() (net/can/isotp.c) across
> synchronize_rcu() cause severe latency spikes and potential denial of
> service system-wide?
>
> synchronize_rcu() blocks until all currently active RCU read-side critical
> sections across the system finish, which stalls all other network
> configuration operations while rtnl_lock() is held.
Okay this is indeed true. rtnl_lock/unlock is not really necessary as 
now receivers will be tracked
through *dev_tracker*. I wanted to mimic *raw_release() *but maybe too 
faithfully. Just dropping rtnl_lock/unlock from
*isotp_release* and *isotp_bind* will be enough since they are 
serialized under *lock_sock.*
> *[*Severity: Critical]
> This is a pre-existing issue, but skipping synchronize_rcu() when the socket
> is already unbound (e.g., by NETDEV_UNREGISTER where isotp_notify() sets
> so->bound = 0) appears to allow a use-after-free.
>
> If synchronize_rcu() is bypassed, isotp_release() proceeds to cancel the
> socket's hrtimers and drop the user reference without waiting for concurrent
> receive operations to complete.
>
> Could a concurrent CAN receive operation like isotp_rcv_ff() or
> isotp_rcv_echo() executing within an RCU read-side critical section
> unconditionally re-arm the hrtimer after isotp_release() has cancelled it,
> leading to a use-after-free when the timer later fires on the freed socket
> memory?
This is a pre-existing issue that is not touched by my changes so it's 
out of scope of this patch. Its true that timers could potentially be 
rearmed after being canceled and *synchronize_rcu *should be called 
unconditionally but I would do that in a separate patch for it to be 
fully bisectable.
Looking forward to any comment reviewers might have before sending v2.
>>   	}
>>   
>>   	hrtimer_cancel(&so->txfrtimer);

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-06-16 14:57 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-16 12:39 [PATCH] can: isotp: hold a reference to the bound netdevice Vasileios Almpanis
2026-06-16 12:53 ` sashiko-bot
2026-06-16 14:57   ` Vasileios Almpanis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox