LWN:mseal() 即将确定!

Linux News搬运工

共 2730字,需浏览 6分钟

 · 2024-04-10

关注了就能看到更多这么棒的文章哦~

mseal() gets closer

By Jonathan Corbet
January 19, 2024
Gemini translation
https://lwn.net/Articles/958438/

2023 年 10 月首次提出的 mseal() 系统调用引发了一些争议。自那以后,它一直静悄悄地开发演进,并且似乎已经达到了相关评论者愿意接受的程度。预计会在未来的开发周期中合并 mseal(),届时它将看起来与当初的版本大不相同。

回顾一下,发明 mseal() 是为了防止对虚拟地址空间的部分区域进行更改。它的目的是阻止那些通过改变只读的内存或其他方式干扰进程内存布局的攻击方式。例如,能够改变内存权限或映射(mapping)的攻击者可能能够规避控制流完整性保护(control-flow-integrity protection)。通过使用 mseal(),进程可以防止这种类型的更改。预期会最开始使用的场景是 Chrome 浏览器,它将用此来进一步强化程序以抵御基于内存的攻击。

在 10 月份提出时,mseal() 具有以下原型:

int mseal(void /addr, size_t len, unsigned int types, unsigned int flags);

types 参数允许调用者微调 mseal() 所禁用的改动,这是更有争议的功能之一,许多人质疑除了彻底禁止更改之外难道还有什么别的有用处的选项么。即便如此,版本 2(在第一版本发布后不久发布)和 版本 3 (在 12 月中旬发布)还是保留了该参数。作为对后面发布的回复,Linus Torvalds 重申了他对 API 该方面的厌恶,并问道:“我想知道为什么我们不能只按 BSD 的 immutable 的方式,又为什么要这种多级别的密封机制”。

Chrome 开发者 Stephen Röttger 回答说,在其他地方可以密封区域的特定位置,Chrome 需要允许 madvise(MADV_DONTNEED) 在某些地方可用,而该操作在密封内存中是被禁止的,因为它本质上会更改映射;它会把底层内存内容废弃掉,如果是匿名页面,在再次访问的时候它将被重新填充为 0。它对于(例如)丢弃不再需要的缓存数据的场景很有用,但也有可能带来意外麻烦。在 Chrome 实例中,type 参数用于在可写的匿名内存(即使已密封,进程也有能力直接写入的内存)上允许 MADV_DONTNEED。Torvalds 答复说,正确解决方法是 mseal() 只允许在所说的映射可写时使用 MADV_DONTNEED。事实上,他认为即使没有密封,这种限制也可能是有意义的。

通过上述讨论,版本4 于 1 月初发布,实施了关于 MADV_DONTNEED 的新语义。此版本也最终放弃了 types 参数;内存现在要么处于密封状态,要么就是未密封状态。Torvalds 对这些 改动感到满意;他声称 "我觉得所有这些都很合理了",并且退出了讨论。第五个版本 仅带来一些小型改动,表明主要的疑虑已得到解决;Kees Cook 指出 "此代码看起来可以合入了"。自那以后 版本 6 已经发布,附有一些其它的小型改动。

如果 mseal() 以这种形式合并的话,其原型如下:

int mseal(void /addr, size_t len, unsigned long flags);

addr 和 len 指定要密封的内存范围;flags 参数当前未使用,且必须为零。它仅可用于 64 位系统。该文档补丁 包含有关其使用情况的更多信息。

所以这个故事可能已经到头了,但仍有一方面有点被忽视了。OpenBSD 有一个类似的系统调用 m[[https://lwn.net/Articles/915640/]mmmimmimmutable(),已于 2022 年发布。它也能阻止对地址空间特定范围进行修改。在交流过程中,有人多次提出简单地为 Linux 实现 mimmutable() 。mseal() 的开发人员 Jeff Xu 一直对这一建议置之不理,以至于 mimmutable() 的创建者 Theo de Raadt, 建议 ""或许此提案应该使用 chromesyscall() 作为名称""。这似乎意味着为 Linux 实现 mimmutable() 这个建议从未被认真考虑过。

然而,随着 mseal() 变得更加简单,将其与 mimmutable() 区分开的功能也不再存在了,以至于现在二者的作用几乎相同。唯一区别在于 mimmutable() 允许降级权限(即使内存已经过密封,也可以将其设置为只读),而 mseal() 不允许;不过,OpenBSD 可能还会去除 这项功能,进一步减少了两个系统调用之间的语义区别。考虑到这一点,或许再问一次这个问题有它的价值,即 Linux 为什么不采用现有接口并添加 mimmutable()。这并不是一个有直接答案的问题。

可能的答案确实存在。来自 长期的实践 的经验告诫我们, mseal() 采用 flags 参数是个好主意,即使尚不能明确说出可能的使用场景。也有可能是这个系统调用的用途一直非常专用且底层,以至于使用该调用的任何代码无论如何都需要针对特定系统进行设计,在这种情况下,使用相同名称的意义并不大。最后,如果认为有价值,可以在 C 库中为 mseal() 添加一个 mimmutable() 包装器,这几乎不需要花费什么精力。

当 mseal() 合并之后,它最初只会使 Chrome 浏览器(及其小部分用户)受益。但正如 mseal() 的附言信指出,Rottger 正在致力于为 GNU C 库添加支持,以便大多数程序能够自动运行大量的密封操作。该操作将极大地增加此新系统调用的使用,而且能在 C 库中使用它就可以提高对 API 正确性的信心。这似乎有望一锤定音。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~



浏览 1
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报