0/10 已展开

LLM 分析

sched_ext: Follow-up fixes and missing cap enforcement

系列基线信息

字段
标题sched_ext: Follow-up fixes and missing cap enforcement
作者Tejun Heo
版本单次提交,无版本号标注
规模5 patches,13 文件,+181 / -64 行
基线分支sched_ext/for-7.3 (c509d8107026)
Message-ID20260724182125.985061-1-tj@kernel.org
来源sched-ext 频道

明确目的

本 patch 系列解决两类问题:

  1. 工具重启循环 bug:sched_ext 用户态工具在收到内核 RESTART 退出码时自动重启,但没有检查 exit_req 标志。若用户在重启等待期间请求退出,该请求被忽略,工具陷入无限重启循环。

  2. 子调度器 capability 缺口:近期子调度器(sub-scheduler)引入了分层 capability 机制,但部分 cid-addressed 操作缺少 cap 检查,形成权限漏洞——无任何 cap 的子调度器可对目标 CPU 发 IPI(reenq)或操控 cpufreq(cidperf_set)。本系列逐一堵住这些漏洞,并为每次拒绝添加可观测的事件计数器。


遍历代码

Patch 1/5 — 工具重启循环修复

所有 sched_ext 工具的主循环在内核退出调度器后检查退出码:

// 修复前:无条件重启
if (UEI_ECODE_RESTART(ecode))
    goto restart;

// 修复后:先看用户是否请求了退出
if (!exit_req && UEI_ECODE_RESTART(ecode))
    goto restart;

scx_userland 需要额外修复:

  • 主循环缺少 UEI_EXITED() 检查,导致从不感知内核侧退出;
  • exit_req 同时充当统计打印线程的停止信号,重启时被重置为 0,造成退出请求丢失。
  • 新增 stats_stop 专用于打印线程,exit_req 只表达退出意图且不再重置。

Patch 2/5 — 本地 DSQ reenq 拦截

scx_bpf_dsq_reenq()SCX_DSQ_LOCAL_ON 目标会在目标 CPU 上调度延迟 reenq 工作,发送 IPI。此前无 cap 检查。

修复:在 schedule_dsq_reenq() 入口处检查 SCX_CAP_BASE

if (unlikely(scx_missing_caps(sch, cpu_of(rq), SCX_CAP_BASE))) {
    __scx_add_event(sch, SCX_EV_SUB_REENQ_DENIED, 1);
    return;
}

检查无锁(lockless)——cap 撤销后的瞬态漏过无害,误拒不可能(调用者已看到所有权 → 检查也能看到)。

Patch 3/5 — kick 拒绝计数

kick_one_cpu()kick_one_cpu_if_idle() 已在 SCX_CAP_BASE 门后静默跳过 kick,但拒绝不可观测。

修复:提取 kickable 变量,当 CPU 可 kick 但缺少 cap 时计入 SCX_EV_SUB_KICK_DENIED

kickable = (cpu_online(cpu) || cpu == cpu_of(this_rq)) &&
           !sched_class_above(cur_class, &ext_sched_class);

if (kickable && !scx_missing_caps(pcpu->sch, cpu, SCX_CAP_BASE))
    // 正常 kick
else if (kickable)
    __scx_add_event(pcpu->sch, SCX_EV_SUB_KICK_DENIED, 1);

与 reenq 拒绝事件形成互补:reenq 被丢弃前计数,kick 被跳过后计数,preempt 降级已有 SCX_EV_SUB_PREEMPT_DENIED

Patch 4/5 — scx_cpuperf_set() 重构

scx_bpf_cpuperf_set() 中的 cpuperf 写入逻辑抽取为 scx_cpuperf_set(sch, cpu, perf) 返回 0/-errno。嵌套验证改为早返回。

无功能变更,为 Patch 5 做准备——后续需要在此函数内加 cap 门并报告结果。

Patch 5/5 — SCX_CAP_PERF 门控 cidperf_set

新增 SCX_CAP_PERF(独立于 SCX_CAP_BASE),仅 root 持有默认。在 scx_cpuperf_set() 内检查:

if (likely(!scx_missing_caps(sch, cpu, SCX_CAP_PERF))) {
    rq->scx.cpuperf_target = perf;
    cpufreq_update_util(rq, 0);
    ret = 0;
} else {
    __scx_add_event(sch, SCX_EV_SUB_CIDPERF_DENIED, 1);
    ret = -EACCES;
}

关键设计:

  • 检查在目标 rq 锁下进行,ecaps 更新也在同一锁下折叠,因此是权威的——撤销生效后写入不可能到达。
  • 新增 scx_bpf_cidperf_set___v2() 返回 s32(0/-EACCES/-errno),旧 void 版本保留为兼容包装。
  • tools 层 compat wrapper 优先调用 v2,旧内核总是应用写入并返回 0。
  • scx_qmap 的子调度器 grant/revoke 现在附带 SCX_CAP_PERF,确保 cpufreq demo 继续工作。

ASCII 流程图

                     Sub-scheduler capability enforcement
                     ====================================

  BPF kfunc call          cap check point              outcome
  ──────────────          ─────────────────            ──────

  scx_bpf_dsq_reenq() ──> SCX_CAP_BASE ────────────> reenq: proceed
     (LOCAL_ON target)      (lockless)                  or: drop + EV_SUB_REENQ_DENIED

  scx_bpf_kick_cid()  ──> SCX_CAP_BASE ────────────> kick: send IPI
     (kick_one_cpu)          (lockless)                  or: skip + EV_SUB_KICK_DENIED
                                                        or: degrade + EV_SUB_PREEMPT_DENIED

  scx_bpf_cidperf_set() ─> SCX_CAP_PERF ───────────> write cpuperf_target
     (cidperf_set___v2)      (under rq lock)             or: deny + EV_SUB_CIDPERF_DENIED
                                                        return -EACCES

  ┌─────────────────────────────────────────────────────────────┐
  │  cap hierarchy (independent axes)                           │
  │                                                             │
  │  SCX_CAP_BASE ── queue access (enqueue, reenq, kick)       │
  │  SCX_CAP_PERF ── hardware control (cpufreq target)         │
  │  SCX_CAP_ENQ / PREEMPT / ENQ_IMMED ── finer-grained ops   │
  │                                                             │
  │  PERF ≠ BASE; parent may grant scheduling without freq      │
  └─────────────────────────────────────────────────────────────┘
  Tools restart-loop fix (before → after)

  BEFORE:                         AFTER:
  ┌──────────────┐               ┌──────────────┐
  │ kernel exits │               │ kernel exits │
  │ with RESTART │               │ with RESTART │
  └──────┬───────┘               └──────┬───────┘
         │                               │
         ▼                               ▼
  ┌──────────────┐               ┌──────────────────┐
  │ always goto  │               │ check exit_req?  │
  │ restart      │               │  NO → goto rest. │
  └──────┬───────┘               │  YES → return 0  │
         │                       └──────┬───────────┘
         ▼                               │
  ┌──────────────┐                       ▼
  │ infinite     │               ┌──────────────┐
  │ restart loop │               │ clean exit   │
  │ (bug!)       │               └──────────────┘
  └──────────────┘

概念类比

把 sub-scheduler capability 机制比作公司部门权限体系

  • SCX_CAP_BASE = 门禁卡:有卡才能进入办公楼(CPU),发送通知(IPI/kick)或把文件放到某人桌上(reenq 到本地 DSQ)。没有门禁卡,保安会在门口拦住你,并在登记簿上记一笔(事件计数器)。

  • SCX_CAP_PERF = 空调遥控器:进入楼不等于可以随意调空调温度。公司可能把门禁卡给了外包团队(允许他们在楼里工作),但空调遥控器只给物业(root)。两把钥匙互不包含——这正是 PERF ≠ BASE 的设计。

  • lockless 检查 ≈ 门禁卡的即时刷卡验证:偶尔系统刚注销你的卡、门还没锁上你滑过去了——无关紧要(无害瞬态)。但绝不会出现你有卡却刷卡失败的情况(误拒不可能)。

  • rq 锁下的检查 ≈ 空调遥控器操作必须在机房里完成:谁在机房里、谁有遥控器,都在同一锁下确认——不可能出现遥控器已被收回但你还能调温度的情况。

  • v2 kfunc ≈ 遥控器给出操作反馈:旧遥控器按了就算,不管有没有生效;新遥控器会告诉你"操作成功"或"权限不足"。


Highlight 突出问题

  1. kernel-doc 断裂:sashiko-bot 发现 Patch 4 重构后,kernel-doc 注释落在了 scx_cpuperf_set() 内部函数上而非 scx_bpf_cpuperf_set() kfunc 上。Tejun 确认将在合入时修正——若不修正,API 文档将指向错误函数。

  2. SCX_CAP_PERF 向后兼容:sashiko-bot 提出 scx_qmap 在 grant/revoke 中无条件传入 SCX_CAP_PERF,旧内核不知此 bit 可能拒绝。Tejun 回应:子调度器 cap 接口仍在初始开发期,无跨版本兼容承诺,in-tree 工具跟随 in-tree 内核。但外部/下游调度器若移植此代码需注意——旧内核会拒绝未知的 cap bit。

  3. lockless 检查的理论边界:reenq/kick 的 cap 检查无锁,依赖"漏过无害、误拒不可能"论证。该论证假设 cap 撤销的可见性与调用者对 CPU 所有权的可见性一致——如果未来 cap 传播路径变化,需重新验证此假设。

  4. SCX_EV_SUB_KICK_DENIED vs SCX_EV_SUB_PREEMPT_DENIED 的语义区分需使用者理解:前者是 kick 被完全跳过,后者是 kick 送达但 preempt 降级为普通 reschedule。

  5. cidperf_set___v2 compat wrapper 的生命周期:声明在 v7.6 后移除旧版包装——需跟踪时间线确保届时完成清理。


版本演进

本次为首次发布,无版本演进。


与其他相关 patch 系列的关联

本系列紧跟近期 sub-scheduler capability 框架的引入(SCX_CAP_BASESCX_CAP_ENQSCX_CAP_PREEMPT 等),补上遗漏的检查点并增加 cpufreq 控制的独立权限轴。目标分支 sched_ext/for-7.3 表明这些改动计划合入 Linux 7.3 的 sched_ext 子系统。


一句话总结

堵住 sub-scheduler 的三个权限漏洞(reenq IPI、kick 计数、cidperf cpufreq),修复工具重启循环 bug,新增 SCX_CAP_PERF 作为独立于 BASE 的 cpufreq 权限轴,并通过版本化 kfunc 同步报告拒绝结果。