点击上方蓝色“Linux News搬运工”关注我们~
Some near-term arm64 hardening patches
By Jonathan Corbet
November 18, 2019
原文来自:https://lwn.net/Articles/804982/
arm64架构是非常多的(可能是绝大多数)移动设备(mobile device)的核心,也就意味着arm64设备会成为全球攻击者的一个关注目标。因此,大家越来越关注于一些对arm64系统进行加固(harden)的技术。目前已经有几种技术可以实现了,都是基于软硬件结合的方式,可供arm64 kernel合入了。下面我们逐个来过一下。
E0PD
Meltdown漏洞可以让user space的攻击者能通过一些预测执行机制(speculative execution)和一些cache based side channels组合起来读到一些kernel里的数据。kernel针对Meltdown进行的防御方式就是做kernel page-table isolation——把kernel的page table从user-space的mapping里面彻底拿掉。这样确实有效,不过会导致性能明显下降,并且也会影响处理器的其他一些功能。不过,近来大家已经广泛赞同地址空间隔离是对系统进行保护的一个重要方案了。
不过这里还有一个其他选择:修复硬件本身。E0PD功能就是其中一个尝试,是在Arm v8.5扩展里面新加入的功能。关于E0PD的文档目前很难看到,甚至Mark Brown提交的支持此功能的patch里面都没有描述清楚它的工作机制,以及这个名词的含义。目前对E0PD最主要的描述信息只能找到下面这一段:“E0PD,是ARM v8.5扩展中引入的新功能,[...]它确保每次在userspace对kernel这一半memory map进行访问的时候,fault错误都是在固定耗时之后返回的,从而可以避免时序攻击(timing attach),而不需要频繁地进行unmap和remap操作,也不需要阻止正当的访问。”
因此E0PD其实没有阻止speculative execution来进入user space不该能访问的memory,不过确实能够阻止side channel attach通过利用那些不正确地预测操作而暴露出来的数据。支持E0PD的系统就不需要再打开kernel page-table isolation功能了,这样就不会遭受相关的性能损失。不过patch set并没有附上性能数据。E0PD在kernel里应该很快会合入了,不过相应的具有E0PD功能的处理器还需要很长时间才会上市。
Return-address signing
Arm pointer authentication机制,是对执行中的代码里用到的指针进行签名验证。有一个特殊指令会利用一个秘钥对指定的指针值进行签名,签名会存在指针的高几位本来没有用处的bit内。还有另一个指令会用秘钥来验证这个签名。这个机制可以用来防止攻击者骗过kernel去访问一个篡改过的指针。
Amit Daniel Kachhap的return-address signing patch set就利用了这个机制,专门用来保护存放在stack里的函数的返回地址。GCC 7新增了一个-msign-return-address flag,利用这个flag来编译kernel就可以打开保护。调用函数的时候会把返回地址进行签名,等到要从函数返回回去的时候就验证这个签名。如果验证失败,kerel会报告oops信息,当前进程则会被kill掉。
这么做的目的,很显然是希望保护kernel不受缓冲区溢出等类似针对stack的攻击影响。攻击者可能会破坏堆栈,不过他们没法让返回地址指针通过签名验证。这样就能阻止多种潜在的攻击方式,因为太多攻击都是通过在stack上篡改返回地址来进行的。
Shadow call stacks
Sami Tolvanen还提了一个shadow call stack support patch set,也是用来保护返回地址的。这个方案没有对返回地址进行签名验证,而是利用了Clang的-fsanitize=shadow-call-stack option,来确保返回地址是存在内存中另一个位置的"shadow" stack(影子堆栈)内。在函数返回之前,会把返回地址从shadow stack里面恢复出来。
目前call stack对于攻击者来说非常容易破坏,只要对一个变量进行缓冲区溢出攻击就可以。使用shadow call stack之后,这种缓冲区破坏的做法就很难对系统带来重大损害了,因为返回地址不放在这个stack里面。shadow stack对于攻击者来说通常很难破坏了,甚至都不会让他们知道位于哪里。效果一样,系统会更加不害怕缓冲区溢出攻击。
返回地址签名,还有shadow call stack,是针对同一个问题的两种不同解决方案。人们通常不用把两个都打开。Tolvanen在patch开头就介绍了应该选择哪个来用:“shadow call stack的性能损失较小,不过分配shadow stack内存空间会浪费一些kernel memory。因此这个功能对那些缺乏PAC指令的硬件来说会很有用。”
换句话说,拥有指针验证功能的处理器就直接用这个功能好了。shadow call stack则适用于没有这个机制的硬件。这个patch本身也已经准备好了,预计会在5.6 merge window内合入。
Branch target identification
目前最后一项还在考虑中的arm64功能就是branch-target identification (BTI),这个功能是用来捕捉wild jump(野跳转)的。理念很简单:打开BTI的情况下,每一个间接跳转(indirect jump)之后碰到的第一条指令必须是一个特殊的BTI指令。这个指令在不具备BTI的系统上就是no-op(什么都不做的指令);在具有BTI机制的硬件上,BTI指令可以不报出fault。如果跳过去的代码开头不是BTI指令,则会马上把当前进程杀死。
BTI其实就是标记出这一段代码是一个间接跳转的目标,这样如果攻击者用什么方法骗过kernel跳转到某个位置的是偶,就可以阻止后续攻击。很多类型的攻击都可以被这种方式阻止,例如,篡改了kernel中一个存放了很多函数指针的结构的情况。有意思的是,BTI在从函数末尾返回的时候并不会检查,因此还是需要用专门的返回地址签名机制来保护函数返回。GCC 9版本包含了BTI支持。
在arm64系统上,每种技术都解决了一类问题。合在一起,应该能够让系统成为一个更加难于攻破的目标。arm架构还在不断扩展,攻击者肯定还会找到一些方法来绕过这些技术。不过,我们乐于见到的是,攻击者们今后一定会对这些技术感到抓狂。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
极度欢迎将文章分享到朋友圈
热烈欢迎转载以及基于现有协议修改再创作~
长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~