LWN: 5.14 中的 memfd_secret()!

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

memfd_secret() in 5.14

By Jonathan Corbet
August 6, 2021
DeepL assisted translation
https://lwn.net/Articles/865256/

2020 年 2 月之后,memfd_secret() 系统调用其实已经在 LWN 以其他形式报道过。一开始,它是 memfd_create()的一个 flag,但这部分功能后来被移到单独的系统调用中了。在这个功能发展的过程中有许多变化,但核心目的仍然是保持未变的,即允许用户空间的进程来创建一个内存区域,确保其他人(包括 kernel 本身)都无法访问。该内存区域可以用来存放加密密钥,或其他类似的不能暴露给他人的数据。这个新的系统调用最终被合并到了即将发布的 5.14 版本中。下面我们来看看这个系统调用最终在 mainline kernel 中是什么样子的。

memfd_secret()的原型如下:

int memfd_secret(unsigned int flags);

其中唯一可用的 flag 取值是 O_CLOEXEC,用来设置当此进程后续调用 execve()时,这部分区域会被移除掉。不过,在 fork 调用之后这部分应该保密的区域对子进程仍是可以访问的。memfd_secret() 的返回值是一个文件描述符,用来跟这个新创建的秘密内存区域关联起来。

在这个时刻,进程实际上不能访问这部分内存区域,因为目前甚至连 size 都还不知道。后续必须调用 ftruncate() 来设置该区域的 size,然后使用 mmap() 将该 "file" map 到属主进程的地址空间之内。针对这个 mapping 而分配的那些内存 page 会被从 kernel 的 direct map 中移除掉,并被特别标记过,防止它们后续被错误地重新 map 回来。此后,该内存区域是可以被该进程访问的,但对其他任何进程甚至包括内核都不能访问这一区域了。因此,这部分内存的数据就被很好地保护了起来。很好,但这也带来一些缺点。例如,指向这部分秘密内存区域的指针不能用在系统调用里了;该内存区域也不能用于 DMA 操作。

这项工作第一次公开发表是在 2019 年 10 月。在后续两年不断演进这个系统调用的时间里,它已经有了不少改变,不仅仅是变成了一个单独的系统调用(发生于 2020 年 7 月)而已,这是因为人们认为此功能与普通 memfds 操作完全没有什么共同点。在 2020 年 7 月底发布的第二版中 memfd_secret() 支持了预留一块固定区域的 memory,但在 9 月的第 5 版中就被删除了。

早期的 patch set 中包括一个 flag,用来要求从内核的 direct map 中删除这部分秘密 page。这样就可以让内核无法访问这些内存区域了(任何可能黑入内核的人也就无法看到这些数据了),但人们担心把用来形成 direct mapping 的 1GB page 中间到处挖洞的话会降低性能。不过,这些担忧已经随着时间的推移都消退了。实际上 2MB page 的性能表现与 1GB page 的性能并没有什么差异。因此,这个 flag 在 2020 年 8 月的第 4 版中消失了,变成了必须要从 direct map 中移除。

11 月发布的第 8 版中也增加了一些改动。会使用 CMA 来专门用作 memfd_secret() 的内存来源,而不是随便分配一块内核管理的内存了。另一个改动也一直让大家很有争议,它的作用是只要有一个秘密内存区域处于 active 状态时,就禁止系统的休眠(hibernation)。这样做的目的是防止秘密数据被写入持久性存储设备中(persistent storage),但如果一些用户发现他们的系统不能再休眠的话很可能会感到不满。尽管如此,这个行为也还是进入了 5.14 内核。

从一开始,memfd_secret()就支持一个 flag 专门用来申请 uncached mapping 来作为秘密数据区域,也就是说希望绕过 memory cache。这样就可以提供更高的安全性,因为在 cache 中都不会再有这些数据了(毕竟 Spectre 漏洞就可以窃取 cache 内容),但将内存设置为 uncached 的话会大大降低性能。cache 毕竟是非常重要的。Andy Lutomirski 一再反对提供这种 uncached mapping,反对因此引入的性能降低等。RApoport 最终同意把这个 flag 删除掉了。在第 11 版完成了删除,此后到现在的最新版本为止都不再具有这个功能了。

在第 17 版(2021 年 2 月)代码中,memfd_secret() 被默认禁用了,并增加了一个 command-line option(secretmem_enable=),用来在系统启动时启用这个功能。这个决定是出于担心系统性能可能会因为打破了 direct mapping 以及对秘密区域内存进行 locking 操作而受到影响。所以除非系统管理员将其打开,否则该功能就不可用。这个版本也不再使用 CMA 进行内存分配了。

基本上这就是最终版本的 memfd_secret() 的效果了。它已经来到了第 23 个版本,看起来版本非常多了,但一般来说内存管理方面的改动基本上都是这样的。等 5.14 版本发布后,我们将能看到这个功能的用户群体有多大,以及他们还需要哪些进一步的改动。不过目前而言,这项工作似乎最终已经成功实现完毕了。有兴趣的人可以参见这个 man page 草案(https://lwn.net/ml/linux-mm/20210729082900.1581359-1-rppt@kernel.org/)。

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值