]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
can: hi311x: fix null pointer dereference when resuming from sleep before interface...
authorChen Yufeng <chenyufeng@iie.ac.cn>
Thu, 11 Sep 2025 15:08:20 +0000 (23:08 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 12 Oct 2025 11:01:02 +0000 (13:01 +0200)
[ Upstream commit 6b696808472197b77b888f50bc789a3bae077743 ]

This issue is similar to the vulnerability in the `mcp251x` driver,
which was fixed in commit 03c427147b2d ("can: mcp251x: fix resume from
sleep before interface was brought up").

In the `hi311x` driver, when the device resumes from sleep, the driver
schedules `priv->restart_work`. However, if the network interface was
not previously enabled, the `priv->wq` (workqueue) is not allocated and
initialized, leading to a null pointer dereference.

To fix this, we move the allocation and initialization of the workqueue
from the `hi3110_open` function to the `hi3110_can_probe` function.
This ensures that the workqueue is properly initialized before it is
used during device resume. And added logic to destroy the workqueue
in the error handling paths of `hi3110_can_probe` and in the
`hi3110_can_remove` function to prevent resource leaks.

Signed-off-by: Chen Yufeng <chenyufeng@iie.ac.cn>
Link: https://patch.msgid.link/20250911150820.250-1-chenyufeng@iie.ac.cn
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/can/spi/hi311x.c

index 6441ff3b4198718e8e662daa3022f06a2405f322..963ea8510dd9bf7bc7d4fd86124dc5d3ef894f7b 100644 (file)
@@ -545,8 +545,6 @@ static int hi3110_stop(struct net_device *net)
 
        priv->force_quit = 1;
        free_irq(spi->irq, priv);
-       destroy_workqueue(priv->wq);
-       priv->wq = NULL;
 
        mutex_lock(&priv->hi3110_lock);
 
@@ -770,34 +768,23 @@ static int hi3110_open(struct net_device *net)
                goto out_close;
        }
 
-       priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
-                                  0);
-       if (!priv->wq) {
-               ret = -ENOMEM;
-               goto out_free_irq;
-       }
-       INIT_WORK(&priv->tx_work, hi3110_tx_work_handler);
-       INIT_WORK(&priv->restart_work, hi3110_restart_work_handler);
-
        ret = hi3110_hw_reset(spi);
        if (ret)
-               goto out_free_wq;
+               goto out_free_irq;
 
        ret = hi3110_setup(net);
        if (ret)
-               goto out_free_wq;
+               goto out_free_irq;
 
        ret = hi3110_set_normal_mode(spi);
        if (ret)
-               goto out_free_wq;
+               goto out_free_irq;
 
        netif_wake_queue(net);
        mutex_unlock(&priv->hi3110_lock);
 
        return 0;
 
- out_free_wq:
-       destroy_workqueue(priv->wq);
  out_free_irq:
        free_irq(spi->irq, priv);
        hi3110_hw_sleep(spi);
@@ -909,6 +896,15 @@ static int hi3110_can_probe(struct spi_device *spi)
        if (ret)
                goto out_clk;
 
+       priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
+                                  0);
+       if (!priv->wq) {
+               ret = -ENOMEM;
+               goto out_clk;
+       }
+       INIT_WORK(&priv->tx_work, hi3110_tx_work_handler);
+       INIT_WORK(&priv->restart_work, hi3110_restart_work_handler);
+
        priv->spi = spi;
        mutex_init(&priv->hi3110_lock);
 
@@ -944,6 +940,8 @@ static int hi3110_can_probe(struct spi_device *spi)
        return 0;
 
  error_probe:
+       destroy_workqueue(priv->wq);
+       priv->wq = NULL;
        hi3110_power_enable(priv->power, 0);
 
  out_clk:
@@ -964,6 +962,9 @@ static void hi3110_can_remove(struct spi_device *spi)
 
        hi3110_power_enable(priv->power, 0);
 
+       destroy_workqueue(priv->wq);
+       priv->wq = NULL;
+
        clk_disable_unprepare(priv->clk);
 
        free_candev(net);