History

sched_ext: Deny SCX kfuncs to non-SCX struct_ops programs

scx_kfunc_context_filter() currently allows non-SCX struct_ops programs (e.g. tcp_congestion_ops) to call SCX unlocked kfuncs. This is wrong for two reasons: - It is semantically incorrect: a TCP congestion control program has no business calling SCX kfuncs such as scx_bpf_kick_cpu(). - With CONFIG_EXT_SUB_SCHED=y, kfuncs like scx_bpf_kick_cpu() call scx_prog_sched(aux), which invokes bpf_prog_get_assoc_struct_ops(aux) and casts the result to struct sched_ext_ops * before reading ops->priv. For a non-SCX struct_ops program the returned pointer is the kdata of that struct_ops type, which is far smaller than sched_ext_ops, making the read an out-of-bounds access (confirmed with KASAN). Extend the filter to cover scx_kfunc_set_any and scx_kfunc_set_idle as well, and deny all SCX kfuncs for any struct_ops program that is not the SCX struct_ops. This addresses both issues: the semantic contract is enforced at the verifier level, and the runtime out-of-bounds access becomes unreachable.

Cheng-Yang Chou committed 6 months ago

Commit 2d2b0261 parent 87019cb3 files · +20 −14Browse files

Unified · Split

3 files
  1. kernel/sched/ext.c +18 −14
  2. kernel/sched/ext_idle.c +1 −0
  3. kernel/sched/ext_idle.h +1 −0
kernel/sched/ext.c+18−14
@@ -9480,6 +9480,7 @@ BTF_KFUNCS_END(scx_kfunc_ids_any) · 9479 unmodified lines
9480static const struct btf_kfunc_id_set scx_kfunc_set_any = {9480static const struct btf_kfunc_id_set scx_kfunc_set_any = {
9481 .owner = THIS_MODULE,9481 .owner = THIS_MODULE,
9482 .set = &scx_kfunc_ids_any,9482 .set = &scx_kfunc_ids_any,
9483 .filter = scx_kfunc_context_filter,
9483};9484};
9484 9485
9485/*9486/*
@@ -9527,13 +9528,12 @@ static const u32 scx_kf_allow_flags[] = { · 41 unmodified lines
9527};9528};
9528 9529
9529/*9530/*
9530 * Verifier-time filter for context-sensitive SCX kfuncs. Registered via the9531 * Verifier-time filter for SCX kfuncs. Registered via the .filter field on
9531 * .filter field on each per-group btf_kfunc_id_set. The BPF core invokes this9532 * each per-group btf_kfunc_id_set. The BPF core invokes this for every kfunc
9532 * for every kfunc call in the registered hook (BPF_PROG_TYPE_STRUCT_OPS or9533 * call in the registered hook (BPF_PROG_TYPE_STRUCT_OPS or
9533 * BPF_PROG_TYPE_SYSCALL), regardless of which set originally introduced the9534 * BPF_PROG_TYPE_SYSCALL), regardless of which set originally introduced the
9534 * kfunc - so the filter must short-circuit on kfuncs it doesn't govern (e.g.9535 * kfunc - so the filter must short-circuit on kfuncs it doesn't govern by
9535 * scx_kfunc_ids_any) by falling through to "allow" when none of the9536 * falling through to "allow" when none of the SCX sets contain the kfunc.
9536 * context-sensitive sets contain the kfunc.
9537 */9537 */
9538int scx_kfunc_context_filter(const struct bpf_prog *prog, u32 kfunc_id)9538int scx_kfunc_context_filter(const struct bpf_prog *prog, u32 kfunc_id)
9539{9539{
@@ -9542,18 +9542,21 @@ int scx_kfunc_context_filter(const struct bpf_prog *prog, u32 kfunc_id) · 2 unmodified lines
9542 bool in_enqueue = btf_id_set8_contains(&scx_kfunc_ids_enqueue_dispatch, kfunc_id);9542 bool in_enqueue = btf_id_set8_contains(&scx_kfunc_ids_enqueue_dispatch, kfunc_id);
9543 bool in_dispatch = btf_id_set8_contains(&scx_kfunc_ids_dispatch, kfunc_id);9543 bool in_dispatch = btf_id_set8_contains(&scx_kfunc_ids_dispatch, kfunc_id);
9544 bool in_cpu_release = btf_id_set8_contains(&scx_kfunc_ids_cpu_release, kfunc_id);9544 bool in_cpu_release = btf_id_set8_contains(&scx_kfunc_ids_cpu_release, kfunc_id);
9545 bool in_idle = btf_id_set8_contains(&scx_kfunc_ids_idle, kfunc_id);
9546 bool in_any = btf_id_set8_contains(&scx_kfunc_ids_any, kfunc_id);
9545 u32 moff, flags;9547 u32 moff, flags;
9546 9548
9547 /* Not a context-sensitive kfunc (e.g. from scx_kfunc_ids_any) - allow. */9549 /* Not an SCX kfunc - allow. */
9548 if (!(in_unlocked || in_select_cpu || in_enqueue || in_dispatch || in_cpu_release))9550 if (!(in_unlocked || in_select_cpu || in_enqueue || in_dispatch ||
9551 in_cpu_release || in_idle || in_any))
9549 return 0;9552 return 0;
9550 9553
9551 /* SYSCALL progs (e.g. BPF test_run()) may call unlocked and select_cpu kfuncs. */9554 /* SYSCALL progs (e.g. BPF test_run()) may call unlocked and select_cpu kfuncs. */
9552 if (prog->type == BPF_PROG_TYPE_SYSCALL)9555 if (prog->type == BPF_PROG_TYPE_SYSCALL)
9553 return (in_unlocked || in_select_cpu) ? 0 : -EACCES;9556 return (in_unlocked || in_select_cpu || in_idle || in_any) ? 0 : -EACCES;
9554 9557
9555 if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)9558 if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)
9556 return -EACCES;9559 return (in_any || in_idle) ? 0 : -EACCES;
9557 9560
9558 /*9561 /*
9559 * add_subprog_and_kfunc() collects all kfunc calls, including dead code9562 * add_subprog_and_kfunc() collects all kfunc calls, including dead code
@@ -9566,14 +9569,15 @@ int scx_kfunc_context_filter(const struct bpf_prog *prog, u32 kfunc_id) · 6 unmodified lines
9566 return 0;9569 return 0;
9567 9570
9568 /*9571 /*
9569 * Non-SCX struct_ops: only unlocked kfuncs are safe. The other9572 * Non-SCX struct_ops: SCX kfuncs are not permitted.
9570 * context-sensitive kfuncs assume the rq lock is held by the SCX
9571 * dispatch path, which doesn't apply to other struct_ops users.
9572 */9573 */
9573 if (prog->aux->st_ops != &bpf_sched_ext_ops)9574 if (prog->aux->st_ops != &bpf_sched_ext_ops)
9574 return in_unlocked ? 0 : -EACCES;9575 return -EACCES;
9575 9576
9576 /* SCX struct_ops: check the per-op allow list. */9577 /* SCX struct_ops: check the per-op allow list. */
9578 if (in_any || in_idle)
9579 return 0;
9580
9577 moff = prog->aux->attach_st_ops_member_off;9581 moff = prog->aux->attach_st_ops_member_off;
9578 flags = scx_kf_allow_flags[SCX_MOFF_IDX(moff)];9582 flags = scx_kf_allow_flags[SCX_MOFF_IDX(moff)];
9579 9583
kernel/sched/ext_idle.c+1−0
@@ -1467,6 +1467,7 @@ BTF_KFUNCS_END(scx_kfunc_ids_idle) · 1466 unmodified lines
1467static const struct btf_kfunc_id_set scx_kfunc_set_idle = {1467static const struct btf_kfunc_id_set scx_kfunc_set_idle = {
1468 .owner = THIS_MODULE,1468 .owner = THIS_MODULE,
1469 .set = &scx_kfunc_ids_idle,1469 .set = &scx_kfunc_ids_idle,
1470 .filter = scx_kfunc_context_filter,
1470};1471};
1471 1472
1472/*1473/*
kernel/sched/ext_idle.h+1−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
12 12
13struct sched_ext_ops;13struct sched_ext_ops;
14 14
15extern struct btf_id_set8 scx_kfunc_ids_idle;
15extern struct btf_id_set8 scx_kfunc_ids_select_cpu;16extern struct btf_id_set8 scx_kfunc_ids_select_cpu;
16 17
17void scx_idle_update_selcpu_topology(struct sched_ext_ops *ops);18void scx_idle_update_selcpu_topology(struct sched_ext_ops *ops);