首页 > 解决方案 > 关于 RIDL 漏洞和负载的“重放”

问题描述

我正在尝试了解RIDL类漏洞。

这是一类能够从各种微架构缓冲区读取陈旧数据的漏洞。
今天已知的漏洞利用:LFB、加载端口、eMC 和存储缓冲区。

链接的论文主要集中在 LFB。

我不明白为什么 CPU 会用 LFB 中的陈旧数据来满足负载。
我可以想象,如果负载在 L1d 中命中,它会在内部“重放”,直到 L1d 将数据带入 LFB 信号通知 OoO 核心停止“重放”它(因为读取的数据现在有效)。

但是我不确定“重播”实际上是什么意思。
我认为负载被分派到具有负载能力的端口,然后记录在负载缓冲区(在 MOB 中),并最终根据需要保持,直到它们的数据可用(由 L1 发出信号)。
所以我不确定“重播”是如何发挥作用的,此外,为了让 RIDL 工作,每次“播放”负载的尝试也应该解除阻塞相关指令。
这对我来说似乎很奇怪,因为 CPU 需要跟踪加载正确完成后要重播的指令。

关于 RIDL 的论文使用此代码作为示例(不幸的是,我不得不将其粘贴为图像,因为 PDF 布局不允许我复制它):

RIDL 片段

它可以工作的唯一原因是,如果 CPU 将首先用过时的数据满足第 6 行的负载,然后重放它。
这似乎证实了以下几行:

具体来说,我们可能期望两个访问速度很快,而不仅仅是与泄露信息对应的一个。毕竟,当处理器发现错误并在第 6 行以正确的值重新启动时,程序也会使用该索引访问缓冲区。

但我希望 CPU 在转发 LFB(或任何其他内部缓冲区)中的数据之前检查负载的地址。
除非 CPU 实际重复执行加载,直到它检测到加载的数据现在有效(即重放)。
但是,为什么每次尝试都会解除对相关指令的阻塞?

重放机制究竟是如何工作的(如果它存在的话),以及它如何与 RIDL 漏洞交互?

标签: x86cpucpu-architecturemicro-architecturecpu-mds

解决方案


重播 = 再次从 RS(调度程序)调度。(这不是你整个问题的完整答案,只是关于回放是什么的部分。虽然我认为这涵盖了大部分内容,包括解除阻塞依赖的微指令。)

这个答案的一部分对负载重播有误解。

请参阅聊天中的讨论- 重播依赖于拆分或缓存未命中负载的微指令,但不会重播负载本身。(除非负载在循环中依赖于自身,就像我一直在做的测试 >.<)。TODO:修复此答案的其余部分和其他内容。


事实证明,缓存未命中负载不仅位于负载缓冲区中,并且在数据到达时唤醒相关的微指令。调度程序必须重新分派加载 uop 以实际读取数据并写回物理寄存器。(并将其放在转发网络上,依赖的微指令可以在下一个循环中读取它。)

因此,L1 未命中/L2 命中将导致分派 2 倍的负载微指令。(调度器是乐观的,并且 L2 在核心上,因此 L2 命中的预期延迟是固定的,这与非核心响应的时间不同。IDK 如果调度器继续对从 L3 到达某个时间的数据持乐观态度。 )


RIDL 论文提供了一些有趣的证据,表明加载微指令确实直接与 LFB 交互,而不是等待传入的数据被放入 L1d 并从那里读取。


对于缓存行拆分加载,我们可以在实践中最容易地观察重播,因为重复导致这种情况比缓存未命中更简单,占用的代码更少。对于仅拆分负载的循环,计数uops_dispatched_port.port_2port_3将是大约两倍。(我已经在 Skylake 的实践中验证了这一点,使用与如何在 x86_64 上准确地对未对齐访问速度进行基准测试相同的循环和测试程序)

检测到拆分的加载(仅在地址计算之后才可能)将加载数据的第一部分,而不是向 RS 发送成功完成的信号,将此结果放入拆分缓冲区1以与数据连接uop 第二次从第二个缓存行开始调度。(假设这两个时间都不是缓存未命中,否则它也需要重放。)


当负载微指令分派时,调度程序预计它将在 L1d 中命中并分派相关微指令,以便它们可以在负载将它们放在该总线上的周期中从转发网络读取结果。

如果这没有发生(因为加载数据还没有准备好),那么依赖的微指令也必须被重放。dispatch同样,IIRC 这可以通过端口的 perf 计数器观察到。


现有问答与英特尔 CPU 上的 uop 重放证据:


脚注1:

我们知道拆分缓冲区的数量是有限的;有一个ld_blocks.no_sr负载计数器,因为缺少一个而停止。我推断它们在装载端口中,因为这是有道理的。重新分派相同的加载 uop 会将其发送到相同的加载端口,因为 uop 在发布/重命名时分配给端口。虽然也许有一个共享的拆分缓冲区池。


RIDL:

乐观调度是产生问题的机制的一部分。更明显的问题是让后来的微指令的执行看到来自 LFB 的“垃圾”内部值,就像在 Meltdown 中一样。

http://blog.stuffedcow.net/2018/05/meltdown-microarchitecture/甚至显示 PPro 中的崩溃负载暴露了各种微架构状态,就像最新处理器中仍然存在的这个漏洞一样。

Pentium Pro 从字面上理解“负载值是无关紧要的”。对于所有禁止的加载,加载单元完成并产生一个值,该值似乎是从处理器的各个部分获取的各种值。该值会有所不同,并且可能是不确定的。返回的值似乎都不是内存数据,因此 Pentium Pro 似乎不易受到 Meltdown 的影响。

可识别的值包括负载的 PTE(至少在最近几年,它本身被认为是特权信息)、第 12 个最近存储的值(存储队列有 12 个条目),以及很少来自某处的段描述符.

(后来的 CPU,从 Core 2 开始,暴露了 L1d 缓存中的值;这就是 Meltdown 漏洞本身。但是 PPro / PII / PIII 不容易受到 Meltdown 的影响。在这种情况下,它显然容易受到 RIDL 攻击。)

因此,将一些微架构状态暴露给推测执行的英特尔设计理念是相同的。

在硬件中将其压缩为 0 应该很容易解决;加载端口已经知道它不成功,因此根据成功/失败屏蔽加载数据应该希望只增加几个额外的门延迟,并且在不限制时钟速度的情况下是可能的。(除非加载端口中的最后一个流水线阶段已经是 CPU 频率的关键路径。)

因此,对于未来的 CPU,这可能是一个简单而廉价的硬件修复,但很难通过现有 CPU 的微码和软件来缓解。


推荐阅读