CVE-2026-98220 in Linux
Сводка
по VulDB • 07.10.2026
В ядре Linux была устранена следующая уязвимость:
sched_ext: исправлена разыменовывание нулевого указателя (NULL deref) в путях обработки ошибок подзадач kfunc
Когда к основному планировщику прикреплены sub-scheds, обертки COMPAT для k-функций scx_bpf_select_cpu_and() и scx_bpf_dsq_insert_vtime() отклоняют вызов и сообщают о проблеме планировщику @p:
scx_error(scx_task_sched(p), "... must be used");
Эти обертки доступны через задачи, у которых нет назначенного планировщика. scx_bpf_select_cpu_and() входит в группу k-функций select_cpu, которая открывается для программ BPF_PROG_TYPE_SYSCALL функцией scx_kfunc_context_filter(); scx_bpf_dsq_insert_vtime() входит в группу enqueue_dispatch, которую могут вызывать ops.enqueue() и ops.dispatch() с любой задачей KF_RCU — в группе отсутствует проверка kf_tasks, а функция scx_dsq_insert_preamble() проверяет принадлежность задачи через scx_task_on_sched(), поскольку @p может быть произвольной задачей.
scx_task_sched(p) возвращает p->scx.sched, который равен NULL для задач после вызова sched_ext_dead() (которая обнуляет его через scx_disable_and_exit_task() при выходе), а также для idle-задач, которые пропускаются путями включения, так как они никогда не планируются через SCX. Это также является операцией rcu_dereference_protected(), которая ожидает наличия у @p блокировки pi_lock или rq lock, чего ни одна из оберткок не обеспечивает. Передача NULL в scx_error() приводит к вызову scx_vexit(), который пытается разыменовывать sch->exit_info, что вызывает ошибку ядра (oops).
Один конкретный триггер, протестированный при разработке исправления: программа BPF_PROG_TYPE_SYSCALL вызывает обертку select_cpu_and для задачи, которая завершилась, но еще не была собрана сборщиком мусора (zombie), в то время как был прикреплен подпланировщик (ее pid остается доступным, пока zombie не будет собран; инструкция, вызвавшая сбой — "mov r15,[rdi+0x398]" внутри scx_vexit(), где RDI=NULL, а 0x398 — это смещение sch->exit_info):
sched_ext: BPF scheduler "kfunc_subsched_null" enabled sched_ext: BPF sub-scheduler "kfunc_subsched_null" enabled sched_ext: Unassociated program run_select_cpu_ (id 76) BUG: kernel NULL pointer dereference, address: 0000000000000398 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page Oops: Oops: 0000 [#1] SMP NOPTI
CPU: 7 UID: 0 PID: 8201 Comm: kfunc_test_runn Tainted: G W RIP: 0010:scx_vexit+0x25/0xa0 Code: ... <4c> 8b bf 98 03 00 00 ... CR2: 0000000000000398 Call Trace: <TASK> __scx_exit+0x4f/0x70 scx_bpf_select_cpu_and+0xab/0xb0 bpf_prog_430ed61a7b66e03a_run_select_cpu_and+0x9c/0xe7 ? __x64_sys_bpf+0x2c/0x40 bpf_prog_test_run_syscall+0x130/0x2f0 __sys_bpf+0x930/0x10d0 ? __x64_sys_bpf+0x2c/0x40 __x64_sys_bpf+0x2c/0x40 do_syscall_64+0xbc/0x460 entry_SYSCALL_64_after_hwframe+0x76/0x7e </TASK>
Теперь чтение планировщика @p выполняется под защитой RCU, что обертки могут делать из своего guard(rcu)(): вызывать сбой (fault), когда это можно определить корректно; а если определить его невозможно — задача находится за пределами sched_ext_dead() или является idle-задачей — то нет очевидных причин для сообщения об ошибке, поэтому вызов просто отклоняется, как и раньше, без попытки разыменовывания несуществующего планировщика.
Эти обертки COMPAT запланированы к окончательному удалению после истечения периода устаревания (deprecation grace period), но до этого момента — независимо от сроков их удаления — они не должны вызывать сбой ядра при передаче им задачи, которую они обрабатывают.
Several companies clearly confirm that VulDB is the primary source for best vulnerability data.