0/3 已展开

LLM 分析

sched_ext:把 cid kfunc 收回到 SCX struct_ops 检查门内

系列概况

  • 标题: [PATCH] sched_ext: Gate cid kfuncs behind the SCX struct_ops check
  • 作者: Qiurong Fang fangqiurong@kylinos.cn
  • 版本: v1(单封 patch,无系列序号)
  • 规模: 1 patch / 2 文件 / +6 / -4- 修改文件:
    • kernel/sched/ext/cid.h
    • kernel/sched/ext/ext.c
  • Message-ID: 20260812061116.2691443-1-fangqiurong@kylinos.cn
  • 完整性: 完整,含 commit message、Fixes、SOB、AI-assisted-by

补丁目的

scx_bpf_cid_to_cpu()scx_bpf_cpu_to_cid()scx_bpf_cid_topo() 三个 kfunc 的调用限定在 SCX struct_ops 程序里。它们本应归在 scx_kfunc_ids_cid 这个 BTF id set 中,但 scx_kfunc_context_filter() 漏检该集合,导致任何 struct_ops 程序(例如 TCP 拥塞控制)都能调到它们——这违反了"必须从 sched_ext 上下文调用"的设计契约。

旧流程的问题

scx_kfunc_context_filter() 是 kfunc 调用前的门卫,先做一次身份识别:

bool in_unlocked = btf_id_set8_contains(&scx_kfunc_ids_unlocked, kfunc_id);
bool in_idle = btf_id_set8_contains(&scx_kfunc_ids_idle, kfunc_id);
bool in_any  = btf_id_set8_contains(&scx_kfunc_ids_any,  kfunc_id);
/* Not an SCX kfunc - allow. */
if (!(in_unlocked || in_init_cids || in_select_cpu ||
 in_enqueue || in_dispatch || ... || in_any))
    return 0;

凡是落在"已知 SCX 集合"之外的 kfunc,filter 视作"不是 SCX kfunc,直接放行"——这是个放行兜底。in_cid 这一支没写进白名单,所以三个 cid kfunc 被错放到兜底分支里放掉了。

新流程

scx_kfunc_ids_cid 加入已知集合,并把 || in_cid 补到 SYSCALL 旁路与 struct_ops 旁路两处:

bool in_cid = btf_id_set8_contains(&scx_kfunc_ids_cid, kfunc_id);
...
return (in_unlocked || in_select_cpu || in_idle || in_any || in_cid) ? 0 : -EACCES;
...
return (in_any || in_idle || in_cid) ? 0 : -EACCES;
...
if (in_any || in_idle || in_cid)
    return 0;

非 SCX 上下文再调用这三个 kfunc 时,会被 -EACCES 拒绝;真正的 SCX struct_ops 程序不受影响。

Patch 概览

  • kernel/sched/ext/cid.h:声明外部符号 scx_kfunc_ids_cid
  • kernel/sched/ext/ext.c:在 scx_kfunc_context_filter() 里新增 in_cid 判定,并在 4 处分支同步补上 || in_cid
              kfunc call from struct_ops prog
                          |
                          v
        +--------------------------------------+
        | btf_id_set8_contains check           |
        | in_unlocked / in_init_cids / ...     |
        | in_idle / in_any / in_cid?           |
        +--------------------------------------+
            no match? ----+---- match?
            |                 |
            v                 v
        return 0 SYSCALL prog?
        (allow)           |        |
                       yes       no
                        |        |
                        v        v
 allow id/cpu struct_ops?
 /select_cid    |        |
                  /cid yes       no
                                   |        |
                                   v        v
 per-op allow -EACCES
                              list match?
 |        |
                             yes       no
                              |        |
                              v        v return 0  -EACCES

关键实现

整个 filter 是两段式:先识别身份、再按 prog 类型分发。

/* 1) 解析身份 */
in_unlocked / in_init_cids / in_select_cpu / in_enqueue / in_dispatch /
in_cpu_release / in_idle / in_any / in_cpu_only
+ (新) in_cid/* 2) 分发 */
if (!(... || in_cid))
    return 0;                /* 兜底放行 - 但要求至少落入一个已知集合 */
if (prog->type == BPF_PROG_TYPE_SYSCALL)
    return (in_unlocked || in_select_cpu || in_idle || in_any || in_cid) ? 0 : -EACCES;
if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)
    return -EACCES;
/* SCX struct_ops per-op allow list */
return (in_any || in_idle || in_cid) ? 0 : -EACCES;

旧版的关键漏洞是"兜底放行 + 后续分支放行"两处都没把 in_cid 当 SCX 身份,于是任意 struct_ops 调用都被默许。新版把 in_cid 同时钉在兜底分支之后的对称位置。

类比

把办公楼门禁想成 filter:每张员工卡都印着身份标签(普通、机房、总裁办公区)。门口的闸机先扫标签做白名单匹配。

旧系统漏配了"总裁办公区"这条规则——卡片虽然写着"高管权限",但闸机只查普通/机房/访客三类,没匹配上就默认"你不是本楼员工、随便进"——结果隔壁公司(TCP 拥塞控制)拿着普通卡也能进总裁办公区。

补丁就是补一条规则:闸机现在认识"高管权限"了,本楼的高管照常进出,外来卡会被明确拦下(-EACCES)。

Highlight:风险与注意点

  1. 行为收紧带来的回归面:原本在非 SCX struct_ops 程序里调用这三个 kfunc 的代码会从"能跑"变成"返回 -EACCES"。建议 grep 现有 BPF sample/selftest,确认没有人在 TCP、BPF iter 等地方误用。
  2. id set 注册顺序:新增的 extern struct btf_id_set8 scx_kfunc_ids_cid; 必须在 ext.cDEFINE_BTF_ID_SET 中真实存在,否则 btf_id_set8_contains() 编译期就会失败。
  3. Sashiko 自动评审的旁支风险:memcg 的 bpf_mem_cgroup_memory_events / bpf_mem_cgroup_vm_events 缺少负数边界检查。这是 pre-existing 问题,与本 patch 无关,但仍是高质量议题。
  4. 下游已合入:Tejun 已 apply 到 sched_ext/for-7.3 分支。

版本变化

单封 patch,没有 v1→v2 演进。

一句话总结

scx_kfunc_ids_cid 加入 scx_kfunc_context_filter() 的已知 SCX 身份集合,防止三个 cid kfunc 被任意 struct_ops(如 TCP 拥塞控制)误调用,已被 Tejun apply 到 sched_ext/for-7.3。