Linux CAN drivers development
 help / color / mirror / Atom feed
* [PATCH] can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free
@ 2026-11-07 15:33 hyunjungg
  2026-11-08 15:33 ` sashiko-bot
  2026-11-09  4:24 ` netdev-bot+sashiko
  0 siblings, 2 replies; 4+ messages in thread
From: hyunjungg @ 2026-11-07 15:33 UTC (permalink / raw)
  To: Marc Kleine-Budde, linux-can
  Cc: Vincent Mailhol, netdev, linux-kernel, Hyunjung Ko, stable

From: Hyunjung Ko <hj351016@gmail.com>

pcan_usb arms pdev->restart_timer from pcan_usb_restart_pending() (the
restart-command URB completion) during CAN bus-off auto-restart; the
timer callback pcan_usb_restart() obtains the pcan_usb via container_of()
and calls peak_usb_restart_complete(&pdev->dev).

Unlike the other PEAK USB adapters, pcan_usb provides no dev_stop
callback, so restart_timer is never cancelled on teardown. When the
interface is brought down or the USB device is disconnected,
peak_usb_ndo_stop() / unregister_candev() tear the device down and
free_candev() frees the peak_usb_device (and the containing pcan_usb)
without deleting restart_timer. A restart timer still pending then fires
pcan_usb_restart() against the freed pcan_usb (use-after-free).

Add a pcan_usb dev_stop callback that deletes restart_timer, so it is
drained on ndo_stop() - which unregister_candev() invokes on disconnect
while the interface is up. pcan_usb_restart() does not re-arm the timer,
so a single timer_delete_sync() drains it.

Found by static analysis and code review; not tested on real hardware
(needs a PEAK PCAN-USB adapter and a bus-off auto-restart racing unbind).

Fixes: 46be265d3388 ("can: usb: PEAK-System Technik PCAN-USB specific part")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunjung Ko <hj351016@gmail.com>
---
 drivers/net/can/usb/peak_usb/pcan_usb.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
index 8fd058c..4c13195 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
@@ -882,6 +882,14 @@ static int pcan_usb_start(struct peak_usb_device *dev)
 	return pcan_usb_set_ext_vcc(dev, 0);
 }
 
+static int pcan_usb_stop(struct peak_usb_device *dev)
+{
+	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
+
+	timer_delete_sync(&pdev->restart_timer);
+	return 0;
+}
+
 static int pcan_usb_init(struct peak_usb_device *dev)
 {
 	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
@@ -1051,6 +1059,7 @@ const struct peak_usb_adapter pcan_usb = {
 	.dev_decode_buf = pcan_usb_decode_buf,
 	.dev_encode_msg = pcan_usb_encode_msg,
 	.dev_start = pcan_usb_start,
+	.dev_stop = pcan_usb_stop,
 	.dev_restart_async = pcan_usb_restart_async,
 	.do_get_berr_counter = pcan_usb_get_berr_counter,
 };
-- 
2.43.0


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

* Re: [PATCH] can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free
  2026-11-07 15:33 [PATCH] can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free hyunjungg
@ 2026-11-08 15:33 ` sashiko-bot
  2026-11-09  4:24 ` netdev-bot+sashiko
  1 sibling, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-11-08 15:33 UTC (permalink / raw)
  To: hyunjungg, linux-can
  Cc: Marc Kleine-Budde, Oliver Hartkopp, Vincent Mailhol,
	Oleksij Rempel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] can: peak_usb: in-flight restart URB arms timer after
  pcan_usb_stop()
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [Critical] can: peak_usb: incomplete restart_timer cleanup in
  peak_usb_create_dev() [1]
--

commit 711582c698eedc1cd7434111f52222eb7dcd4279
Author: Hyunjung Ko <hj351016@gmail.com>

can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free

This commit adds a dev_stop callback to pcan_usb that calls
timer_delete_sync() on pdev->restart_timer when the interface is stopped.

> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
> index 8fd058c328564..4c131958b5aa6 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
> @@ -882,6 +882,14 @@ static int pcan_usb_start(struct peak_usb_device *dev)
>  	return pcan_usb_set_ext_vcc(dev, 0);
>  }
>  
> +static int pcan_usb_stop(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
> +
> +	timer_delete_sync(&pdev->restart_timer);
> +	return 0;
> +}

[Severity: Medium]
Can an in-flight restart URB complete after pcan_usb_stop() and re-arm
pdev->restart_timer?

pcan_usb_restart_async() submits its bulk URB without anchoring it to
dev->tx_submitted, so peak_usb_unlink_all_urbs() does not kill it before
calling dev_stop().  In addition, pcan_usb_restart_pending() unconditionally
calls mod_timer(&pdev->restart_timer, ...) without checking urb->status or
PCAN_USB_STATE_STARTED:

CPU0                              CPU1
can_restart()
  ...
    pcan_usb_restart_async()
      usb_submit_urb(urb, ...)
peak_usb_ndo_stop()
  peak_usb_unlink_all_urbs(dev)
  pcan_usb_stop(dev)
    timer_delete_sync(&pdev->restart_timer)
                                  pcan_usb_restart_pending(urb)
                                    mod_timer(&pdev->restart_timer, ...)
  ...
  free_candev(netdev)
                                  pcan_usb_restart()
                                    peak_usb_restart_complete(&pdev->dev)
                                      <-- use-after-free

If the interface remains registered, pcan_usb_restart() marks a stopped
interface as CAN_STATE_ERROR_ACTIVE and wakes its queue; if the USB device
disconnects after peak_usb_ndo_stop() has cleared IFF_UP, unregister_candev()
does not call peak_usb_ndo_stop() again and free_candev() frees pdev while
restart_timer is still armed.

Anchoring the restart URB so peak_usb_unlink_all_urbs() kills it before
pcan_usb_stop(), or only arming restart_timer in pcan_usb_restart_pending()
when urb->status == 0 and PCAN_USB_STATE_STARTED is set, would close this
race.

> +
>  static int pcan_usb_init(struct peak_usb_device *dev)
>  {
>  	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);

[Severity: Critical]
This problem wasn't introduced by this patch, but can a failed probe in
peak_usb_create_dev() free netdev while pdev->restart_timer is still armed
[1]?

peak_usb_create_dev() calls register_candev(netdev) before calling
dev->adapter->dev_init() (pcan_usb_init()) and dev->adapter->dev_set_bus():

drivers/net/can/usb/peak_usb/pcan_usb_core.c:peak_usb_create_dev() {
	...
	err = register_candev(netdev);
	if (err) {
		...
		goto lbl_restore_intf_data;
	}
	...
	if (dev->adapter->dev_init) {
		err = dev->adapter->dev_init(dev);
		if (err)
			goto lbl_unregister_candev;
	}

	/* set bus off */
	if (dev->adapter->dev_set_bus) {
		err = dev->adapter->dev_set_bus(dev, 0);
		if (err)
			goto adap_dev_free;
	}
	...
adap_dev_free:
	if (dev->adapter->dev_free)
		dev->adapter->dev_free(dev);

lbl_unregister_candev:
	unregister_candev(netdev);
	...
lbl_free_candev:
	free_candev(netdev);
}

Because register_candev() publishes the netdev and drops rtnl_lock before
pcan_usb_init() or dev_set_bus() blocks on USB bulk transfers, another
context can open the device and trigger a bus-off restart via
pcan_usb_restart_async().  If pcan_usb_get_serial() fails in pcan_usb_init(),
or if an in-flight restart URB completes after unregister_candev(),
free_candev() frees pdev while restart_timer is pending, causing a
use-after-free when pcan_usb_restart() fires.

[1] https://sashiko.dev/#/bug/linux-c6d07ad0-7ac4-41cf-a567-927ba96a5e93

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

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

* Re: [PATCH] can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free
  2026-11-07 15:33 [PATCH] can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free hyunjungg
  2026-11-08 15:33 ` sashiko-bot
@ 2026-11-09  4:24 ` netdev-bot+sashiko
  2026-11-09  4:49   ` khj
  1 sibling, 1 reply; 4+ messages in thread
From: netdev-bot+sashiko @ 2026-11-09  4:24 UTC (permalink / raw)
  To: hj351016
  Cc: mkl, linux-can, mailhol.vincent, netdev, linux-kernel, stable,
	kuba

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

Critical: 0 · High: 1 · Medium: 0 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] The fix leaves the use-after-free reachable, because
  restart_timer can be armed again after pcan_usb_stop() has deleted it.

--

can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free

This adds a dev_stop callback to the PCAN-USB adapter. The callback calls
timer_delete_sync() on pdev->restart_timer, so the timer is drained in
peak_usb_ndo_stop(). The aim is to stop pcan_usb_restart() from running
on a freed struct pcan_usb after free_candev().

> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
> index 8fd058c328564..4c131958b5aa6 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
> @@ -882,6 +882,14 @@ static int pcan_usb_start(struct peak_usb_device *dev)
>  	return pcan_usb_set_ext_vcc(dev, 0);
>  }
>  
> +static int pcan_usb_stop(struct peak_usb_device *dev)
> +{
> +	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
> +
> +	timer_delete_sync(&pdev->restart_timer);

[Severity: High]
Can restart_timer be armed again after this delete?

The timer is armed by the restart URB completion, not by the timer
callback. The completion calls mod_timer() every time and never checks
urb->status:

drivers/net/can/usb/peak_usb/pcan_usb.c:pcan_usb_restart_pending() {
	...
	mod_timer(&pdev->restart_timer,
			jiffies + msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT));
	...
}

pcan_usb_restart_async() submits that URB without anchoring it to
dev->rx_submitted or dev->tx_submitted:

	usb_fill_bulk_urb(urb, dev->udev,
			usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT),
			buf, PCAN_USB_CMD_LEN,
			pcan_usb_restart_pending, pdev);

	return usb_submit_urb(urb, GFP_ATOMIC);

So peak_usb_unlink_all_urbs() never kills it.

Suppose a bus-off auto-restart, or a manual restart through netlink, has
this URB in flight when the interface is brought down. The path is
peak_usb_set_mode(CAN_MODE_START) -> peak_usb_restart() ->
pcan_usb_restart_async(). peak_usb_ndo_stop() then runs as follows:

  close_candev()
    cancels only the restart work
  peak_usb_unlink_all_urbs()
    kills only rx_submitted and tx_submitted
  dev->adapter->dev_stop()
    timer is not pending yet, so nothing is deleted
  dev->adapter->dev_set_bus(dev, 0)
    synchronous usb_bulk_msg() on PCAN_USB_EP_CMDOUT

Bulk URBs on one endpoint complete in order. Would the queued restart URB
then complete and call mod_timer() after pcan_usb_stop() has returned?

Next, suppose the device is unplugged, unbound, or the module is removed
within PCAN_USB_STARTUP_TIMEOUT. peak_usb_disconnect() calls
unregister_candev() on an interface that is already down, so ndo_stop
and dev_stop do not run again. pcan_usb sets no .dev_free, so nothing
stops the timer before free_candev():

drivers/net/can/usb/peak_usb/pcan_usb_core.c:peak_usb_disconnect() {
	...
		unregister_candev(netdev);

		kfree(dev->cmd_buf);
		dev->next_siblings = NULL;
		if (dev->adapter->dev_free)
			dev->adapter->dev_free(dev);

		free_candev(netdev);
	...
}

pcan_usb_restart() -> peak_usb_restart_complete() would then write
dev->can.state and call netif_wake_queue() on freed memory.

No disconnect is needed for a related problem. Wouldn't the late timer
still move a stopped interface from CAN_STATE_STOPPED to
CAN_STATE_ERROR_ACTIVE and wake its queue after ndo_stop?

The commit message says:

  pcan_usb_restart() does not re-arm the timer, so a single
  timer_delete_sync() drains it.

That holds for the callback. Does it cover the case here, though, where
the timer is armed by the asynchronous URB completion?

Would a pcan_usb .dev_free callback that calls timer_shutdown_sync()
work instead? peak_usb_disconnect() calls it after unregister_candev()
and before free_candev(). By then, usb_unbind_interface() has already
flushed the endpoint URBs with usb_disable_interface(), because the
driver has no soft_unbind.

Another option is to anchor the restart URB and kill it before dev_stop
deletes the timer.

The probe error paths in peak_usb_create_dev() also reach free_candev()
with no restart_timer teardown for pcan_usb. These are the adap_dev_free
and lbl_unregister_candev labels. Would the dev_free approach cover those
paths too?

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261107153319.3765325-1-hj351016%40gmail.com

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

* Re: [PATCH] can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free
  2026-11-09  4:24 ` netdev-bot+sashiko
@ 2026-11-09  4:49   ` khj
  0 siblings, 0 replies; 4+ messages in thread
From: khj @ 2026-11-09  4:49 UTC (permalink / raw)
  To: netdev-bot+sashiko
  Cc: mkl, linux-can, mailhol.vincent, netdev, linux-kernel, stable,
	kuba

Thanks -- this is correct. v1's dev_stop + timer_delete_sync does not
cover the re-arm: restart_timer is armed from the restart-command URB
completion (pcan_usb_restart_pending),
which is not anchored and can run after the delete, and neither
disconnect nor the adap_dev_free probe-error path calls dev_stop at
all.

v2 drops the dev_stop approach and adds a pcan_usb .dev_free that
timer_shutdown_sync()s restart_timer. dev_free() runs after
unregister_candev() and before free_candev(),
and shutdown (unlike delete) also prevents a late URB completion from
re-arming the timer. Respinning as a new thread.

pw-bot: cr

2026년 11월 9일 (월) 오후 1:25, <netdev-bot+sashiko@kernel.org>님이 작성:
>
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider.
>
> Critical: 0 · High: 1 · Medium: 0 · Low: 0
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [High] The fix leaves the use-after-free reachable, because
>   restart_timer can be armed again after pcan_usb_stop() has deleted it.
>
> --
>
> can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free
>
> This adds a dev_stop callback to the PCAN-USB adapter. The callback calls
> timer_delete_sync() on pdev->restart_timer, so the timer is drained in
> peak_usb_ndo_stop(). The aim is to stop pcan_usb_restart() from running
> on a freed struct pcan_usb after free_candev().
>
> > diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
> > index 8fd058c328564..4c131958b5aa6 100644
> > --- a/drivers/net/can/usb/peak_usb/pcan_usb.c
> > +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
> > @@ -882,6 +882,14 @@ static int pcan_usb_start(struct peak_usb_device *dev)
> >       return pcan_usb_set_ext_vcc(dev, 0);
> >  }
> >
> > +static int pcan_usb_stop(struct peak_usb_device *dev)
> > +{
> > +     struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
> > +
> > +     timer_delete_sync(&pdev->restart_timer);
>
> [Severity: High]
> Can restart_timer be armed again after this delete?
>
> The timer is armed by the restart URB completion, not by the timer
> callback. The completion calls mod_timer() every time and never checks
> urb->status:
>
> drivers/net/can/usb/peak_usb/pcan_usb.c:pcan_usb_restart_pending() {
>         ...
>         mod_timer(&pdev->restart_timer,
>                         jiffies + msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT));
>         ...
> }
>
> pcan_usb_restart_async() submits that URB without anchoring it to
> dev->rx_submitted or dev->tx_submitted:
>
>         usb_fill_bulk_urb(urb, dev->udev,
>                         usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT),
>                         buf, PCAN_USB_CMD_LEN,
>                         pcan_usb_restart_pending, pdev);
>
>         return usb_submit_urb(urb, GFP_ATOMIC);
>
> So peak_usb_unlink_all_urbs() never kills it.
>
> Suppose a bus-off auto-restart, or a manual restart through netlink, has
> this URB in flight when the interface is brought down. The path is
> peak_usb_set_mode(CAN_MODE_START) -> peak_usb_restart() ->
> pcan_usb_restart_async(). peak_usb_ndo_stop() then runs as follows:
>
>   close_candev()
>     cancels only the restart work
>   peak_usb_unlink_all_urbs()
>     kills only rx_submitted and tx_submitted
>   dev->adapter->dev_stop()
>     timer is not pending yet, so nothing is deleted
>   dev->adapter->dev_set_bus(dev, 0)
>     synchronous usb_bulk_msg() on PCAN_USB_EP_CMDOUT
>
> Bulk URBs on one endpoint complete in order. Would the queued restart URB
> then complete and call mod_timer() after pcan_usb_stop() has returned?
>
> Next, suppose the device is unplugged, unbound, or the module is removed
> within PCAN_USB_STARTUP_TIMEOUT. peak_usb_disconnect() calls
> unregister_candev() on an interface that is already down, so ndo_stop
> and dev_stop do not run again. pcan_usb sets no .dev_free, so nothing
> stops the timer before free_candev():
>
> drivers/net/can/usb/peak_usb/pcan_usb_core.c:peak_usb_disconnect() {
>         ...
>                 unregister_candev(netdev);
>
>                 kfree(dev->cmd_buf);
>                 dev->next_siblings = NULL;
>                 if (dev->adapter->dev_free)
>                         dev->adapter->dev_free(dev);
>
>                 free_candev(netdev);
>         ...
> }
>
> pcan_usb_restart() -> peak_usb_restart_complete() would then write
> dev->can.state and call netif_wake_queue() on freed memory.
>
> No disconnect is needed for a related problem. Wouldn't the late timer
> still move a stopped interface from CAN_STATE_STOPPED to
> CAN_STATE_ERROR_ACTIVE and wake its queue after ndo_stop?
>
> The commit message says:
>
>   pcan_usb_restart() does not re-arm the timer, so a single
>   timer_delete_sync() drains it.
>
> That holds for the callback. Does it cover the case here, though, where
> the timer is armed by the asynchronous URB completion?
>
> Would a pcan_usb .dev_free callback that calls timer_shutdown_sync()
> work instead? peak_usb_disconnect() calls it after unregister_candev()
> and before free_candev(). By then, usb_unbind_interface() has already
> flushed the endpoint URBs with usb_disable_interface(), because the
> driver has no soft_unbind.
>
> Another option is to anchor the restart URB and kill it before dev_stop
> deletes the timer.
>
> The probe error paths in peak_usb_create_dev() also reach free_candev()
> with no restart_timer teardown for pcan_usb. These are the adap_dev_free
> and lbl_unregister_candev labels. Would the dev_free approach cover those
> paths too?
>
> [ ... ]
>
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261107153319.3765325-1-hj351016%40gmail.com

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

end of thread, other threads:[~2026-11-09  4:49 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-07 15:33 [PATCH] can: peak_usb: pcan_usb: cancel restart timer on stop to avoid use-after-free hyunjungg
2026-11-08 15:33 ` sashiko-bot
2026-11-09  4:24 ` netdev-bot+sashiko
2026-11-09  4:49   ` khj

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