0/2 已展开

LLM 分析

sched_ext selftest:exit 测试缺少 skeleton 打开失败检查

系列概况

  • 标题:[PATCH] selftests/sched_ext: Check skeleton open failure in exit test
  • 作者:Liang Luo luoliang@kylinos.cn
  • 版本:v1,单 patch
  • 规模:1 文件,+1 行
  • 修改文件tools/testing/selftests/sched_ext/exit.c
  • 代码统计:1 insertion(+), 0 deletion(-)
  • Message-ID20260807013912.3178229-1-luoliang@kylinos.cn
  • 完整性:完整。patch + maintainer 接受回执(Tejun Heo 已合入 sched_ext/for-7.3

补丁目的

sched_ext 提供了一组 BPF selftests,每个测试在 run() 中调用对应的 xxx__open() 打开 libbpf 生成的 skeleton。

exit.c 是其中用来验证「BPF 程序运行中触发用户态 exit_point 路径」的测试,但它没有像其它 selftest 那样在 exit__open() 返回 NULL 时立刻 fail。一旦 skeleton 打开失败,下一行 SCX_ENUM_INIT(skel) 就会通过 skel->rodata 解引用空指针,造成 NULL pointer dereference。

本 patch 的目标:在 exit__open() 返回 NULL 时使用 SCX_FAIL_IF(!skel, "Failed to open") 显式失败,使错误信息更清晰,并消除空指针解引用风险。

旧流程的问题

[run() in exit.c, before fix]

 skel = exit__open();
          |
          v   +---------------------------+
   | skel == NULL ?             |
   +---------------------------+
          |
          | (not checked -- bug)
          v
   SCX_ENUM_INIT(skel);
 |
          v
   SCX_ENUM_SET(skel->rodata, ...)
          |
          v
   *** NULL pointer dereference ***
  • exit__open() 在 libbpf 分配 skeleton 失败、ELF 解析失败等情况下会返回 NULL
  • SCX_ENUM_INIT(skel) 宏展开后访问 skel->rodata,对空指针解引用。
  • 后果是测试进程直接崩溃(segfault),而不是输出可定位的错误信息;同时 selftest 框架无法把这个失败归类为「打开失败」。
  • 其它兄弟 selftest(如 enq_lastenq_select_cpu_fails 等)都已经有 SCX_FAIL_IF(!skel, "Failed to open skel"),只有 exit.c 漏掉了。

新流程

[run() in exit.c, after fix]

   skel = exit__open();
          |
          v
   +---------------------------+
   | skel == NULL ?             |
   +---------------------------+
        |            |
   NULL |            | non-NULL
        v            v
   +-----------+   SCX_ENUM_INIT(skel)
   | SCX_FAIL  |   skel->rodata->exit_point = tc
   | _IF(!skel)|   SCX_FAIL_IF(exit__load(skel), ...)
   +-----------+
 |
 v
   return SCX_TEST_STATUS_FAIL
   ("Failed to open")

失败路径上:exit__open() 返回 NULL -> SCX_FAIL_IF 立刻把测试标记为 FAILED 并打印 "Failed to open",避免后续空指针解引用。

关键实现

新增的就是一行:

SCX_FAIL_IF(!skel, "Failed to open");

SCX_FAIL_IF 是 sched_ext selftest 框架提供的宏,语义是:当条件为真时,打印第二条参数给出的信息,把测试状态置为 SCX_TEST_STATUS_FAILreturn。它正是 selftest 里「提前 bail」的惯用 idiom,与其它 selftest 的写法保持一致。

类比

xxx__open() 想成去图书馆借一本特定编号的书:

  • 其它 selftest:柜台服务员会先看手里是不是真的拿到书(SCX_FAIL_IF(!skel, ...)),拿到空册子就直接告诉你「借不到」,然后让你离开。
  • 旧版 exit.c:柜台递过来一个空册子,你没检查就翻到第 5 页写笔记(skel->rodata->exit_point = tc),结果翻页时书页是空的——页码指向虚无,手指戳穿了「空气」,当场崩溃(NULL deref)。
  • 修复后:你在翻页前先看一眼,发现是空册子,立刻说「借不到」,柜台记录这次失败,流程干净结束。

Highlight:风险与注意点

  • 看似一行 but 关键exit__open() 失败概率不高,但只要发生就是进程崩溃;修复后变成受控失败,CI 上更易诊断。
  • 同质化要求:sched_ext selftests 数量在增长,每个新 selftest 都需要遵循 SCX_FAIL_IF(!skel, ...) 的统一风格。可以考虑把「打开失败检查」写进一个公共 helper 或断言宏,从源头杜绝漏写。
  • 后续观察点SCX_ENUM_INIT(skel) 自身是否也会被调用到?它访问 skel->rodata,因此修复必须放在它之前——本次 patch 顺序正确。
  • Fixes tag 链向原始 selftest 引入 commita5db7817af78 ("sched_ext: Add selftests"),说明这是从一开始引入时就遗留的疏漏,对 stable 回溯有价值。

版本变化

本线程只有 v1 一次提交,maintainer Tejun Heo 直接回复「Applied to sched_ext/for-7.3.」,没有 v2 也没有 review 争论,属于干净利落的一行修复。

一句话总结

sched_ext selftest 中 exit.c 漏掉了对 exit__open() 返回值的检查,导致 skeleton 打开失败时会触发空指针解引用;本 patch 用一行 SCX_FAIL_IF(!skel, "Failed to open") 与其它 selftest 对齐,已被 Tejun 合入 sched_ext/for-7.3