Linux v5.9 安全特性深度解析
Linux v5.9 于2020年10月发布。以下是我发现的各种有趣安全特性的总结:
seccomp 用户通知文件描述符注入
Sargun Dhillon 添加了 SECCOMP_RET_USER_NOTIF 过滤器使用 SECCOMP_IOCTL_NOTIF_ADDFD 向目标进程注入文件描述符的能力。这使得容器管理器能够完全模拟如 open() 和 connect() 这样的系统调用,在这些调用成功执行后,实际的文件描述符将可用。在此过程中,我修复了几个错误并重构了文件描述符接收代码。
使用 Clang 零初始化堆栈变量
当 Alexander Potapenko 落地支持 Clang 的自动变量初始化时,它使用了一个字节模式,旨在在内核崩溃中真正突出显示。现在他添加了通过 CONFIG_INIT_STACK_ALL_ZERO 进行零初始化的支持,除了实际上更快之外,还有一些行为上的好处。“与模式初始化相比,零初始化触发现有错误的机会更低,为字符串、指针、索引和大小提供了安全的默认值。”与模式初始化一样,此功能阻止了整个类别的未初始化堆栈变量缺陷。
通用系统调用入口/出口例程
Thomas Gleixner 创建了与架构无关的代码来处理系统调用入口和出口,因为内核在系统调用入口和出口期间的大部分工作是相同的。没有必要在每个架构中重复这一点,并且单独实现意味着错误(或功能)可能只在少数架构中得到修复(或实现)。这意味着像 seccomp 这样的功能变得更容易构建,因为它不再需要每个架构的实现。目前只有 x86 切换到通用例程。
SLAB kfree() 加固
为了达到与 SLUB 堆分配器的 CONFIG_SLAB_FREELIST_HARDENED 功能对等,我在 SLAB 分配器中添加了简单的双重释放检测和跨缓存释放检测能力。这应该防止一类类型混淆错误影响使用 SLAB 的内核。(大多数发行版内核使用 SLUB,但一些较小的设备更喜欢稍微更紧凑的 SLAB,因此这种加固主要针对这些系统。)
新的 CAP_CHECKPOINT_RESTORE 能力
Adrian Reber 添加了新的 CAP_CHECKPOINT_RESTORE 能力,将此功能从 CAP_SYS_ADMIN 中分离出来。内核正确检查点和恢复进程的需求(例如用于在容器之间移动进程)不断增长,并且很明显,其安全影响低于 CAP_SYS_ADMIN,但与其他能力不同。使用此能力现在是更改 /proc/self/exe 等操作的首选方法。
debugfs 启动时可见性限制
Peter Enderborg 添加了 debugfs 启动参数来控制内核调试文件系统的可见性。debugfs 的内容仍然是向攻击者暴露敏感信息的常见区域。虽然通过取消设置 CONFIG_DEBUG_FS 可以有效地实现这一点,但对于需要一组内核配置的系统构建者(例如发行版内核)来说,这不是一个很好的方法,因此现在可以在启动时禁用它。
更多 seccomp 架构支持
Michael Karcher 实现了 SuperH seccomp 钩子,Guo Ren 实现了 C-SKY seccomp 钩子,Max Filippov 实现了 xtensa seccomp 钩子。每个都包括对内核自测试中 seccomp 回归测试套件的重要更新。
RISC-V 的堆栈保护支持
Guo Ren 为 RISC-V 实现了 -fstack-protector(和 -fstack-protector-strong)支持。这是初始的全局金丝雀支持,而支持每任务金丝雀的 GCC 补丁正在完成(类似于为 arm64 完成的每任务金丝雀)。这意味着在此架构上,几乎所有的堆栈帧写入溢出对攻击者不再有用。很高兴看到这最终落地到 RISC-V,它正迅速接近内核中其他主要架构的功能对等。
新的 tasklet API
Romain Perier 和 Allen Pais 引入了一个新的 tasklet API,使其使用更安全。就像之前完成的 timer_list 重构工作一样,tasklet API 也是通过线性堆覆盖进行简单函数指针和第一个参数控制利用的潜在来源。由于在内核中使用较少,攻击面较小,但它是相同的弱设计,使其成为替换的合理选择。虽然 tasklet API 的使用被认为已弃用(被线程化 IRQ 替换),但这并不总是简单的机械重构,因此旧 API 仍然需要重构(因为在大多数情况下可以机械地完成)。
x86 FSGSBASE 实现
Sasha Levin、Andy Lutomirski、Chang S. Bae、Andi Kleen、Tony Luck、Thomas Gleixner 和其他人落地了期待已久的 FSGSBASE 系列。这提供了任务切换性能改进,同时保护内核免受模块意外(或恶意)尝试直接使用这些功能的影响(这暴露了一个无特权的直接内核访问漏洞)。
过滤 x86 MSR 写入
虽然长期以来人们都理解从用户空间写入 CPU 模型特定寄存器(MSR)是一个坏主意,但它一直为诸如 MSR_IA32_ENERGY_PERF_BIAS 之类的事情启用。Boris Petkov 认为已经足够了,现在默认启用日志记录和内核污染(TAINT_CPU_OUT_OF_SPEC),并提供了一种在运行时禁用 MSR 写入的方法。(但是,由于这是由普通模块参数控制的,并且 root 用户可以重新打开写入,我继续建议人们使用 CONFIG_X86_MSR=n 构建。)期望在未来内核中完全删除用户空间 MSR 写入。
移除 uninitialized_var() 宏
我进行了全树更改以移除 uninitialized_var() 宏,该宏曾用于消除编译器警告。此宏的基本原理一开始就很弱(“编译器报告了一个明显已初始化的未初始化变量”),因为它主要是掩盖编译器错误。然而,它在内核中创建了一个更脆弱的情况,因为现在这样的使用实际上可以禁用自动堆栈变量初始化,并掩盖合法的“未使用变量”警告。正确的解决方案是初始化编译器警告的变量。
函数指针转换移除
Oscar Carter 已开始从内核中移除函数指针转换,以努力使内核能够使用 -Wcast-function-type 构建。控制流完整性检查(CFI)的未来使用(它验证调用者和目标之间的函数原型匹配)往往不能很好地与函数转换配合,因此最好在 CFI 落地之前摆脱这些。
灵活数组转换
作为 Gustavo A. R. Silva 正在进行的用灵活数组替换零长度和单元素数组的工作的一部分,他记录了灵活数组转换的细节,以及在内核代码中使用的各种辅助函数。每次提交都使内核更接近使用 -Warray-bounds 构建,这在编译时捕获了许多潜在的缓冲区溢出。
以上就是全部内容!如果您认为还有其他需要关注的内容,请告诉我。接下来是 Linux v5.10。
© 2021 – 2022, Kees Cook。本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。