LWN:支持Intel线性地址屏蔽功能!

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

Support for Intel's Linear Address Masking

Support for Intel's Linear Address Masking
DeepL assisted translation
https://lwn.net/Articles/902094/

一个 64 位的指针可以对海量的内存进行寻址,远远超过真实世界中一个应用程序可能需要的内存数量。因此,在这种类型的指针中,有一些 bit 在表示地址的方面并不是必须要用的,那么它们就可能会被用于其他需求场景。在指针中存储几个 bit 的 metadata 已经是一个很常见的用例了,以至于目前在多个架构上都有在硬件层面上增加对这个功能的支持。英特尔也不例外;对其 "线性地址屏蔽"(LAM, Linear Address Masking)功能的支持已经在慢慢地靠近 mainline kernel 了。

CPU 可以通过在对指针进行解析引用(dereferencing)之前直接屏蔽某些 bit,就可以支持这种 metadata 了。当然,每个 CPU 厂商都在以不同的方式来支持这个功能。Arm 的 top-byte ignore 功能也是用来将地址中最高位的那个 byte 用来做其他用途的;自 2019 年 Linux kernel 5.4 版问世以来,内核一直就支持该功能。相反,AMD 的 "upper address ignore" 功能只允许以这种方式来使用最上面的七个 bit;今年早些时候提出了对这个功能的支持,但尚未被接受。

AMD 这个功能面临的一个障碍是,这个功能让使用了最高位这个 bit 的用户空间指针也是可以合法创建的了。在目前的内核中,只有内核空间的地址设置了最高 bit,而大量的底层代码都依赖于这个特点来进行判断。混淆用户空间和内核空间地址的后果可能会很严重,会出现大量漏洞也许 CVE 编号都不够用了的,所以开发者对任何可能导致这种混淆的新功能都很紧张。可能会有相当多的代码要被审计,才有可能让我们相信允许用户空间地址设置该 bit 不会引出一系列的安全漏洞。

英特尔的 LAM 功能提供了两种模式,这两种模式都与其他厂家的方案不同:

  • LAM_U57 允许在第 62 到 57 位有 6 位元数据。

  • LAM_U48 允许在第 62 到 48 位有 15 位元数据。

值得注意的是,这两种模式都不允许将第 63 位(最重要的位)用于此目的,所以 LAM 避免了这个给 AMD 带来不小麻烦的陷阱。

Kirill Shutemov 的这个 patch set 增加了对 LAM 的支持。由于 LAM 必须由特权代码在 CPU 中启用,所以该 patch set 以两个新的 arch_prctl()命令的形式来引入了一个新的 API,希望能够支持任何 CPU 的指针 metadata 机制。第一个是 ARCH_ENABLE_TAGGED_ADDR,使当前进程可以使用 LAM。其具有一个整数参数,表示进程希望在指针中存储多少 bit 的数据,并从上面两种方式中选择一种(肯定得要包含该 bit)。

希望使用这个功能的程序需要知道他们的 metadata 可以存储在指针的哪个位置;如果这样的程序要在不同的架构上移植运行,那么就需要一种通用的方法来实现这一点。因此引出了第二个 arch_prctl()操作,ARCH_GET_UNTAG_MASK,它会返回一个 64 位的值,设置了相应的 bit 来表示可用的空间。该 patch set 还在每个进程的 /proc 中的 arch_status 文件中增加了一行,用来指示起作用的 mask。

整体来说,LAM patch 没有什么争议;人们认为此功能比 AMD 的对应功能设计得更好。不过对于 LAM_U48 模式有一些担忧,可能会阻止它能很快在 Linux 中得到支持。

自 2017 年的 4.11 版本以来,Linux 在 x86 系统上支持五级页表。在启用了五级页表的系统上,57 位的地址空间可供系统中运行的进程来使用。内核通常不会将内存映射到该地址空间的前九个 bit,也就是第五级页表所增加的部分,因为担心这会破坏应用程序;除此之外,有一些程序可能会在这些 bit 上存储 metadata,哪怕是没有硬件支持的时候。在使用这个技巧时必须更加小心,因为 metadata 的那些 bit 在解析引用指针之前必须要先 mask 掉,这个确实是可能实现的。不过,如果这些 bit 对于在其地址空间内寻址的内存来说是必须要用的,那么这种程序显然会出问题。

为了避免这种问题,内核只在应用程序在 mmap() 调用中明确要求的时候,会仅仅将内存映射到地址空间的上半部分。很少有应用程序需要这样做,而且在希望这么做的时候,也很容易实现。

LAM_U48 模式使用了 15 个指针位,只为实际地址留下 48 个有效位,显然与之前使用了 57 位地址空间的用法就不一致了。有人可能会争辩说,任何试图同时使用这两种模式的程序员碰到任何问题都是罪有应得的,不过我们最好还是尽可能不要提供 useless footguns 了。由于内核已经在这两种模式的使用中都有办法控制,因此可以很好地确保它们不被同时使用。

在 Shutemov 的 patch set 中,启用 LAM_U48 功能放在了最后的一组可选的 patch 中;如果它们被排除在外,那么内核将只支持 LAM_U57 模式,这当然是解决问题的一种方式。如果这个 patch 被包括在内,那么用户空间必须要选择一下它将使用这两个特性中的哪一个(如果有的话);首先被选中的模式会成为现实。如果 LAM_U48 被启用了,那么这个进程映射内存时就永远不可以利用地址空间的高 9 位了。但是如果该进程在试图启用 LAM_U48 时已经在那个范围内映射了一些内存,那么该尝试就会失败。

这似乎是一个合理的解决方案,它可以让所有的功能都可用,并让进程选择他们将实际使用的功能,但开发人员仍然担心 LAM_U48 模式。Alexander Potapenko 建议,如果这种模式进入 mainline,发行版提供商也会想要移除它,但是随着时间的推移,其他的改动会让他们更加难以进行移除。Dave Hansen,x86 的维护者之一,说他不会立即合并 LAM_U48,但会考虑在将来合并。

因此,虽然似乎没有什么阻碍 LAM 的采用,但目前还无法确定所有的 LAM patch 是否会很快被合入。如果有人有 LAM_U48 的使用场景,那么现在就是一个很好的时机来让开发者们知道这些用例。否则他们可能会发现这个功能未来不再可以使用了。

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值