sched discussion
[PATCH] sched/topology: don't claim sched_domain_shared twice on the same domain
LLM 分析
sched/topology:避免 sched_domain_shared 在同一 domain 上被重复 claim
系列概况
- 标题: [PATCH] sched/topology: don't claim sched_domain_shared twice on the same domain
- 作者: Breno Leitao leitao@debian.org
- 版本: v1,单封 patch
- 规模: 1 file changed, 7 insertions(+), 1 deletion(-)
- 修改文件: kernel/sched/topology.c
- 代码统计: +7 / -1
- Message-ID: 20260810-b4-sched_shared_leak-v1-1-3eacd249d0a3@debian.org
- 完整性: 完整,包含 commit message、修改 hunk、签名行、复现步骤与对比数据
补丁目的
修复 build_sched_domains() 在某些拓扑下导致 sched_domain_shared 对象泄漏的问题。
当 asymmetric capacity 路径与 LLC share 路径最终落在同一个 sched_domain 上时,
init_sched_domain_shared() 会被调用两次:第二次直接覆盖 sd->shared 指针却不释放
第一次申请的对象,refcount 与 nr_busy_cpus 相等但没有任何指针指向它,造成泄漏。
commit message 明确 Fixes: 9e005ed21152 ("sched/topology: Allow multiple domains to claim sched_domain_shared"),
说明漏洞由“允许多个 domain 共享同一对象”这一改动引入。
旧流程的问题
原代码片段大致是:
/* 假设 asym 路径已经处理过 */
if (sd->flags & SD_SHARE_LLC) {
/* unconditionally */
init_sched_domain_shared(&d, sd, SD_SHARE_LLC);
}
在 arm64 系统只有一个 MC 域、同时承载 SD_SHARE_LLC 和 SD_ASYM_CPUCAPACITY_FULL
时,asym 路径先 claim 一次;接着 SD_SHARE_LLC 分支又再次调用
init_sched_domain_shared(),把 sd->shared 覆盖到新对象。被覆盖的旧对象
refcount 等于在线 CPU 数,但 claim_allocations() 看到非零 refcount 就把 per-CPU
slot 清空,__sds_free() 因此也不会回收,泄漏发生。kmemleak 每启动或每次
build_sched_domains() 重建都会报告一个 32 字节的对象。
新流程
在 SD_SHARE_LLC 分支加上守卫:
if (!sd->shared)
init_sched_domain_shared(&d, sd, SD_SHARE_LLC);
sd_init() 在构建每个 sched_domain 时把 sd->shared 清零,所以这里非 NULL 就意味着
asym 路径已经 claim 了同一个 domain,直接跳过第二次 claim。Cache-aware 调度仍能通过
sd_llc 找到同一个共享对象,覆盖区间不变。
Patch 概览
仅 kernel/sched/topology.c 一个 hunk。新增 7 行(含一段注释)+ 守卫条件,删除 1 行
无条件调用。
关键实现
- 触发条件:
SD_SHARE_LLC与SD_ASYM_CPUCAPACITY_FULL落到同一个 sched_domain; - 守卫:
sd_init()把sd->shared初始化为 NULL,asym 路径 claim 后置为非 NULL; if (!sd->shared)等价于“asym 路径没碰过这个 domain”;- 注释明确说明:第二次 claim 会覆盖指针并泄漏第一次的引用。
类比
想象一张共享办公桌:asym 路径先放上一叠资料并把“在用”小牌子翻过来;接着 LLC 路径
进场,看到桌上已经有资料就直接说“不用再摆一份”。原代码相当于后者把前者的资料扔掉
重新摆了一叠,旧资料没人认领就堆在角落,kmemleak 看见的就是这堆被遗忘的资料。
build_sched_domains()
|
+--> asym path: pick lowest SD_ASYM_CPUCAPACITY_FULL ancestor
| |
| +--> init_sched_domain_shared(d, sd, SD_ASYM_CPUCAPACITY)
| sd->shared = blobA (refcount = nr_cpus)
|
+--> LLC path: pick topmost SD_SHARE_LLC
|
+--> (OLD) unconditionally init_sched_domain_shared(...)
| sd->shared = blobB (refcount = nr_cpus) <-- blobA leaked
|
+--> (NEW) if (!sd->shared) init_sched_domain_shared(...)
blobA kept, sd->shared unchanged, no leak
Highlight:风险与注意点
claim_allocations()故意不释放 refcount 非零的对象,这是设计预期;但 refcount 恰好
等于 nr_busy_cpus、alloc_flags 为SD_ASYM_CPUCAPACITY的对象大概率就是泄漏信号;- 复现仅在 arm64 virtme-ng + 两档 capacity(0x400 / 0x200)下完成,其它拓扑(x86 DIE/MC
拆分、NUMA 不对称等)是否同病需要交叉验证; - 修复针对 9e005ed21152 引入的“允许多个 domain 共享同一对象”语义,需要确认其它可能
重复 claim 的路径(例如 NUMA 路径或后续新增的 share flag)是否还有遗漏; - hunk context 在 v7.2-rc7 与当前 linux-next 完全一致,作者已确认干净 apply。
版本变化
单封 v1,没有 v1→v2 演进。
一句话总结
在 LLC share claim 之前先用 if (!sd->shared) 检查 asym 路径是否已经占用同一 domain,
避免重复 claim 覆盖旧对象并泄漏 32 字节的 sched_domain_shared。