Linux v5.1 内核安全特性深度解析

本文详细介绍了Linux内核v5.1版本中的多项安全增强特性,包括pidfd的引入、用户空间映射检查、LSM堆叠支持、SafeSetID LSM、refcount_t转换、隐式fall-through移除以及栈变量初始化改进等。

Linux v5.1 中的安全特性

发布日期:2019年5月27日

Linux内核v5.1已发布!以下是我注意到的一些与安全相关的内容:

pidfd的引入

Christian Brauner完成了他的部分工作,以消除内核中的pid竞争:使用文件描述符来引用进程(“pidfd”)。现在可以打开/proc/$pid并将其作为参数用于通过新的pidfd_send_signal()系统调用发送信号。此句柄将仅引用打开open()时原始进程,而不会引用进程死亡后分配相同pid的任何后续“重用”进程。使用此方法,现在可以无竞争地向确切的目标进程发送信号,而无需担心pid重用。(顺便说一句,此提交赢得了2019年“最佳文档化提交日志理由”奖。)

显式测试用户空间对堆内存的映射

在Linux Conf AU 2019内核加固BoF期间,Matthew Wilcox指出,当用户空间映射应用于内核堆内存时,内核中实际上没有任何健全性检查(这将允许攻击者绕过copy_{to,from}_user()基础设施)。驱动程序错误或能够混淆映射的攻击者不会被捕获,因此他添加了检查。引用提交日志:“将SLAB分配的页面映射到用户空间永远不合适”和“使用page_type的页面绝不能映射到用户空间,因为这会破坏其页面类型”。后一个检查几乎立即发现了一个不良情况,并迅速修复以避免页面类型损坏。

LSM堆叠:共享安全blob

Casey Schaufler完成了让多个Linux安全模块(LSM)同时运行(称为“堆叠”)的主要部分之一。现在,LSM可以共享与各种核心结构(例如inode、任务等)关联的安全特定存储“blob”,LSM可以使用这些blob来保存其状态(例如存储给定任务所限制的配置文件)。内核最初仅允许单个活动的“主要”LSM(例如SELinux、AppArmor等)完全控制整个存储blob。通过“共享”安全blob,LSM基础设施负责内存的分配和管理,LSM使用偏移量来读写其部分。这为“中型”LSM(如SARA和Landlock)与“主要”LSM堆叠铺平了道路,因为它们需要存储比“次要”LSM(例如Yama、LoadPin)更多的状态,这些次要LSM已经可以堆叠,因为它们不需要blob存储。

SafeSetID LSM

Micah Morton添加了新的SafeSetID LSM,它提供了一种缩小与CAP_SETUID能力相关的权限的方法。通常,具有CAP_SETUID的进程可以成为系统上的任何用户,包括root,这使得将其分配给非root用户以“降低权限”到某些较低权限用户变得毫无意义。在Chrome OS下,有进程树需要在不同用户ID下运行,其他安全完成这些转换的方法不足。相反,这提供了一种方法,当进程具有CAP_SETUID能力时,通过setuid()(和通过setgid()的组转换)创建系统范围的用户ID转换策略,使其成为分配给需要执行uid或gid转换的非root进程的更有用能力。

进行中:refcount_t转换

Elena Reshetova继续在内核核心代码(例如调度程序、futex、perf)中落地更多refcount_t转换,Anand Jain在btrfs中进行了额外转换。现有的转换,主要与syzkaller结合使用时,继续显示其在发现内核各处错误方面的效用。

进行中:隐式fall-through移除

Gustavo A. R. Silva在标记更多隐式fall-through案例方面继续取得进展。这项工作与refcount_t一样,令我印象深刻的是它发现了多少错误(参见所有“缺失break”的补丁)。它真正展示了内核通过添加-Wimplicit-fallthrough来防止此类错误再次出现的好处有多快。

栈变量初始化包括标量

structleak gcc插件(最初从PaX移植)改进了其“通过引用”覆盖范围,以初始化标量类型(使“structleak”有点用词不当:它现在阻止的不仅仅是结构体的泄漏)。除非编译器有错误,这意味着内核中的所有栈变量都可以在函数入口使用前初始化。对于未通过引用传递给函数的变量,-Wuninitialized编译器标志(通过-Wall启用)已经确保内核不会构建仅本地未初始化的栈变量。现在,启用CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL后,所有通过引用传递的变量也将被初始化。这应该消除大多数(如果不是全部)未初始化栈缺陷,性能成本非常低(对于大多数工作负载,它消失在噪声中),尽管它没有GCC_PLUGIN_STACKLEAK的栈数据生命周期缩短的好处,后者在系统调用退出时擦除栈。Clang最近获得了类似的自动栈初始化支持,我很希望这个功能能原生出现在gcc中。为了评估各种栈自动初始化功能的覆盖范围,我还在lib/test_stackinit.c中编写了回归测试。

目前就这些;如果我遗漏了什么,请告诉我。v5.2内核开发周期已经启动并运行。 :)

© 2019 – 2022, Kees Cook。本作品采用知识共享署名-相同方式共享4.0国际许可协议进行许可。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计