关于RIDL漏洞和“重播”负载

4
我正在尝试理解RIDL漏洞类别。 这是一种漏洞类型,能够从各种微架构缓冲区中读取陈旧数据。 今天已知的漏洞利用:LFB、加载端口、eMC和存储缓冲区。 所链接的论文主要关注于LFB。 我不明白为什么CPU会使用LFB中的陈旧数据来满足负载。 我可以想象,如果负载命中L1d,则会在内部“重播”,直到L1d将数据带入LFB并向OoO核心发出停止“重播”的信号(因为现在读取的数据是有效的)。
然而我不确定"replay"具体指什么。我认为负载被分配到一个负载可用的端口,然后记录在负载缓冲区(在MOB中),并最终保持需要直到它们的数据可用(由L1信号)。所以我不确定"replaying"是如何发挥作用的,此外对于RIDL工作,每次尝试"play"负载也应该解除阻塞的依赖指令。这对我来说很奇怪,因为CPU需要跟踪哪些指令在负载正确完成后重新播放。
关于RIDL的论文使用以下代码作为示例(不幸的是我不得不将其粘贴为图像,因为PDF布局不允许我复制它):

RIDL snippet

唯一的原因是,如果CPU首先使用旧数据满足第6行的负载,然后重播它,它才能起作用。
这在几行以下似乎得到了证实:
具体而言,我们可能期望两个访问都很快,不仅仅是对应于泄漏信息的一个。毕竟,当处理器发现自己的错误并在第6行重新启动时,程序也将使用此索引访问缓冲区。
但我希望CPU在转发LFB(或任何其他内部缓冲区)中的数据之前检查负载的地址。
除非CPU实际上重复执行负载,直到它检测到已加载的数据现在有效(即重放)。
但是,为什么每次尝试都会解除依赖指令的阻塞?
如果存在重放机制,则它的工作方式是什么,以及它如何与RIDL漏洞交互?

什么是“eMC”? - Hadi Brais
@HadiBrais 嵌入式存储控制器,至少是连接环形总线的部分。 - Margaret Bloom
我不明白为什么内存控制器在这里很重要。《RIDL论文》的第四张表格显示了哪些硬件结构会导致哪些漏洞。 - Hadi Brais
@HadiBrais 我也是。可能我误解了首页上突出显示eMC的图片,认为它是MDS漏洞的另一个数据源。 - Margaret Bloom
啊,那很可能是一个错误。从RIDL和Fallout论文中可以清楚地看出,作者(像我们一样)并不完全理解正在发生的事情。 - Hadi Brais
2个回答

3

重新调度=从RS(调度程序)再次派遣。(这不是关于您整个问题的完整答案,只是关于重放的部分。虽然我认为这涵盖了大部分内容,包括解除依赖uops。)

此答案的部分对负载重播有误解。

请参见聊天中的讨论-依赖于分裂或缓存未命中负载的uops将被重播,但不会重播负载本身。(除非负载在循环中依赖于自身,例如我一直在测试的那样)。 TODO:修复此答案和其他答案的其余部分。


事实证明,缓存未命中加载不仅仅在加载缓冲区中等待数据到达时唤醒相关的 uop。调度程序必须重新分派加载 uop 来实际读取数据并写回物理寄存器。(并将其放在转发网络上,在下一个周期中可以读取依赖的 uop。)因此,L1 未命中 / L2 命中将导致 2 倍的加载 uop 被分派。(调度程序是乐观的,并且 L2 在核心上,因此 L2 命中的预期延迟是固定的,而不像离核心响应的时间。我不知道调度程序是否继续对从 L3 到达某个时间的数据保持乐观。)
RIDL论文提供了一些有趣的证据,表明负载uop确实直接与LFB交互,不等待传入数据被放置在L1d中,而是直接从那里读取。
我们可以最容易地在实践中观察到缓存行分裂加载的重放,因为反复引起这种情况甚至比缓存未命中更微不足道,需要更少的代码。仅执行拆分加载的循环,uops_dispatched_port.port_2port_3的计数将约为两倍。(我已经在Skylake上实践验证了这一点,使用基本相同的循环和测试过程,如How can I accurately benchmark unaligned access speed on x86_64所述)
检测到分裂的加载(仅在地址计算后可能)不会向RS返回成功完成的信号,而是执行第一部分数据的加载,并将其结果放入一个分裂缓冲区1,以便在第二次uop调度时与第二个缓存行的数据结合。 (假设两次都不是缓存未命中,否则也需要重放。)
当一个负载uop调度时,调度器会预测它会命中L1d,并调度依赖的uops,以便它们可以在负载将它们放到总线上的那个周期内从转发网络中读取结果。
如果这种情况没有发生(因为负载数据还没有准备好),那么依赖的uops也将不得不重新执行。我记得这也可以通过端口的perf计数器观察到。

现有的关于Intel处理器上uop重播证据的问答:


注1:

我们知道分裂缓冲区数量有限;对于因缺乏缓冲区而停顿的加载,有一个ld_blocks.no_sr计数器。我推断它们在加载端口中,因为这是有意义的。重新调度相同的加载uop将把它发送到同一加载端口,因为uop在发布/重命名时被分配给端口。虽然也许有一个共享的分裂缓冲区池。


RIDL:

乐观调度是导致问题的机制之一。更明显的问题是让后面的uop执行看到来自LFB的“垃圾”内部值,就像Meltdown中一样。

http://blog.stuffedcow.net/2018/05/meltdown-microarchitecture/甚至显示,PPro中的Meltdown加载公开了各种微架构状态的位,就像最新处理器中仍存在的此漏洞一样。

Pentium Pro对“负载值是不关心”的理解非常字面。对于所有禁止的负载,负载单元都会完成并生成一个值,并且该值似乎是从处理器的各个部分获取的各种值。该值是变化的,可能是非确定性的。返回的值中没有一个是内存数据,因此Pentium Pro似乎不容易受到Meltdown攻击。

可识别的值包括负载的PTE(至少在最近几年中,它本身被认为是特权信息),第12个最近存储的值(存储队列有12个条目)以及很少情况下从某个地方的段描述符。

(后来的CPU,从Core 2开始,公开了L1d高速缓存中的值;这就是Meltdown漏洞本身。但PPro / PII / PIII不容易受到Meltdown的攻击。在这种情况下,它显然容易受到RIDL攻击。)因此,这是相同的英特尔设计理念,将微架构状态的一些位暴露给了推测执行。在硬件上把它压制为0应该是一个简单的修复;负载端口已经知道它没有成功,所以根据成功/失败屏蔽加载数据的掩码只会增加几个门延迟,并且可以在不限制时钟速度的情况下实现。(除非负载端口中的最后一个管道级别已经是CPU频率的关键路径。)因此,对于未来的CPU来说,这可能是一个简单而廉价的硬件修复,但对于现有的CPU来说,通过微码和软件进行缓解非常困难。)

那么,一个依赖于其他操作的uop将会一直保留在RS中,直到该加载被标记为成功完成?基本上,每个uop都有一个“成功执行”位,只有当它在uop本身和所有先前的uop中设置时才有效(由于RS是按顺序填充的,因此很容易检查)。所以,调度程序的乐观性质是RIDL的问题所在。 - Margaret Bloom
@MargaretBloom:可能需要多个uops才能完成,不过我并不完全惊讶他们能够拼凑出一段微代码序列来设置内部状态为“安全”值,也许是通过滥用原本用于其他目的的uops来实现。比如说,将零存储到一个不会写入物理内存的虚拟地址中?总之,我的回答的最后一段仍然有效;一个新的MSR用于写入触发手动刷新并不能很好地防御在同一进程中的WebAssembly针对Javascript沙盒的攻击:没有上下文切换。 - Peter Cordes
“Spectre微码缓解的新指令”实际上是一个可以写入的新MSR。这是英特尔添加完全新的微码内容的唯一钩子,因为实际的解码器部分是固定功能的;不可能添加具有自己操作码的实际新指令。但是,MSR写入/读取基本上是对具有“调用号”和一个输入或输出参数的微码的钩子。可以安全地假设此新钩子也将是MSR写入。 - Peter Cordes
3
@MargaretBloom 和 Peter,这个微码更新增强了 VERW 指令的行为,以便将其解码为更多的 uops。这些额外的 uops 是内存加载和存储 uops,它们只是用某个安全值(例如零)覆盖所有受 MDS 影响的缓冲区。这些与英特尔显示的软件序列等效,可用于没有微码更新的处理器。VERW 在支持它的所有处理器上始终是微编码的。因此,该更新(除其他事项外)更改了 VERW 的微码例程,并且不会改变任何其他内容。 - Hadi Brais
@HadiBrais:不,我没有找到,谷歌也没有搜到。有时候使用“site:stackoverflow.com "cordes" ...”可以找到我在评论中参与的讨论,但是要么我错过了,要么它不在搜索结果中。 - Peter Cordes
显示剩余8条评论

3
我认为从RS加载回放不涉及RIDL攻击。因此,我将根据我对RIDL论文、英特尔漏洞分析和相关专利的理解,讨论我认为正在发生的事情,而不是解释什么是load replays(@Peter的答案是一个很好的起点)。
线填充缓冲区是L1D高速缓存中的硬件结构,用于保存在高速缓存中未命中的内存请求和I/O请求,直到它们得到服务。当所需的高速缓存行被填充到L1D数据阵列中时,可缓存请求得到服务。当发生任何撤销写组合缓冲区的条件时(如手册中所述),写组合写入得到服务。当UC或I/O请求发送到L2高速缓存时(尽可能快地发生),该请求得到服务。
参考RIDL 论文的图4。用于产生这些结果的实验如下:
  • 受害线程向单个内存位置写入已知值。该内存位置的内存类型为WB、WT、WC或UC。
  • 受害线程在循环中读取相同的内存位置。每个加载操作后跟随MFENCE,并且有一个可选的CLFLUSH。从论文中不清楚CLFLUSH与其他两个指令的顺序,但这可能并不重要。MFENCE序列化缓存行刷新操作,以查看当每个加载在缓存中未命中时会发生什么。此外,MFENCE减少了L1D端口上的两个逻辑核之间的争用,从而提高了攻击者的吞吐量。
  • 在兄弟逻辑核上运行的攻击者线程在循环中执行列表1中显示的代码。第6行使用的地址可以是任何内容。唯一重要的是,在第6行进行的加载操作会导致故障或需要微码协助的页面行走(以设置页面表项中的访问位)。页面行走还需要使用LFB,大多数LFB在逻辑核之间共享。
在图4中,我不清楚Y轴代表什么。我的理解是,它表示每秒从隐蔽通道中获取到的被缓存层次结构获取的行数(第10行),其中数组中的索引等于受害者写入的值。
如果内存位置是WB类型,当受害线程将已知值写入内存位置时,该行将被填充到L1D缓存中。如果内存位置是WT类型,则当受害线程将已知值写入内存位置时,该行不会被填充到L1D缓存中。但是,在第一次读取该行时,它将被填充。因此,在两种情况下,没有CLFLUSH,大多数从受害者线程加载的内容都会命中缓存。
当一个加载请求的缓存行到达L1D缓存时,它首先被写入为该请求分配的LFB中。缓存行的请求部分可以直接从LFB提供给加载缓冲区,而无需等待缓存中填充该行。根据MFBDS漏洞的描述,在某些情况下,来自先前请求的陈旧数据可能被转发到加载uop以满足其要求。在WB和WT情况下(不进行清除),受害者的数据最多写入两个不同的LFB中。攻击者线程的页面遍历可以轻松地覆盖LFB中受害者的数据,之后攻击者线程将无法在其中找到该数据。所有命中L1D缓存的加载请求都不经过LFB;它们有一个单独的路径,与来自LFB的路径复用。尽管如此,仍然存在一些情况,LFB中的陈旧数据(噪声)正在被推测性地转发到攻击者的逻辑核心,这可能是来自页面遍历(也可能是中断处理程序和硬件预取器)。
值得注意的是,在写回(WB)和写透(WT)的情况下,陈旧数据转发的频率比其他情况要低得多。这可以通过受害者吞吐量在这些情况下更高,并且实验可能会更早地终止来解释。
在所有其他情况(WC,UC和所有需要刷新的类型)中,每次加载都会错过缓存,并且必须通过LFB从主存储器获取数据到加载缓冲器中。以下事件序列会发生:
  1. 受害者的访问命中TLB,因为它们是相同有效虚拟页的。物理地址从TLB获取并提供给L1D,后者为请求分配LFB(由于未命中),并将物理地址与其他描述加载请求的信息一起写入LFB。此时,受害者的请求挂起在LFB中。由于受害者在每次加载后执行MFENCE,因此在任何给定周期内,来自受害者的LFB中最多只能有一个未完成的加载。
  2. 攻击者在兄弟逻辑核心上运行,并向L1D和TLB发出加载请求。每个加载都是到未映射用户页的,因此会导致故障。当它在TLB中未命中时,MMU告诉加载缓冲区应该阻止加载,直到地址转换完成。根据专利的第26段和其他Intel专利,这就是如何处理TLB未命中的。地址转换仍在进行中,加载被阻止。
  3. 受害者的加载请求接收其缓存行,该缓存行被写入为加载分配的LFB。加载请求请求的部分行被转发到MOB,同时该行被写入L1D高速缓存。之后,LFB可以解除分配,但不清除任何字段(除了指示其空闲的字段)。特别地,数据仍在LFB中。然后,受害者发送另一个加载请求,该请求也未命中缓存,因为它是不可缓存的或缓存行已被刷新。
  4. 攻击者的加载地址转换过程完成。MMU确定需要引发故障,因为物理页面不存在。但是,在加载即将退休(到达ROB顶部)之前,不会引发故障。在Intel处理器上,无效的转换不会缓存于MMU中。MMU仍然必须告诉MOB转换已完成,并在这种情况下,在ROB中对应条目中设置故障代码。似乎当ROB看到其中一个uop具有有效的故障/辅助代码时,它会禁用与该uop大小和地址相关的所有检查(可能还有ROB中的所有后续uop)。这些检查不再重要。假定禁用这些检查可以节省动态能量消耗。退休逻辑知道,当加载即将退休时,故障将被引发。同时,当MOB被告知转换已完成时,它像往常一样重新播放攻击者的加载。然而,这次提供了一些无效的物理地址给L1D高速缓存。通常,物理地址需要与来自同一逻辑核心的LFB中挂起的所有请求进行比较,以确保逻辑核心看到最新值。这是在查找L1D高速缓存之前或并行执行的。物理地址并不重要,因为比较逻辑已被禁用。但是,所有比较的结果的行为都像结果指示成功一样。如果至少有一个分配的LFB,则物理地址将与某个已分配的LFB匹配。由于受害者有一个未完成的请求,并且受害者的秘密可能已经被写入相同的LFB从先前的请求中,因此包含陈旧数据(陈旧数据是秘密)的相同部分的缓存行将转发给攻击者。请注意,攻击者可以控制缓存行内的
    如果攻击者的负载没有故障/协助,LFB将从MMU接收到一个有效的物理地址,并执行所有必要的正确性检查。这就是为什么负载必须故障/协助。
    下面引用的论文讨论了如何在同一线程中执行RIDL攻击:
    “我们通过在自己的线程中编写值并观察从同一线程泄漏的值来执行RIDL攻击。图3表明,如果我们不写入值(“无受害者”),我们只会泄漏零,但如果受害者和攻击者在同一硬件线程中运行(例如,在沙盒中),我们几乎总是泄漏秘密值。”
    我认为在这个实验中没有特权级别的变化。受害者和攻击者在同一个操作系统线程上运行,在从受害者返回到攻击者时,可能仍然存在一些未完成的请求(尤其是存储请求)在LFB中。请注意,在RIDL论文中,所有实验都启用了KPTI(与Fallout论文相反)。
    除了从LFB泄露数据外,MLPDS显示数据也可以从装载端口缓冲区泄露。这些包括行分割缓冲区和用于大于8字节的负载的缓冲区(我认为当负载uop的大小大于负载端口的大小时需要这些缓冲区,例如,在占用端口2个周期的SnB/IvB上的AVX 256b)。
    图5中的WB情况(无刷新)也很有趣。在这个实验中,受害线程将4个不同的值写入4个不同的高速缓存行,而不是从同一个高速缓存行读取。该图表明,在WB情况下,只有写入到最后一个高速缓存行的数据会泄漏给攻击者。解释可能取决于循环的不同迭代中高速缓存行是否不同,但不幸的是,论文中并不清楚。该论文称:
    “对于没有刷新的WB,仅对于最后一个高速缓存行有信号,这表明CPU在LFB的单个条目中执行写结合,然后将数据存储在高速缓存中。”
    如何将不同缓存行的写操作组合在同一个LFB中,然后再将数据存储在缓存中?这完全没有意义。一个LFB只能容纳一个缓存行和一个物理地址。不可能像那样组合写入操作。可能发生的情况是WB写操作被写入了为其RFO请求分配的LFB中。当无效的物理地址传输到LFB进行比较时,数据可能总是从最后分配的LFB中提供。这可以解释为什么只有第四个存储器写入的值会泄漏。

    有关MDS缓解措施的信息,请参见:什么是新的MDS攻击,以及如何缓解它们?。我在那里的答案仅讨论基于Intel微码更新的缓解措施(而不是非常有趣的“软件序列”)。


    以下图示展示了使用数据推测的易受攻击结构。

    enter image description here


2
@Hadi:为什么只对会导致故障/协助的负载进行这种推测? 我猜:可能总是在做,但如果检测到故障,则负载端口会放弃一切并继续前进(以节省电力),而“输出”缓冲区则保留当时的任何内容。非故障负载会为馈送负载结果输出缓冲器的多路复用器生成实际输入,其中包括LFB、L1d或存储转发。再次说明,这只是我的猜测;这是一个听起来合理且能解释观察结果的设计,考虑到我对CPU逻辑设计的了解很少。 - Peter Cordes
1
@MargaretBloom和Peter,ROB和RS之间的根本区别在于ROB是一个循环缓冲区,因此可以有效地维护程序顺序。RS无法有效地确定程序顺序。如果没有ROB,RS必须每个周期检查所有uops的顺序,以确定最老的uop是否准备好退役。这显然太低效了。ROB主要是为了这个目的而存在的。当然还有许多其他的差异,比如ROB维护不同的信息,RS条目可以更早地释放,但这些不是根本的区别。 - Hadi Brais
2
@MargaretBloom 关于重放,我回去查看了英特尔专利中关于重放的知识(有很多)。有四种不同类型的“重放”:(1)当调度器错误预测操作数到达转发网络的时间时,从RS进行重放;(2)当访问TLB时未命中时,从MOB进行重放;(3)当uop执行完成或正在使用错误操作数执行时,从uop缓存进行部分重放;(4)完全重放是管道刷新。显然,同一uop可以有多个并发重放。这是多么酷啊! - Hadi Brais
1
感谢@HadiBrais,唯一让我感到不对劲的是“同时,当MOB被告知翻译完成时,它像往常一样重放攻击者的负载。”但此时攻击者负载尚未执行,如果我正确理解了这些要点。顺便说一句:我认为发生的事情是调度程序分派负载和依赖的uops,假设负载将在L1中命中。也就是说,它将使依赖的uops从写回/转发网络获取其输入... - Margaret Bloom
1
该网络是从LFB、L1d和分裂寄存器(至少)馈入的MUX,如果依赖的uops从中读取时已知物理地址(感谢TLB命中),则正确选择真实源。但是,如果缺少物理地址(TLB未命中或非存在PTE)或负载故障(这将节省能量),则网络将重用上次使用的配置,泄漏数据。如果负载故障,则不会重播,如果得到协助,则在MMU向MOB发出物理地址信号时将重播。 - Margaret Bloom
显示剩余17条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接