[PATCH 2/4] can: gs_usb: gs_usb_probe(): init netdev completely before register_candev()
From: Marc Kleine-Budde
Date: Sun Oct 25 2026 - 14:38:30 EST
In gs_usb_probe(), gs_make_candev() is called to allocate and set up the
CAN channel, as well as to call register_candev(). However, gs_usb_probe()
defers the assignment of dev->parent and parent->hf_size_rx until after
gs_make_candev() returns. This opens a race window for the user space to
bring up the CAN interface, while it's not completely setup.
To close the race window, initialize all CAN interfaces in a first loop and
then register them in a second loop.
Closes: https://sashiko.dev/#/bug/linux-be54de7d-0b75-408c-b3ee-dd90c9756857
Closes: https://sashiko.dev/#/bug/linux-71076d59-0843-4749-a5ab-fdd98a08582f
Fixes: d08e973a77d1 ("can: gs_usb: Added support for the GS_USB CAN devices")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Marc Kleine-Budde <mkl@xxxxxxxxxxxxxx>
---
drivers/net/can/usb/gs_usb.c | 45 ++++++++++++++++++++++++++++----------------
1 file changed, 29 insertions(+), 16 deletions(-)
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index 15f28833f2de..985d6a9113d9 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -1490,18 +1490,8 @@ static struct gs_can *gs_make_candev(unsigned int channel,
can_rx_offload_add_manual(netdev, &dev->offload, GS_NAPI_WEIGHT);
SET_NETDEV_DEV(netdev, &intf->dev);
- rc = register_candev(dev->netdev);
- if (rc) {
- dev_err(&intf->dev,
- "Couldn't register candev for channel %d (%pe)\n",
- channel, ERR_PTR(rc));
- goto out_can_rx_offload_del;
- }
-
return dev;
-out_can_rx_offload_del:
- can_rx_offload_del(&dev->offload);
out_free_candev:
free_candev(dev->netdev);
return ERR_PTR(rc);
@@ -1593,14 +1583,14 @@ static int gs_usb_probe(struct usb_interface *intf,
/* save error code to return later */
rc = PTR_ERR(parent->canch[i]);
- /* on failure destroy previously created candevs */
+ /* on failure clean up previously created candevs */
icount = i;
- for (i = 0; i < icount; i++)
- gs_destroy_candev(parent->canch[i]);
+ for (i = 0; i < icount; i++) {
+ can_rx_offload_del(&parent->canch[i]->offload);
+ free_candev(parent->canch[i]->netdev);
+ }
- usb_kill_anchored_urbs(&parent->rx_submitted);
- kfree(parent);
- return rc;
+ goto out_usb_kill_anchored_urbs;
}
parent->canch[i]->parent = parent;
@@ -1621,7 +1611,30 @@ static int gs_usb_probe(struct usb_interface *intf,
parent->hf_size_rx = max(parent->hf_size_rx, hf_size_rx);
}
+ for (i = 0; i < icount; i++) {
+ struct gs_can *dev = parent->canch[i];
+
+ rc = register_candev(dev->netdev);
+ if (rc) {
+ dev_err(&intf->dev,
+ "Couldn't register candev for channel %d (%pe)\n",
+ i, ERR_PTR(rc));
+
+ icount = i;
+ for (i = 0; i < icount; i++)
+ gs_destroy_candev(parent->canch[i]);
+
+ goto out_usb_kill_anchored_urbs;
+ }
+ }
+
return 0;
+
+out_usb_kill_anchored_urbs:
+ usb_kill_anchored_urbs(&parent->rx_submitted);
+ kfree(parent);
+
+ return rc;
}
static void gs_usb_disconnect(struct usb_interface *intf)
--
2.53.0