Linux内核中损坏的KRETPROBES和OPTIMIZER简史
在LKRG开发过程中,我发现:
- KRETPROBES自内核5.8起损坏(将在即将发布的内核中修复)
- OPTIMIZER自内核5.5起未能充分执行任务
KPROBES和FTRACE简介
Linux内核提供两个出色的钩子框架 - KPROBES和FTRACE。KPROBES更早且经典 - 于2.6.9版本(2004年10月)引入。FTRACE是较新的接口,与K*PROBES相比可能具有更小的开销。
各种类型的K*PROBES包括:
- KPROBES - 可放置在内核中的几乎任何指令上
- JPROBES - 使用KPROBES实现,但自2017年起已弃用
- KRETPROBES - 称为"返回探针",允许在挂钩函数的入口和返回路径上轻松执行用户自己的例程
FTRACE最初在内核2.6.27中引入,其工作原理完全不同,主要基于检测每个编译函数(注入"长NOP"指令)。
Linux内核运行时防护(LKRG)
LKRG对Linux内核执行运行时完整性检查,并检测针对内核的各种漏洞利用。为实现此功能,LKRG必须在内核中放置各种钩子,使用KRETPROBES来满足该要求。
LKRG在FTRACE检测函数上的KPROBE
当函数被FTRACE检测并且有人在其上注册KPROBES时,Linux内核在这种情况下使用特殊类型的KPROBES,称为"基于FTRACE的KPROBES"。
OPTIMIZER
Linux内核开发人员更进一步,积极"优化"所有K*PROBES以使用FTRACE代替。主要原因是性能 - FTRACE的开销更小。
LKRG报告误报
ALT Linux的Vitaly Chikunov报告说,当他运行FTRACE压力测试器时,LKRG报告.text部分损坏。
第一个问题 - KRETPROBES损坏
从内核5.8开始,所有非优化的KRETPROBES都不工作。根本原因来自提交0d00449c7a28a1514595630735df383dec606812,后来由提交8edd7e37aed8b9df938a63f0b0259c70569ce3d2修改。
问题逻辑:
|
|
本质上,exc_int3()调用nmi_enter(),而pre_handler_kretprobe()在调用任何已注册的KPROBE之前通过in_nmi()调用验证是否不在NMI中。
第二个问题 - OPTIMIZER未能充分执行工作
在生成vmlinux二进制文件时,GCC使用INT3操作码在挂钩函数末尾生成填充:
|
|
OPTIMIZER逻辑在此失败:
|
|
然而,这里的情况并非如此。INT3_INSN_OPCODE作为填充放置在函数末尾。
原因是链接器现在使用INT3作为默认填充,如提交7705dc8557973d8ad8f10840f61d8ec805695e9e所示。
通过LKRG开发工作,我们帮助识别并修复了Linux内核中的两个有趣问题。