Skip to content

Commit ffa52df

Browse files
lyakhkv2019i
authored andcommitted
schedule: ll: dynamically allocate the semaphore
The LL scheduler semaphore is used by the userspace, so it has to be allocated dynamically. On the other hand dynamic object freeing from the userspace is unsupported by design. To securely free the semaphore object we add two syscalls that guarantee, that object freeing cannot be abused. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
1 parent d53a762 commit ffa52df

3 files changed

Lines changed: 126 additions & 5 deletions

File tree

src/include/sof/schedule/ll_schedule_domain.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,16 @@ struct ll_schedule_domain *zephyr_domain_init(int clk);
330330
struct k_thread *zephyr_domain_thread_tid(struct ll_schedule_domain *domain);
331331
struct k_mem_domain *zephyr_ll_mem_domain(void);
332332
#endif /* CONFIG_SOF_USERSPACE_LL */
333+
#ifdef CONFIG_SOF_FULL_ZEPHYR_APPLICATION
334+
__syscall int zephyr_ll_task_sem_alloc(struct task *task);
335+
__syscall int zephyr_ll_task_sem_free(struct task *task);
336+
#include <zephyr/syscalls/ll_schedule_domain.h>
337+
#else
338+
int z_impl_zephyr_ll_task_sem_alloc(struct task *task);
339+
int z_impl_zephyr_ll_task_sem_free(struct task *task);
340+
#define zephyr_ll_task_sem_alloc z_impl_zephyr_ll_task_sem_alloc
341+
#define zephyr_ll_task_sem_free z_impl_zephyr_ll_task_sem_free
342+
#endif /* CONFIG_SOF_FULL_ZEPHYR_APPLICATION */
333343
#endif /* __ZEPHYR__ */
334344

335345
struct ll_schedule_domain *dma_multi_chan_domain_init(struct dma *dma_array,

src/schedule/zephyr_ll.c

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ struct zephyr_ll {
4141
struct zephyr_ll_pdata {
4242
bool run;
4343
bool freeing;
44+
struct k_sem *sem_p;
45+
#if !CONFIG_DYNAMIC_OBJECTS
4446
struct k_sem sem;
47+
#endif
4548
};
4649

4750
#if CONFIG_SOF_USERSPACE_LL
@@ -136,7 +139,7 @@ static void zephyr_ll_task_done(struct zephyr_ll *sch,
136139
* zephyr_ll_task_free() is trying to free this task. Complete
137140
* it and signal the semaphore to let the function proceed
138141
*/
139-
k_sem_give(&pdata->sem);
142+
k_sem_give(pdata->sem_p);
140143

141144
tr_info(&ll_tr, "task complete %p %pU", task, task->uid);
142145
tr_info(&ll_tr, "num_tasks %d total_num_tasks %ld",
@@ -448,6 +451,98 @@ static int zephyr_ll_task_schedule_after(void *data, struct task *task, uint64_t
448451
return zephyr_ll_task_schedule_common(sch, task, start, period, after, false);
449452
}
450453

454+
#if CONFIG_DYNAMIC_OBJECTS
455+
static struct list_item zephyr_ll_task_sem_list = LIST_INIT(zephyr_ll_task_sem_list);
456+
457+
struct zephyr_ll_task_sem {
458+
struct task *task;
459+
struct k_sem *sem;
460+
struct list_item list;
461+
};
462+
463+
int z_impl_zephyr_ll_task_sem_alloc(struct task *task)
464+
{
465+
struct zephyr_ll_pdata *pdata = task->priv_data;
466+
struct zephyr_ll_task_sem *ts = rmalloc(SOF_MEM_FLAG_COHERENT, sizeof(*ts));
467+
468+
if (!ts)
469+
return -ENOMEM;
470+
471+
ts->sem = k_object_alloc(K_OBJ_SEM);
472+
if (!ts->sem) {
473+
rfree(ts);
474+
return -ENOMEM;
475+
}
476+
477+
k_sem_init(ts->sem, 0, 1);
478+
479+
ts->task = task;
480+
pdata->sem_p = ts->sem;
481+
/* List is protected by IPC serialization */
482+
list_item_append(&ts->list, &zephyr_ll_task_sem_list);
483+
484+
return 0;
485+
}
486+
487+
int z_impl_zephyr_ll_task_sem_free(struct task *task)
488+
{
489+
struct zephyr_ll_pdata *pdata = task->priv_data;
490+
struct list_item *list;
491+
struct zephyr_ll_task_sem *ts;
492+
bool found = false;
493+
494+
/* List is protected by IPC serialization */
495+
list_for_item(list, &zephyr_ll_task_sem_list) {
496+
ts = container_of(list, struct zephyr_ll_task_sem, list);
497+
if (ts->task == task) {
498+
found = true;
499+
break;
500+
}
501+
}
502+
503+
if (!found)
504+
return -ENOENT;
505+
506+
if (pdata->sem_p != ts->sem)
507+
return -EINVAL;
508+
509+
list_item_del(list);
510+
k_object_free(ts->sem);
511+
rfree(ts);
512+
513+
return 0;
514+
}
515+
516+
#ifdef CONFIG_USERSPACE
517+
#include <zephyr/internal/syscall_handler.h>
518+
static inline int z_vrfy_zephyr_ll_task_sem_alloc(struct task *task)
519+
{
520+
if (!task)
521+
return -EINVAL;
522+
K_OOPS(K_SYSCALL_MEMORY_WRITE(task, sizeof(*task)));
523+
if (!task->priv_data)
524+
return -EINVAL;
525+
K_OOPS(K_SYSCALL_MEMORY_WRITE(task->priv_data, sizeof(struct zephyr_ll_pdata)));
526+
527+
return z_impl_zephyr_ll_task_sem_alloc(task);
528+
}
529+
#include <zephyr/syscalls/zephyr_ll_task_sem_alloc_mrsh.c>
530+
531+
static inline int z_vrfy_zephyr_ll_task_sem_free(struct task *task)
532+
{
533+
if (!task)
534+
return -EINVAL;
535+
K_OOPS(K_SYSCALL_MEMORY_WRITE(task, sizeof(*task)));
536+
if (!task->priv_data)
537+
return -EINVAL;
538+
K_OOPS(K_SYSCALL_MEMORY_WRITE(task->priv_data, sizeof(struct zephyr_ll_pdata)));
539+
540+
return z_impl_zephyr_ll_task_sem_free(task);
541+
}
542+
#include <zephyr/syscalls/zephyr_ll_task_sem_free_mrsh.c>
543+
#endif
544+
#endif
545+
451546
/*
452547
* This is synchronous - after this returns the object can be destroyed!
453548
* Assertion: under Zephyr this is always called from a thread context!
@@ -505,10 +600,13 @@ static int zephyr_ll_task_free(void *data, struct task *task)
505600

506601
if (must_wait)
507602
/* Wait for up to 100 periods */
508-
k_sem_take(&pdata->sem, K_USEC(LL_TIMER_PERIOD_US * 100));
603+
k_sem_take(pdata->sem_p, K_USEC(LL_TIMER_PERIOD_US * 100));
509604

510605
/* Protect against racing with schedule_task() */
511606
zephyr_ll_lock(sch, &flags);
607+
#if CONFIG_DYNAMIC_OBJECTS
608+
zephyr_ll_task_sem_free(task);
609+
#endif
512610
task->priv_data = NULL;
513611
sof_heap_free(sch->heap, pdata);
514612
zephyr_ll_unlock(sch, &flags);
@@ -573,6 +671,7 @@ static void zephyr_ll_scheduler_free(void *data, uint32_t flags)
573671
struct k_thread *zephyr_ll_init_context(void *data, struct task *task)
574672
{
575673
struct zephyr_ll *sch = data;
674+
struct zephyr_ll_pdata *pdata = task->priv_data;
576675
int ret;
577676

578677
/*
@@ -587,7 +686,7 @@ struct k_thread *zephyr_ll_init_context(void *data, struct task *task)
587686
}
588687

589688
assert(!k_is_user_context());
590-
k_thread_access_grant(zephyr_domain_thread_tid(sch->ll_domain), sch->lock);
689+
k_thread_access_grant(zephyr_domain_thread_tid(sch->ll_domain), sch->lock, pdata->sem_p);
591690

592691
tr_dbg(&ll_tr, "granting access to lock %p for thread %p", sch->lock,
593692
zephyr_domain_thread_tid(sch->ll_domain));
@@ -698,10 +797,21 @@ int zephyr_ll_task_init(struct task *task,
698797

699798
memset(pdata, 0, sizeof(*pdata));
700799

701-
k_sem_init(&pdata->sem, 0, 1);
702-
703800
task->priv_data = pdata;
704801

802+
#if CONFIG_DYNAMIC_OBJECTS
803+
ret = zephyr_ll_task_sem_alloc(task);
804+
if (ret < 0) {
805+
sof_heap_free(heap, pdata);
806+
task->priv_data = NULL;
807+
return ret;
808+
}
809+
#else
810+
pdata->sem_p = &pdata->sem;
811+
#endif
812+
813+
k_sem_init(pdata->sem_p, 0, 1);
814+
705815
return 0;
706816
}
707817
EXPORT_SYMBOL(zephyr_ll_task_init);

zephyr/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ zephyr_library_sources_ifdef(CONFIG_SHELL
623623
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/audio/module_adapter/module/generic.h)
624624
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/fast-get.h)
625625
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/ipc/ipc_reply.h)
626+
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/schedule/ll_schedule_domain.h)
626627
zephyr_syscall_header(${SOF_SRC_PATH}/include/ipc4/handler.h)
627628
zephyr_syscall_header(include/rtos/alloc.h)
628629
zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_INTERFACE_ALLOC syscall/alloc.c)

0 commit comments

Comments
 (0)