Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sched_ext: Fix unsafe list iteration in process_ddsp_deferred_locals()

process_ddsp_deferred_locals() executes deferred direct dispatches to the
local DSQs of remote CPUs. It iterates the tasks on
rq->scx.ddsp_deferred_locals list, removing and calling
dispatch_to_local_dsq() on each. However, the list is protected by the rq
lock that can be dropped by dispatch_to_local_dsq() temporarily, so the list
can be modified during the iteration, which can lead to oopses and other
failures.

Fix it by popping from the head of the list instead of iterating the list.

Signed-off-by: Tejun Heo <tj@kernel.org>
Fixes: 5b26f7b920f7 ("sched_ext: Allow SCX_DSQ_LOCAL_ON for direct dispatches")
Acked-by: David Vernet <void@manifault.com>

+6 -4
+6 -4
kernel/sched/ext.c
··· 2726 2726 2727 2727 static void process_ddsp_deferred_locals(struct rq *rq) 2728 2728 { 2729 - struct task_struct *p, *tmp; 2729 + struct task_struct *p; 2730 2730 2731 2731 lockdep_assert_rq_held(rq); 2732 2732 2733 2733 /* 2734 2734 * Now that @rq can be unlocked, execute the deferred enqueueing of 2735 2735 * tasks directly dispatched to the local DSQs of other CPUs. See 2736 - * direct_dispatch(). 2736 + * direct_dispatch(). Keep popping from the head instead of using 2737 + * list_for_each_entry_safe() as dispatch_local_dsq() may unlock @rq 2738 + * temporarily. 2737 2739 */ 2738 - list_for_each_entry_safe(p, tmp, &rq->scx.ddsp_deferred_locals, 2739 - scx.dsq_list.node) { 2740 + while ((p = list_first_entry_or_null(&rq->scx.ddsp_deferred_locals, 2741 + struct task_struct, scx.dsq_list.node))) { 2740 2742 s32 ret; 2741 2743 2742 2744 list_del_init(&p->scx.dsq_list.node);