缓冲缓存更改会阻止Meltdown吗?

时间:2018-01-10 21:35:22

标签: caching x86 cpu cpu-architecture cpu-cache

如果新的CPU有一个缓存缓冲区,如果提交的指令只提交给实际的CPU缓存,那么类似于Meltdown的攻击仍然可能吗?

建议是让推测执行能够从内存加载,但在实际提交之前不要写入CPU缓存。

2 个答案:

答案 0 :(得分:9)

TL:DR:是的我认为它会解决当前形式的Spectre(和Meltdown)(使用刷新+读取缓存 - 时序侧通道从物理寄存器复制秘密数据),但可能过于昂贵(在电力成本,也可能是性能)可能的实施。

但是对于超线程(或者更常见的是任何SMT),如果你可以得到错误推测来运行数据,那么还有 ALU /端口压力侧通道与秘密数据相关的ALU指令,而不是将其用作数组索引。 The Meltdown paper在关注flush + reload cache-timing side-channel之前讨论了这种可能性。 (对于Meltdown而言,它比Spectre更可行,因为你可以更好地控制使用秘密数据的时间。)

因此修改缓存行为并不会阻止攻击。但是,它会夺走可靠的旁道,以便将秘密数据纳入攻击过程。 (即ALU时序具有更高的噪声,因此带宽更低,以获得相同的可靠性; Shannon's noisy channel theorem),并且您必须确保您的代码与受攻击的代码在同一物理内核上运行。

在没有SMT的CPU上(例如Intel的台式机i5芯片),ALU时序侧通道很难与Spectre一起使用,因为你不能直接在代码上使用性能计数器。有特权。 (但是,通过使用Linux perf对您自己的ALU指令进行计时,仍然可以利用Meltdown。

Meltdown具体为much easier to defend against, microarchitecturally,对CPU的硬连线部分进行更简单,更便宜的更改,微码更新无法重新连接。

您不需要阻止投机加载影响缓存;如果TLB命中的负载在达到退役时会出现故障,那么改变可能就像让投机执行继续进行一样简单,但由于权限检查失败而推迟执行后续指令所使用的值被迫0 TLB条目。

因此,错误推测(在secrettouch array[secret*4096]加载的错误加载之后,总是会使相同的缓存行变热,而没有与秘密数据相关的行为。秘密数据本身将进入缓存,但不是物理寄存器。 (这也会阻止ALU /端口压力侧通道。)

阻止故障负荷甚至带来"秘密"首先将行放入缓存可能会更难分辨内核映射和未映射页面之间的区别,这可能有助于防止用户空间试图通过查找内核映射的虚拟地址来击败KASLR。但那不是崩溃。

克特

幽灵是一个很难的,因为对微架构状态进行数据相关修改的错误推测指令确实有权读取秘密数据。 是的,#34;加载队列"与商店队列类似的工作可以解决问题,但有效地实施它可能会很昂贵。 (特别是考虑到我在编写第一部分时没有想到的缓存一致性问题。)

(还有其他方法可以实现你的基本想法;也许还有一种可行的方式。但extra bits on L1D lines to track their status有缺点,并且显然不容易。)< / p>

The store queue跟踪存储执行,直到它们提交到L1D缓存。 (商店在退休之前不能承诺使用L1D,因为他们知道这是非推测性的,因此可以让其他核心全局可见。)< / p>

加载队列必须存储整个传入缓存行,而不仅仅是已加载的字节。 (但请注意,Skylake-X可以执行64字节ZMM存储,因此其存储缓冲区条目必须是缓存行的大小。但如果它们可以互相借用空间或其他东西,那么可能不会{ {1}}可用的存储空间,也就是说,只有全部条目可用于标量或窄矢量存储。我从来没有读过任何关于此类限制的内容,所以我不认为那里是一个,但这似乎是合理的)

更严重的问题是,英特尔当前的L1D设计有2个读端口+ 1个写端口。 (可能还有另一个端口用于写入从L2到达并行提交商店的行?在Unexpectedly poor and weirdly bimodal performance for store loop on Intel Skylake上对此进行了一些讨论。)

如果加载的数据在负载退役之后无法进入L1D,那么他们可能会竞争存储使用的相同写端口。

L1D中的负载仍然可以直接来自L1D,并且内存顺序缓冲区中的负载仍然可以每时钟2执行。 (MOB现在将包括这个新的加载队列以及通常的存储队列+用于加载的标记来维护x86内存排序语义)。您仍然需要两个L1D读取端口来维持不会触及大量新内存的代码的性能,并且主要是重新加载在L1D中一直很热的东西。

这将使MOB大约两倍(就数据存储而言),尽管它不再需要任何条目。据我了解,当前Intel CPU中的MOB由各个加载缓冲区和存储缓冲区条目组成。 (Haswell has 72 and 42 respectively)。

嗯,进一步的复杂性是MOB中的加载数据必须与其他内核保持缓存一致性。这与商店数据非常不同,商店数据是私有的,并且在提交给L1D之前,它不会成为全局可见/不是全局内存顺序和缓存一致性的一部分。

所以这个提议&#34;加载队列&#34;如果没有调整,你的想法的实现机制可能是不可行的:它必须通过来自其他核心的无效请求来检查,以便MOB中需要另一个读取端口。

任何可能的实现都会遇到需要稍后像商店一样提交到L1D的问题。我认为,当它从非核心到达时,不能驱逐+分配新线路将是一个重大的负担。

(即使允许推测性驱逐而不是冲突中的推测性替换,也会打开一个可能的缓存时序攻击。你将所有线路都填满,然后做一个可以驱逐一个线路的负载一组行或另一行,并找出哪一行被驱逐而不是使用类似的缓存时序侧通道取出哪一行。因此,使用L1D中的额外位来查找/逐出从错误推测中恢复时加载的行不会消除这种旁道。)

脚注:所有说明都是推测性的。这个问题措辞得很好,但我认为很多人在阅读有关OoO执行官和思考Meltdown / Spectre的内容时会陷入混淆投机执行与“ mis -speculation”的陷阱。

请记住,所有指令在执行时都是推测性的。在退休之前,人们不知道这是正确的猜测。 Meltdown / Spectre依赖于访问秘密数据并在误推测期间使用它。但当前OoO CPU设计的基础是你不知道你是否正确推测;一切都是投机直到退休

任何加载或存储都可能发生故障,一些ALU指令也是如此(例如,如果异常未被屏蔽,则浮点数),因此任何适用的性能成本只有在推测性地执行时才会出现&#34;实际上一直适用。这就是为什么商店无法从商店队列提交到L1D直到之后商店uops从无序CPU核心退出(商店数据存储在队列中)。

但是,我认为有条件和间接分支是专门处理的,因为他们预计会在某些时候误推,并且优化它们的恢复非常重要。当检测到错误预测时,现代CPU在分支上的表现要好于回滚到当前的退出状态,我认为使用某种检查点缓冲区。因此,在恢复期间分支可以继续执行指令的无序执行。

但是循环和其他分支是非常共同的,所以大多数代码执行&#34;推测性地&#34;从这个意义上讲,至少有一个分支回滚检查点尚未验证为正确的推测。大部分时间它正确推测,因此不会发生回滚。

对错误推测内存排序或故障负载的恢复是一个完整的管道核心,回滚到退休架构状态。所以我认为只有分支使用分支检查点微体系结构资源。

无论如何,所有这一切都是让斯佩克特如此阴险的原因:在事实发生之前,CPU无法区分误推测和正确推测之间的区别。如果它知道它是错误的推测,它将启动回滚而不是执行无用的指令/ uops。间接分支也不罕见(在用户空间中);每个DLL或共享库函数调用在Windows和Linux上使用普通可执行文件中的一个。

答案 1 :(得分:0)

我怀疑缓冲和提交缓冲区的开销会导致specEx /缓存失效吗?

这纯粹是推测性的(没有双关语) - 我希望看到一个背景较低的人在这个权重!