0/4 已展开

LLM 分析

Scheduler 子系统:扩展 task_setscheduler LSM 钩子,把 cpumask 透出给 LSM

系列概况

  • 标题[PATCH v4] security: Expand task_setscheduler LSM hook
  • 作者:Aaron Tomlin <atomlin@atomlin.com>
  • 版本:v4(lore 中还能看到更早的 v1/v2/v3 Message-ID)
  • 规模:单 patch,跨 10 个文件,约 25 行净增
  • 修改文件arch/mips/kernel/mips-mt-fpaff.cfs/proc/base.cinclude/linux/lsm_hook_defs.hinclude/linux/security.hkernel/cgroup/cpuset.ckernel/sched/sycalls.csecurity/commoncap.csecurity/security.csecurity/selinux/hooks.csecurity/smack/smack_lsm.c
  • 代码统计:9 个直接修改点,每个 hunk 都很小;改动集中在钩子签名 + 调用点同步
  • Message-ID20260720014156.459386-1-atomlin@atomlin.com
  • 完整性:v4 邮件正文完整;Casey Schaufler 与 Aaron Tomlin 8 月两封回复在 lore 抓取里被截断,只看到引文前缀

补丁目的

让 LSM 在 sched_setaffinity()、cpuset attach、mipsmt FP affinity 这些"把任务拉到一组 CPU"的路径里,能拿到目标 cpumask,从而做空间隔离 (spatial isolation) 策略。原钩子只能拿到 sched_attr,对 affinity 请求完全"失明"。

旧流程的问题

  • sched_setaffinity() 走 affinity 路径,但 security_task_setscheduler(p) 只有一个参数,LSM 看不到 in_mask
  • 多租户 / 实时环境无法审计或拦截把任务绑到加密核 / 隔离核的请求
  • cpuset attach 把任务拖到新 CPU 集合上时,也没有 LSM 决策点

新流程

  • 钩子扩展为 task_setscheduler(p, attr, in_mask)attrin_mask 都标 __nullable
  • 不涉及亲和性的调用点显式传 NULL(__sched_setscheduler 的 policy/param 路径、timerslack_ns_write
  • 涉及亲和性的调用点把真实 mask 透传:sched_setaffinitymipsmt_sys_sched_setaffinitycpuset_can_attachcpuset_can_fork

Patch 概览

10 个文件、9 个直接修改点,全部围绕"加两个参数并同步所有调用点",没有调度算法本身的改动。

关键实现

/* include/linux/lsm_hook_defs.h */
LSM_HOOK(int, 0, task_setscheduler, struct task_struct *p,
         const struct sched_attr *attr__nullable,
         const struct cpumask *in_mask__nullable)
/* kernel/sched/syscalls.c -- sched_setaffinity */
retval = security_task_setscheduler(p, NULL, in_mask);

/* kernel/sched/syscalls.c -- __sched_setscheduler (policy/param path) */
retval = security_task_setscheduler(p, attr, NULL);

/* kernel/cgroup/cpuset.c -- cpuset_can_attach / cpuset_can_fork */
ret = security_task_setscheduler(task, NULL, cs->effective_cpus);

security/security.c 里仍由 call_int_hook(task_setscheduler, p, attr, in_mask) 串起所有已注册 LSM;capability / SELinux / Smack 的实现都把两个新参数标 __always_unused,v4 本身不引入新策略,只是把能力递出去。

类比

把 LSM 想象成小区门禁:以前门禁只看到住户的身份证,不知道他要进哪栋楼;现在门禁同时拿到房号,可以拒绝"冒名进顶层"。多租户数据中心里,CPU 隔离核就像"机房冷通道"——调度器是前台分单员,原来只告诉门禁"某某要换工作时段",v4 之后连"工位号"也一并递给门禁。

+-----------+     +----------------------------+     +------------------+
| user task | --> | sys_sched_setaffinity      | --> | LSM decision pt. |
|           |     | security_task_setscheduler |     | sees in_mask     |
+-----------+     | (p, NULL, in_mask)         |     +------------------+
                  +----------------------------+             |
                                                              v
                                                     +------------------+
                                                     | SELinux / Smack  |
                                                     | BPF / caps       |
                                                     +------------------+

+-----------+     +-----------------------------+    +------------------+
| user task | --> | sys_sched_setscheduler       | -> | LSM decision pt. |
|           |     | security_task_setscheduler  |    | sees attr        |
+-----------+     | (p, attr, NULL)             |    +------------------+
                  +-----------------------------+            |
                                                               v
                                                     +------------------+
                                                     | SELinux / Smack  |
                                                     | BPF / caps       |
                                                     +------------------+
Hook signature change (one hook, two new __nullable params):

    task_setscheduler(p)
                  |
                  v
    task_setscheduler(p, attr, in_mask)
                         |       |
              NULL when affinity is NULL when policy/param
              not changed      only, e.g. cpuset attach

Call-site matrix (callers -> what they pass):

    sched_setaffinity         -> (p, NULL,    in_mask)
    mipsmt_sched_setaffinity  -> (p, NULL,    new_mask)
    cpuset_can_attach         -> (p, NULL,    effective_cpus)
    cpuset_can_fork           -> (p, NULL,    effective_cpus)
    __sched_setscheduler      -> (p, attr,    NULL)
    timerslack_ns_write       -> (p, NULL,    NULL)

Highlight:风险与注意点

  1. __nullable 是为 BPF verifier 设计的——任何手写 LSM 必须显式判空,否则 verifier 会拒。
  2. SELinux / Smack 当前是机械对齐签名,并没有真实策略;未来要做真实判定时,要决定是基于 task label 还是 cpuset label。
  3. capability 模块完全忽略两个新参数,仍走 cap_safe_nice()——普通 nice/sched_setscheduler 路径策略不变,只有显式 affinity 请求会被 LSM 拦截。
  4. v4 之后 Casey 与 Aaron 的 8 月回复在 lore 抓取里被截断,只看到引文前缀,无法判断是否真有质疑点;后续需到 lore 上拉完整邮件。
  5. security: 是 patch 主分类前缀,但代码改动入口在 kernel/sched/syscalls.ckernel/cgroup/cpuset.c,归类上是 LSM 基础设施改动,不影响调度器本身的公平性 / 实时性
  6. arch/mips/kernel/mips-mt-fpaff.c 是冷门路径,仅在多线程 MIPS 系统编译时需要;交叉编译矩阵要覆盖。

版本变化

  • 本 thread 只看到 v4。lore 历史显示存在 v1(2026-05-09)、v2(2026-05-09)、v3(2026-05-26)、v4(2026-07-20)。
  • 推测 v3 -> v4 关键变化:把 attrin_mask 都加 __nullable;并把 mipsmt_sys_sched_setaffinity 从只传 p 改成 (p, NULL, new_mask),让 mips FP affinity 路径也透传真实 mask。
  • 推测 v1 -> v2 -> v3 主要迭代是"逐步把更多调用点接进来 + 同步 SELinux/Smack 签名"。

一句话总结

v4 给 LSM 的 task_setscheduler 钩子加上 cpumask * 参数,让 SELinux / Smack / BPF LSM 第一次能"看见"并拦截 sched_setaffinity 的目标 CPU,补齐多租户与实时场景下的空间隔离能力。