LWN:Arm64的内存标记扩展功能!

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

The Arm64 memory tagging extension in Linux

By Jonathan Corbet
October 15, 2020
DeepL assisted translation
https://lwn.net/Articles/834289/

5.10 内核开发周期中合入的首批功能之一就是支持 Arm v8.5 memory tagging extension(内存标签扩展,https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/Arm_Memory_Tagging_Extension_Whitepaper.pdf )。通过在指针上添加一个 "key "值就可以自动检测到多种内存安全问题。最终应该会带来更安全、更可靠的代码,当然要等到真有硬件支持了此功能。

人们肯定可以看出来,Arm64 架构会使用 64 位指针来寻址内存。不过,暂时还用不到那么大的地址空间,通常只有 48 位被硬件实际使用到了。如果启用了特殊的 large-address-space 选项,则是 52 位。所以有 12-16 位可以拿来用做其他用途。Arm 系统长期以来一直支持 "top byte ignore,高字节忽略 "的功能,允许软件在虚拟地址的最高字节中存储任意数据,但硬件设计人员也一直忙着为这些 bit 想出其他用途。memory tagging extension(MTE)就是其中一个可能用途。

具体来说,MTE 允许在虚拟地址的第 59-56 位(即最上层字节的低几位)中存储一个四位 "key"。也会把一个特定的 key 值与一个或多个 16 字节的内存范围关联起来。当通过一个指针来间接访问内存区域时,存储在指针中 key 值会与指针所引用的内存已经关联的 key 值进行比较。如果两者不匹配,可能报出异常。key 可以由应用程序管理,也可以由 CPU 随机生成。

四个 bit 只能支持 16 个不同的键值,但这已经足够做一些有价值的事了。如果 malloc()这类的函数能确保内存中相邻区域在分配时获得不同的键值,那么越界访问的时候就会被处理器检测到。在释放一个内存区域时,可以通过立即改变 key 值,来检测释放后是否还会被错误地引用到。如果每个 stack frame 都拥有自己特殊的 key,这样的话堆栈上的缓冲区溢出也会触发异常。试图通过一个跑飞了的指针(或攻击者注入的指针)来访问内存空间时,也有很大机会被检测到。

因此,MTE 有了两种应用场景。如果在正常的软件开发过程中启用,会有助于在发布之前识别出一系列的错误。但它也可以在生产系统上启用,用来为攻击者们增加一个障碍,他们必须克服这个障碍才能利用已知的漏洞。

MTE 在 Linux 系统上默认是禁用的,哪怕硬件支持了 MTE 也会缺省禁用。用户空间的进程可以通过在创建内存映射的 mmap()调用中指定新增的 PROT_MTE 标志,这样就可以为指定的内存区域启用 MTE。也可以使用 mprotect()来对已经映射好的内存区域启用 MTE。只有匿名内存(anonymous memory)可以设置 PROT_MTE,如果在文件对应的内存(file-backed memory)上设置的话会失败。

与任意内存绑定的默认 key 值是 0。要想使用其他值,需要几个步骤。第一步,通常是为感兴趣的内存创建一个 tagged address,也就是把 key 值存入地址指针的 59-56 位中。然后利用一条新的指令(IRG)来生成一个随机 key 值,并将其存储到地址指针中。另一面来说,需要将 key 与 memory 本身关联起来。为此,创建了另一条新指令(STG),会取一个指针值,将包含它所指向 memory 区间的 16 字节 粒度的 key 设置为该指针中找到的 key。还有各种其他新增指令,用于管理 tag、在 memory 中存储数据内容和 tag 等。这些都是不需要特权(unprivileged)的操作,也就不需要内核帮忙。

如果一个进程试图用错误的 key 来访问内存的话,处理器默认情况下什么也不做。这个行为可以通过使用 prctl()系统调用的 PR_SET_TAGGED_ADDR_CTRL 命令来改变。如果改为 PR_MTE_TCF_NONE ,就会禁用 tag 检查(默认行为)。有其他两个值(PR_MTE_TCF_SYNC 和 PR_MTE_TCF_ASYNC)会在 key 不匹配时导致 SIGSEGV,前者会导致立即触发一个 signal(同步行为),而后者则会把一个异步 signal 放到队列中。同步信号会立即被发送给违规线程,操作将不会被执行,如果信号没有得到处理的话,进程将被终止。异步信号将被放入队列,稍后传递给进程,这个 tag 不匹配的访问操作将可以继续进行。

内核还支持一些与 MTE 相关的功能是内核支持的,包括一组 ptrace()命令(用于操作另一个进程的 tag)。更多的信息(和一个示例程序)可以在内核源代码的 Documentation/arm64/memory-tagging-extension.rst 中找到。需要注意的是,在 5.10 中,MTE 只支持在用户空间中使用,内核本身对 MTE 的支持还需在未来的开发周期中实现。

有些读者可能会注意到与 Arm pointer authentication(指针认证)功能很相似,Arm pointer authentication 会把一个简短的加密签名存储到指针的高位字节。它可以防止攻击者创建新的指针,因为指针创建就要求必须知道秘钥才行,它并不会将 key 与内存本身联系起来。这个功能和 MTE 可以一起使用,不过 MTE 会占用一些 bit,使得指针认证功能的签名存放空间更加小。两者都有价值,MTE 可以防止堆栈溢出,而指针认证可以发现堆栈指针本身被破坏的情况。

虽然 MTE 功能看起来很有用,但能由它直接支持的应用数量会很少。令人欣慰的是,大部分好处可以在完全不需要改变应用程序的情况下获得。如果 C 库(尤其是其中的内存分配器)支持 MTE,那么所有的应用程序都会自动获得额外的内存安全检查能力。GNU C 库的 MTE 修补程序已经提出来在 review 了,最终应该可以获得支持。LLVM 编译器现在已经支持 stack tagging,GCC 最终也会有这个功能。

不过这些现在来说都没有什么用处,因为支持 MTE 的硬件还没有面世。好消息是,一旦有了这些硬件,软件方面应该会马上就可以拿来用。如果运气好的话,即使是在没有内存标记功能的硬件上,也可以拥有更安全的系统和错误更少的软件。

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

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

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值