sched-ext discussion
[PATCH bpf-next v3 0/2] bpf: require an owned cpumask for bpf_cpumask_populate()
LLM 分析
BPF cpumask:阻止借用掩码被当作可写目标
系列概况
- 标题:
[PATCH bpf-next v3 0/2] bpf: require an owned cpumask for bpf_cpumask_populate() - 作者:Nicholas Dudar main.kalliope@gmail.com
- 版本:v3(v1/v2 为发往
security@kernel.org的私密报告) - 规模:2 个实际 patch
- 修改文件:5 个(BPF cpumask kfunc、sched_ext 兼容声明及 BPF selftests)
- 代码统计:
+29/-10 - Message-ID:
20260709182800.2037938-1-main.kalliope@gmail.com - 完整性:完整;cover 的
0/2与 2 个 patch 一致,原始线程共 13 封邮件,无缺失警告及 tip-bot2 污染
一句话总结:把
bpf_cpumask_populate()的目标参数收紧为“拥有且可写”的bpf_cpumask,让 verifier 在程序加载期拒绝修改借来的只读 cpumask。
补丁目的
bpf_cpumask_populate() 会用 bitmap_copy() 改写目标,但旧签名却是
struct cpumask *。该类型也用于只读、借用的掩码,例如
scx_bpf_get_online_cpumask() 的返回值,因此 verifier 无法从 kfunc
签名判断调用者是否拥有目标,错误地允许写入全局或其他对象中的掩码。
本系列把写操作统一到 struct bpf_cpumask *:查询 kfunc 仍接收
const struct cpumask *,只有修改操作要求 owned BPF cpumask。
旧流程的问题
旧签名只表达“这是 cpumask”,没有表达“这是调用者拥有的可写对象”。
BPF C 代码即使拿到借用指针,也能将其传给 populate;调用通过 verifier
后,bitmap_copy() 会真实覆盖底层位图,破坏只读契约及共享状态。
问题源自 950ad93df2fc ("bpf: add kfunc for populating cpumask bits")。
它不是复制长度或对齐检查错误,而是目标指针的 BTF 类型过宽。
新流程
BPF 程序调用 bpf_cpumask_populate(dst, src, size)
|
v
verifier 检查 dst 的 BTF 类型
/
owned struct bpf_cpumask * borrowed struct cpumask *
| |
v v
允许加载;运行时复制 加载期拒绝:类型不匹配
|
v
&dst->cpumask <- src bits
旧:任何满足 struct cpumask * 的借用指针都可能成为写目标。
新:只有 struct bpf_cpumask * 能进入写路径;只读查询接口不变。
Patch 概览
| Patch | 核心改动 |
|---|---|
| 1/2 | 收紧 kfunc 与 sched_ext 兼容声明,并从内嵌的 cpumask->cpumask 取得位图 |
| 2/2 | 同步 selftest 声明、删除旧强转,并新增借用目标必须被 verifier 拒绝的失败测试 |
关键实现
-
类型即权限边界
__bpf_kfunc int bpf_cpumask_populate(struct bpf_cpumask *cpumask, void *src, size_t src__sz) { /* 原有长度、对齐检查保持不变 */ bitmap_copy(cpumask_bits(&cpumask->cpumask), src, nr_cpu_ids); return 0; }变化的核心不是复制算法,而是 kfunc 元数据向 verifier 暴露的参数类型。
struct bpf_cpumask封装真实struct cpumask,因此运行时多取一层成员。 -
失败测试验证 BTF,而非 C 强转
新测试把借用的
task->cpus_ptr显式强转为
struct bpf_cpumask *。C 强转只能让编译器接受代码,不能改变寄存器
实际携带的 BTF 来源;verifier 应报告 R1 是STRUCT cpumask,而期望
STRUCT bpf_cpumask。这证明调用者不能靠强转绕过所有权边界。 -
成功与错误路径保持原语义
现有小缓冲区
-EACCES、不支持架构上的非对齐源-EINVAL、正常复制
等测试只删除多余的(struct cpumask *)强转。没有新增锁、RCU 临界区
或运行时分配,热路径成本基本不变。 -
sched_ext 兼容声明同步
tools/sched_ext/include/scx/compat.bpf.h的弱符号签名同时更新,避免
兼容宏继续向 BPF 编译器描述旧类型;注释说明该 compat 宏计划在
v6.19 删除。
类比
旧接口像一台只检查“是不是房卡”的写卡机:访客卡虽然只该查看房间,
也能被拿去修改门锁权限。新接口要求“管理员卡”;C 强转只是给卡贴上
管理员标签,门禁 verifier 仍会读取芯片里的真实卡种并拒绝访客卡。
Highlight:风险与注意点
- 这是有意的 BPF kfunc 源码接口收紧。树外程序若把普通或借用
cpumask *传给 populate,将在编译或 verifier 加载阶段失败;真正拥有
bpf_cpumask的调用只需移除旧强转。 - selftest 使用
task->cpus_ptr代表借用对象,没有直接调用最初场景中的
sched_ext online/idle cpumask kfunc。Emil 指出这一覆盖差异,但认为缺少
sched_ext selftest 基础设施时,当前 tracepoint 测试足以验证禁止
cpumask -> bpf_cpumask转换的核心规则。 - Sashiko 对 1/2 单独扫描时提示 selftests 未同步;2/2 已完整处理,因此
这是按单 patch 观察产生的提醒,不是系列遗留缺陷。 - Emil 对两个 patch 均给出
Reviewed-by;Kumar 认为不会破坏预期用法;
Tejun 给出Acked-by。但 Tejun 的邮件晚于 patchwork 的合入通知,不能
将该 Ack 描述成合入前已具备的条件。 - patchwork-bot 通知系列已由 Kumar 合入
bpf/bpf-next.git,对应
63a704c8b369与5aaad4d9fe9f。当前本地仓库不含这两个对象,且
git.kernel.org 的补丁页受反爬挑战阻挡,因此这里只确认了原始 v3 diff、
邮件评审和合入通知,未对最终对象做逐字节复核。
版本变化
公开线程只有 v3。cover 说明 v1/v2 是私密安全报告;v3 将修复与测试拆成
两个 patch,并把目标树从 bpf 改为 bpf-next。不可见的私密版本内容
无法独立比较,除 cover 明示的两点外不应推断更多差异。
一句话总结
该系列用精确的 BTF 参数类型把“可读 cpumask”和“拥有且可写的
bpf_cpumask”分开,在 verifier 加载期堵住借用掩码被覆盖的路径,并用
正反 selftest 固化这一所有权规则,运行时复制逻辑及同步语义不变。