Commit 6b696808 authored by Chen Yufeng's avatar Chen Yufeng Committed by Marc Kleine-Budde
Browse files

can: hi311x: fix null pointer dereference when resuming from sleep before interface was enabled



This issue is similar to the vulnerability in the `mcp251x` driver,
which was fixed in commit 03c42714 ("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: default avatarChen Yufeng <chenyufeng@iie.ac.cn>
Link: https://patch.msgid.link/20250911150820.250-1-chenyufeng@iie.ac.cn


Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent cbf658dd
Loading
Loading
Loading
Loading
+17 −16
Original line number Diff line number Diff line
@@ -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);
@@ -908,6 +895,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);

@@ -943,6 +939,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:
@@ -963,6 +961,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);