From abfee9d347558cd3b9e18d6a1570cd8ce2df8f27 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Sep 2026 11:03:40 +0300 Subject: [PATCH 1/2] schedule: dp: restore the component driver on the error path scheduler_dp_task_init() keeps its own copy of the component driver inside task_memory and repoints mod->dev->drv at it: task_memory->drv = *mod->dev->drv; mod->dev->drv = &task_memory->drv; Every error path ends at e_tmem, which releases task_memory, so mod->dev->drv is left pointing into memory that has just been freed. Nothing notices until the host tears the pipeline down and the module is freed for real: module_free(): ops = mod->dev->drv->adapter_ops That dereference faults. The module heap is a vregion whose pages are unmapped when it is released, so the access is rejected by the MMU rather than quietly returning junk: ** FATAL EXCEPTION ** CPU 2 EXCCAUSE 28 (load prohibited) ** PC 0xa008297f Backtrace: module_free <- module_adapter_free <- lib_manager_module_free <- ipc4_delete_pipeline Remember the original pointer and put it back before task_memory is freed. Verified on PTL by reverting the vpage reservation fix to bring back the partition overlap that makes DP task creation fail: 18 consecutive failures were reported to the host as errors with no heap corruption, no exception and no panic, where previously the first one halted the core. Signed-off-by: Jyri Sarha --- src/schedule/zephyr_dp_schedule_application.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index e1d34f0458c8..1ae7f144cd34 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -478,6 +478,8 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, memset(task_memory, 0, sizeof(*task_memory)); + const struct comp_driver *drv = mod->dev->drv; + task_memory->drv = *mod->dev->drv; mod->dev->drv = &task_memory->drv; @@ -643,6 +645,8 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, e_stack: user_stack_free(p_stack); e_tmem: + /* the copy lives in task_memory, so stop pointing at it before freeing */ + mod->dev->drv = drv; mod_free(mod, task_memory); return ret; } From 51a342c1dbf92c2894177aeccab291493c341abd Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Sep 2026 00:57:57 +0300 Subject: [PATCH 2/2] schedule: dp: publish the task pointer only once it cannot be freed scheduler_dp_task_init() stored the new task in *task, which is the caller's comp->task, immediately after initialising it - well before the memory domain setup that can still fail. The task lives inside task_memory, so every error path from that point on frees the object that comp->task points at: e_dom -> ... -> e_tmem: mod_free(mod, task_memory) module_adapter_new_ext() then runs its own cleanup, which starts with if (dev->task) schedule_task_free(dev->task); and so releases the same memory a second time. A failure that should have been reported as a plain -EINVAL instead took the core down: scheduler_dp_task_init: failed to add LLEXT to domain -22 module_adapter_new_ext: DP task creation failed with error -22. sys_heap_free: heap corruption (double free?) at 0xa017fffc ** FATAL EXCEPTION ** CPU 2 EXCCAUSE 63 (zephyr exception) >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 2 A stale comp->task is harmful on its own as well, because pipeline_comp_dp_task_init() returns early when it is set and would hand out a dangling task on a later attempt. Assign *task only after the last failure point, which is what the non-userspace scheduler_dp_task_init() in zephyr_dp_schedule_thread.c already does. Signed-off-by: Jyri Sarha --- src/schedule/zephyr_dp_schedule_application.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/schedule/zephyr_dp_schedule_application.c b/src/schedule/zephyr_dp_schedule_application.c index 1ae7f144cd34..226c86ba4c9e 100644 --- a/src/schedule/zephyr_dp_schedule_application.c +++ b/src/schedule/zephyr_dp_schedule_application.c @@ -528,7 +528,6 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, ptask->ops.get_deadline = ops->get_deadline; ptask->priv_data = pdata; list_init(&ptask->list); - *task = ptask; /* create a zephyr thread for the task */ pdata->thread_id = k_thread_create(pdata->thread, p_stack, @@ -629,6 +628,9 @@ int scheduler_dp_task_init(struct task **task, const struct sof_uuid_entry *uid, k_event_init(pdata->event); k_thread_start(pdata->thread_id); + /* ptask points into task_memory, so only publish it once it cannot be freed */ + *task = ptask; + return 0; e_dom: