[PATCH 1/3] accel/ethosu: clean up resources on probe failure

From: zhaoguohan

Date: Thu Jul 16 2026 - 04:27:26 EST


From: GuoHan Zhao <zhaoguohan@xxxxxxxxxx>

ethosu_job_init() creates a scheduler before ethosu_init() and
drm_dev_register(). Errors from either later step return directly, leaving
the scheduler workqueue allocated. A drm_dev_register() failure also leaves
the optional SRAM allocation behind.

Unwind runtime PM, SRAM, and scheduler state in reverse order on probe
failure. Disable the clocks when runtime PM setup fails as well.

Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Link: https://sashiko.dev/#/patchset/20260716065219.931088-1-zhaoguohan@xxxxxxxxxx?part=1
Signed-off-by: GuoHan Zhao <zhaoguohan@xxxxxxxxxx>
---
Sashiko report:

> This is a pre-existing issue, but was not introduced by this patch.
>
> If ethosu_init() or drm_dev_register() fail, the function returns
> directly without calling ethosu_job_fini() or freeing the sram pool.
>
> Because the job scheduler is embedded in the devm-allocated
> ethosu_device, it will be freed when devm cleans up. However, the drm
> scheduler kthreads and timers remain active and will access the freed
> memory.
>
> Can this cause a use-after-free regression leading to a kernel panic?

drivers/accel/ethosu/ethosu_drv.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c
index ed9c748a54ad..d1153b15ca3e 100644
--- a/drivers/accel/ethosu/ethosu_drv.c
+++ b/drivers/accel/ethosu/ethosu_drv.c
@@ -293,6 +293,14 @@ static int ethosu_sram_init(struct ethosu_device *ethosudev)
return 0;
}

+static void ethosu_sram_fini(struct ethosu_device *ethosudev)
+{
+ if (ethosudev->sram)
+ gen_pool_free(ethosudev->srampool,
+ (unsigned long)ethosudev->sram,
+ ethosudev->npu_info.sram_size);
+}
+
static int ethosu_init(struct ethosu_device *ethosudev)
{
int ret;
@@ -306,7 +314,7 @@ static int ethosu_init(struct ethosu_device *ethosudev)
pm_runtime_use_autosuspend(ethosudev->base.dev);
ret = devm_pm_runtime_set_active_enabled(ethosudev->base.dev);
if (ret)
- return ret;
+ goto err_suspend;
pm_runtime_get_noresume(ethosudev->base.dev);

ethosudev->npu_info.id = id = readl_relaxed(ethosudev->regs + NPU_REG_ID);
@@ -326,6 +334,11 @@ static int ethosu_init(struct ethosu_device *ethosudev)
ethosudev->npu_info.sram_size / 1024);

return 0;
+
+err_suspend:
+ pm_runtime_dont_use_autosuspend(ethosudev->base.dev);
+ ethosu_device_suspend(ethosudev->base.dev);
+ return ret;
}

static int ethosu_probe(struct platform_device *pdev)
@@ -353,13 +366,21 @@ static int ethosu_probe(struct platform_device *pdev)

ret = ethosu_init(ethosudev);
if (ret)
- return ret;
+ goto err_job_fini;

ret = drm_dev_register(&ethosudev->base, 0);
if (ret)
- pm_runtime_dont_use_autosuspend(ethosudev->base.dev);
+ goto err_runtime_suspend;

pm_runtime_put_autosuspend(ethosudev->base.dev);
+ return 0;
+
+err_runtime_suspend:
+ pm_runtime_dont_use_autosuspend(ethosudev->base.dev);
+ pm_runtime_put_sync_suspend(ethosudev->base.dev);
+ ethosu_sram_fini(ethosudev);
+err_job_fini:
+ ethosu_job_fini(ethosudev);
return ret;
}

@@ -369,9 +390,7 @@ static void ethosu_remove(struct platform_device *pdev)

drm_dev_unregister(&ethosudev->base);
ethosu_job_fini(ethosudev);
- if (ethosudev->sram)
- gen_pool_free(ethosudev->srampool, (unsigned long)ethosudev->sram,
- ethosudev->npu_info.sram_size);
+ ethosu_sram_fini(ethosudev);
}

static const struct of_device_id dt_match[] = {
--
2.43.0