树莓派是如何免疫 Meltdown 和 Spectre 漏洞的

前段时间,对Meltdown和spectre安全漏洞的讨论很是激烈,该漏洞影响了全部的现代intel处理器,一开始ARM还声称这些漏洞不会影响ARM系列的处理器,但后面的事实证实AMD处理器和ARM内核都没有免遭其害。python

spectre漏洞使得攻击者能够绕过软件检查,读取当前地址空间中的任意位置数据,meltdown漏洞使得攻击者能够读取操做系统核地址空间的任意位置数据(用户一般不可访问该数据)。这两种漏洞皆经过边信道攻击利用不少现代处理器都有的性能特征(缓存和推测执行)来泄露数据。但树莓派称不受这些漏洞的影响。算法

meltdown影响了intel处理器,打破了用户应用程序和操做系统之间最基本的隔离(地址空间隔离),这种攻击容许程序访问其余程序和操做系统的内存,这会致使数据泄露。(一般而言,目前操做系统都采用了虚拟内存管理方式,这样可让内存需求超过实际物理内存限制的进程或线程可以运行,加上页面置换便可。虚拟内存管理也能够实现对不一样内存区域的保护)缓存

而spectre除了可以影响intel还能影响AMD和ARM架构的大量处理器,也就是说除了PC,手机等终端也会受到影响,几乎全部现代计算机处理器均没法幸免。安全

可是相似树莓派等廉价计算设备可能并不会受到影响。架构

接下来介绍一些如今处理器设计的概念,使用简单的python来解释这些概念:oop

t=a+b性能

u=c+d操作系统

v=e+f线程

w=v+g设计

x=h+i

y=j+k


标量处理器

最简单的现代处理器每次循环执行一个指令,称之为标量处理器,也就是说上面的语句在标量处理器中须要执行6次循环。

树莓派1和zero中使用的intel486和arm11767都是标量处理器


超标量处理器

很明显,加速标量处理器的方式就是提供其时钟频率,可是这样很快就会达处处理器内部逻辑门运行的极限,所以处理器设计者开始寻找一次性处理多件事情的方式。

有序超标量处理器尝试在一个pipeline中一次性执行多个指令,这取决于指令之间的依赖关系,依赖关系很重要,你或许认为双向超标量处理器能够将6个指令配置执行:

t,u = a+b, c+d

v,w = e+f, v+g

x,y = h+i, j+k

但这没有做用,咱们必须先计算v再计算w,也就是指令三和四没法同时执行,所以会执行四个循环:

t,u =a+b , c+d

v=e+f #第二个pipe没有

w,x=v+g, h+i

y=j+k

超标量处理器包括intel pentium以及树莓派2,3使用的ARM cortex-A7和cortex-A53,树莓派3的时钟频率只比2高33%,但性能大约是后者的2倍,部分缘由在于A53超出A7的对大量指令的配对执行能力


无序处理器(与原子操做概念相关)

即便v和w存在依赖关系,咱们仍能找到其余独立指令来填补第二次循环中的空pipe。无序超标量处理器可以打乱指令执行的顺序(固然一样受限于指令之间的依赖关系)以保持每一个pipe处于忙碌状态。

无序处理器能够交换w和x的顺序:

t=a+b

u=c+d

v=e+f

x=h+i

w=v+g

y=j+k

这样就容许执行三次循环:

t,u = a+b, c+d

v,x = e+f, h+i

w,y = v+g, j+k

无序处理器包括了intel pentium2(以及大部分后序intel和AM x86处理器)还有近期的ARM处理器,如cortex-A9,A15,A17等。


分支预测器

上述的示例是直线式代码块,真正运行的程序不是这样的:他们还包括正向分支(if语句),反向分支(用于实现loop)。

在获取指令时,处理器可能遇到依赖于计算值的条件分支(而该值目前还没有计算出),为了不停顿,处理器必须猜想出下一个要得到的指令。分支预测器经过收集某一个分支以前被采用频率的相关统计数据,帮助处理器猜想该分支是否被采用。

如今分支预测器很是复杂,能够生成很是准确的预测,树莓派3的额外性能是因为cortex-A7和A53之间分支预测的改进。


推测

重排序顺序指令是一种恢复指令级别并行化的强大方法,可是因为处理器变得更宽(可以一次执行3-4个指令),保证全部pipeline处于忙碌就更难了,所以如今处理器提升了推测能力,推测执行能够处理并不须要的指令:这样能够保证pipeline处理忙碌状态,若是最后该指令没有被执行,咱们只须要放弃结果就能够了。

推测执行没必要要的指令须要耗费大量能源,可是在不少状况下为了得到单线程性能的提高,该方法是值得的。

为了展现推测的好处,看另外一个示例:

t=a+b

u=t+c

v=u+d

if v:

    w=e+f

    x=w+g

    y=x+h

如今咱们有从t到u到v,从w到x到y的依赖关系,那么没有推测的双向无序处理器没法填充第二个pipeline,它会用三次循环来计算t,u和v,以后处理器知道if语句是否被执行,而后再用三次循环来计算w,x和y。假设if使用了一次循环,那么该示例能够执行4次(v为0)或7次循环(v不是0),若是分支预测器代表if语句的主体极可能被执行,那么推测能够有效打乱程序顺序,以下:

t=a+b

u=t+c

v=u=d

w_=e+f

x_=w_+f

y_=x_+h

if v:

w,x,y=w_,x_,y_

循环计数在推测无序处理器中变得不太明确,但w,x和y的分支和条件更新几乎是空闲的,所以上述示例几乎执行三个循环。


什么是缓存?

在过去处理器速度与内存访问速度成正比,可是如今,处理器已经变得很是快,但内存几乎没有变化,树莓派3只须要0.5ns执行一次指令,但可能须要100ns才能访问主存。

在实践中,程序倾向于以相对可预测的方式访问内存,同时展现时间局部性(若是我访问一个定位,我可能很快会再次访问)和空间局部性(若是访问一个定位,极可能会很快访问附近的位置),缓存利用这些属性来下降访问内存的平均成本。

缓存是一个小的片上内存,接近于处理器,存储最近使用的位置还有近邻内容的副本,以便随后的访问中能够快速获取(最快的存储器固然是CPU内部的寄存器,与CPU同一种材质制成,访问速度与CPU同样。可是大小极其有限)。


什么是边信道攻击?

边信道攻击是基于从密码系统的物理实现得到的信息的任何攻击,而不是算法中的蛮力或者理论弱点。例如定时信息,功耗等均可以提供额外的信息。

meltdown和spectre是经过定时观察缓存中是会否有另外一个可访问的位置,以推测内存位置的内容,这些内容一般不该该被访问。


把这些概念放在一块儿

如今让咱们看看如何结合推测和缓存以容许相似meltdown的攻击。

考虑下面这个示例,该程序时一个读取非法(内核)地址的用户程序,会致使错误:

t=a+b

u=t+c

v=u+d

if v:

    w=kern_mem[addr]  //若是到了这,因为非法访问内核地址,会致使程序错误

    x=w&0x100

    y=user_mem[x]

如今,假设咱们能够训练分支预测器,使其相信v极可能是非0的,那么咱们的无序双向超标量处理器就会混洗程序:

t,w_=a+b, kern_mem[addr]

u,x_=t+c, w&0x100

v,y_=u+d, user_mem[x]

if v:

    #fault

    w,x,y=w_,x_,y_ #we never get here

即便处理器老是推测性读取内核地址,它必须推测产生的错误,直到知道v是非0,也就是说当v为非0,意味着if中的语句要被执行,也就是要用户能够获得内核地址中的内容了,这个时候会程序报错。也就是说,从表面上看,这是安全的:

1.若v是0,,非法读取的结果不会提交给w

2.若v是非0,但在读取结果被提交给w以前发生了错误

然而,假设在执行代码前刷新了缓存,并排列a,b,c,d使得v实际为0,第三个循环中的推测性读取为:

v,y_=u+d, user_mem[x_]

将其依赖非法读取结果的第八位获取用户地址0x000或0x100,并把地址及其近邻加载进缓存,因为v是0,推测性指令的结果将被摒弃,执行将继续。若是咱们随后访问其中一个地址,就能够决定哪一个地址在缓存之中,那么恭喜:你刚刚从内核地址空间读取了一个位。而且你知道了该位的具体地址。


真正的meltdown实际上更为复杂(由于咱们实际上优先执行了非法读取,并处理产生的异常),但原理是相同的。



结论:

现代处理器不遗余力保持抽象,从而成为直接访问内存的有序标量机器,而事实上,使用包括缓存,指令重排序以及推测在内的大量技术来提供比简单处理器更高的性能成为了现实。meltdown和spectre就是当咱们在发展过程当中,咱们的理想和现实的细微差异致使的漏洞。

树莓派使用的ARM1176, cortex-A7和cortex-A53内核中推测功能的缺失使得树莓派免疫此类攻击。

相关文章
相关标签/搜索