MSBDS(Fallout)背后的微架构细节是什么?

What are the microarchitectural details behind MSBDS (Fallout)?

CVE-2018-12126 已分配给 MSBDS(微架构 StoreBuffer 数据采样),属于新创建的英特尔处理器的漏洞 MDS (Microarchitectural Data Sampling) class.

我正在尝试获取这些漏洞背后的微体系结构细节。我从 MSBDS 开始,也称为 Fallout (cfr Meltdown),它允许攻击者泄露存储缓冲区的内容。

出于某种原因,讨论微架构细节的网络安全论文通常不够精确。
幸运的是,MSBDS 论文引用了 patent US 2008/0082765 A1(从中拍摄图片)。

根据我收集到的信息,似乎在 MSBDS 的情况下,漏洞存在于内存消歧算法如何处理具有无效物理地址的负载。

这是用于检查存储缓冲区中的负载是否匹配的算法:

302 检查加载引用的页面偏移量是否与存储缓冲区中任何先前存储引用的页面偏移量匹配。
如果此检查失败,则负载与任何商店都不匹配,可以在 304.
处执行(已发送) 如果 302 检查,则负载的 虚拟地址 的上半部分将根据虚拟地址检查 1店铺地址。
如果找到匹配项,则负载匹配,并且在 308 转发它需要的数据或负载本身被阻止(直到匹配的存储提交)如果转发是不可能的(例如窄存储到更宽的负载)。
注意同一个虚拟地址可以映射到两个不同的物理地址(在不同的时间但在存储转发内window)。不正确的转发不是通过这个算法来防止的,而是通过耗尽存储缓冲区(例如使用正在序列化的 mov cr3, X2.
如果加载的虚拟地址与存储的任何虚拟地址都不匹配,则在 310.
处检查物理地址 这对于处理不同虚拟地址映射到同一物理地址的情况是必要的。

[0026] 段添加:

In one embodiment, if there is a hit at operation 302 and the physical address of the load or the store operations is not valid, the physical address check at operation 310 may be considered as a hit and the method 300 may continue at operation 308. In one instance, if the physical address of the load instruction is not valid, the load instruction may be blocked due to DTLB 118 miss. Further, if the physical address of the store operation is not valid, the outcome may be based on the finenet hit/miss results in one embodiment or the load operation may be blocked on this store operation until the physical address of the store operation is resolved in an embodiment.

这意味着如果物理地址不可用,CPU 将只考虑地址的低 (12) 位3.
考虑到下面几行正在解决 TLB 未命中的情况,这只剩下访问的页面不存在的情况。

这确实是研究人员提出攻击的方式:

char * victim_page = mmap (... , PAGE_SIZE , ...) ;
char * attacker_page = mmap (... , PAGE_SIZE, ...) ;

mprotect ( attacker_page , PAGE_SIZE , PROT_NONE ) ;

offset = 7;
victim_page [ offset ] = 42;

//Why people hate specpolines??
if ( tsx_begin () == 0) {
  //Read the stale value and exfiltrate it with a spectre gadget
  memory_access ( lut + 4096 * attacker_page [ offset ]) ;
  tsx_end () ;
}

//Reload phase of FLUSH+RELOAD
for ( i = 0; i < 256; i ++) {
  if ( flush_reload ( lut + i * 4096) ) {
     report ( i ) ;
  }
}

我不确定还有什么会引发无效的物理地址(访问特权页面 return 正确的物理地址)。

真的是无效物理地址的处理触发了MSBDS漏洞?


1SBA(Store Buffer Address)组件保存了一个store的虚拟地址和物理地址,可能只是物理地址的一个片段(其余的在一个专用数组,可能命名为物理地址缓冲区)。
2我不清楚是否真的有可能通过将页面 table 条目更改为指向其他地方然后发出 invlpg.[= 来触发错误转发52=] 3我的理由是,由于我们不是在可恢复的情况下,负载是错误的,跳过另一个检查是值得的,冒着错误转发的风险性能-wise 因为它会使负载更早退出(和故障)。

内存一致性要求加载 uop 获取最近存储到目标内存位置的值。因此,内存顺序缓冲区 (MOB) 必须确定加载是否与程序顺序中任何较早的存储 uop 重叠。加载缓冲区和存储缓冲区都是循环的,并且每个加载都标记有按程序顺序在加载之前的最新存储的 ID(分配器在必须分配负载时知道它分配的最后一个存储的 ID ).这使 MOB 能够正确确定哪些存储先于哪些加载。

从 Intel Core 微体系结构和 Goldmont 微体系结构开始,调度程序包括一个推测性内存消歧 (SMD) 逻辑,该逻辑使用负载的 IP 来决定是否允许分派负载 out-of-order到所有早期商店的 STA uops。这类似于分支预测如何使用正在获取的当前 16 字节块的 IP 来预测控制流,除了在这种情况下 IP 用于内存消歧。如果RS中没有等待的STA,或者如果所有STA都可以在与负载uop相同的周期内被调度,则忽略SMD结果并调度负载。否则,如果 SMD 决定阻塞负载,则调度程序仅在所有较早的 STA 已被调度或将在与负载相同的周期内被调度时才调度负载。对于一些负载uops,SMD总是阻断RS中的负载。

当负载uop被分配到负载AGU端口之一时,负载的有效地址,即线性地址,使用指定的段基址、基址寄存器操作数、变址寄存器操作数、比例和移位。同时,store buffer中可以有store。将加载的线性地址与执行了 STA 微指令的所有较早存储的线性地址进行比较(即,存储的线性地址可用)。可能还需要比较物理地址,但此时负载的物理地址仍然不可用(这种情况在专利中称为无效物理地址)。为了最小化加载的可观察延迟,MOB 仅使用加载的线性地址的最低有效 12 位和每个较早的存储执行快速比较。有关此比较的更多信息,请参阅 L1 memory bandwidth: 50% drop in efficiency using addresses which differ by 4096+64 bytes(但此处未讨论屏蔽的微指令)。这种逻辑称为松散网络,它构成了推测记忆消歧机制的另一部分。自 Pentium Pro(包括 in-order Bonnell)以来,所有 Intel 微架构都支持松散网络,但确切的实现已经改变,因为单个加载或存储 uop 可以操作的数据大小增加了,并且由于从 Pentium II 开始引入屏蔽内存 uops。在loose net操作的同时,将load的线性地址发送给TLB,获取对应的物理地址并进行必要的页属性检查,同时进行段检查。

如果负载不与在根据松散净结果分派负载时其地址已知的任何较早商店重叠,则向 L1D 发送负载请求。我们已经从 RIDL 漏洞中了解到,即使没有来自 TLB 的有效物理地址,某些数据也可能会被转发到 MOB,但前提是负载导致故障或协助。在 first-level TLB 未命中时,加载在加载缓冲区中被阻塞,因此它不会继续其 L1D 访问。稍后,当请求的页面条目到达 first-level TLB 时,MOB 会被告知该虚拟页面的地址,它依次检查该页面上阻塞的所有加载和存储,并通过重放 uops 解除阻塞根据 TLB 端口的可用性。

我认为松散网络只需要一个周期即可将给定负载的地址与存储缓冲区中的任意数量的存储进行比较,并确定比负载更旧的最年轻的重叠存储(如果有的话)。查找 first-level TLB 并在命中时向 L1D 提供物理地址的过程应该只需要一个周期。这就是如何获得 4 个周期的 best-case load-to-use 延迟(这还需要(1)正确推测物理页面地址,(2)没有索引或有索引的 base+disp 寻址模式一个零索引,和 (3) 一个零段基地址,否则会有至少一个周期的惩罚)。有关更多信息,请参阅评论中的讨论。

注意,如果加载uop在loose net中丢失,则可以得出结论,加载不与任何先前的存储重叠,但前提是所有早期uop的STA在加载uop时已经执行派遣。两个最低有效12位不同的线性地址是不可能重叠的。

若松净结果表示负载与较早的存储重叠,MOB 并行执行两件事。其中之一是内存消歧过程继续使用精细网络(即全线性地址比较)。如果负载在精细网络中丢失,则在可用时比较物理地址。否则,如果负载命中细网,则负载和存储重叠。请注意,x86 ISA 需要在更改分页结构后使用完全序列化指令。所以fine net hit case不需要比较物理地址。除此之外,无论何时调度新的 STA uop,都会重复整个过程,但这次所有负载都在加载缓冲区中。将所有这些比较的结果组合在一起,并且在针对所有早期存储检查加载后,最终结果决定了如何正确执行加载 uop。

同时,MOB 推测与负载一起进入松散网络的商店具有应转发给负载的值。如果加载和存储到同一个虚拟页面,那么推测是正确的。如果加载和存储到不同的虚拟页面,但虚拟页面映射到同一个物理页面,推测也是正确的。否则,如果加载和存储到不同的物理页面,则 MOB 已经混乱,导致称为 4K 别名的情况。但是等等,让我们回滚一点。

可能无法将存储数据转发到负载。例如,如果加载没有完全包含在存储中,那么它必须等到存储被提交,然后允许加载继续并从缓存中获取数据。另外,如果商店的 STD uop 尚未执行怎么办(例如,它取决于长延迟 uop)?通常情况下,只有满足存储转发的要求时,才会从存储缓冲区中转发数据。然而,MSBDS 漏洞表明情况并非总是如此。特别地,当负载导致故障或协助时,存储缓冲器可以将数据转发给负载而不进行任何存储转发检查。来自英特尔关于 MDS 的文章:

It is possible that a store does not overwrite the entire data field within the store buffer due to either the store being a smaller size than the store buffer width, or not yet having executed the data portion of the store. These cases can lead to data being forwarded that contains data from older stores.

很明显,即使STD uop还没有执行,数据也可能被转发。但是数据从哪里来呢?好吧,存储缓冲区条目的数据字段在释放时不会被清除。数据字段的大小等于存储微指令的宽度,这可以通过测量执行最宽可用存储指令(例如,从 XMM、YMM 或 ZMM 寄存器)所需的存储微指令数来确定。这在 Haswell 上似乎是 32 字节,在 Skyake-SP 上是 64 字节。存储缓冲区条目的每个数据字段都那么大。由于它永远不会被清除,它可能会保存一些随机组合的数据,这些数据来自恰好分配在该存储缓冲区条目中的存储。当负载打在松散的网络中并会导致fault/assist时,负载指定宽度的数据将从存储缓冲区转发到负载,甚至不检查STD的执行或宽度店铺。这就是负载如何从一个或多个存储中获取数据的方式,这些数据甚至可能在十亿条指令之前就已提交。与 MLBDS 类似,转发的数据的某些部分或整个数据可能是陈旧的(即不属于占用条目的存储)。

这些细节实际上只是英特尔提供的,而不是辐射论文。在本文中,作者在禁用了 KPTI 的系统上进行了一项实验(第 4 节)(我将解释原因),但他们并未利用 Meltdown 漏洞。以下是实验的工作原理:

  1. 攻击者执行了一系列存储,所有这些都未命中缓存层次结构。存储的数量至少与存储缓冲区条目的数量一样大。
  2. 调用内核模块,执行一系列存储,每个存储到不同内核页面中的不同偏移量。存储的值是已知的。存储的数量在 1-50 之间变化,如图 5 所示。之后,内核模块 returns 给攻击者。
  3. 攻击者对用户页面(不同于内核页面)执行一系列加载以达到相同的偏移量。每个用户页面仅分配在虚拟地址 space 中,并已取消访问权限(通过调用 mprotect(...,PROT_NONE),将其标记为 User 和 Not Present)。 Table 1 表示不存在的主管页面不起作用。加载的次数与内核模块执行的存储的次数相同。然后使用传统的 FLUSH+RELOAD 攻击泄露加载的值。

第一步尝试让存储缓冲区尽可能多地被占用,以延迟从内核模块提交存储。请记住,错误存储转发仅适用于已占用的存储缓冲区条目。第一步有效,因为商店必须按顺序提交。第三步,重要的是获得宽松的网络点击率请注意,在这个实验中,作者并没有考虑泄露任何陈旧数据,他们只是想从内核存储中获取数据,这些数据有望仍在存储缓冲区中。更改当前特权级别时,在执行新特权级别中的任何指令之前,所有指令都将退出。存储可以快速退出,甚至在 RFO 请求完成之前,但它们仍然必须在存储缓冲区中等待按顺序提交。人们认为以这种方式在存储缓冲区中存储来自不同特权级别的存储不是问题。但是,当攻击者开始执行负载时,如果与当前正在分派的负载具有相同偏移量的存储仍在存储缓冲区中,则当(非陈旧的)数据被推测性转发时,就会发生松散的网络命中。剩下的你就知道了。

启用 KPTI 后,大多数内核页面与用户页面位于不同的虚拟地址 space。因此,当从内核模块返回时,内核必须通过向 CR3 寄存器写入一个值来切换地址 spaces。但这是一个序列化操作,这意味着它将暂停管道,直到所有(内核)存储都被提交。这就是为什么作者需要禁用 KPTI 才能使他们的实验工作(即,存储缓冲区将为空)。不幸的是,由于 Coffee Lake R 具有针对 Meltdown 的硬件缓解措施,Linux 内核默认禁用该处理器上的 KPTI。这就是为什么作者说硬件缓解措施使处理器更容易受到攻击。

Intel 文章(而非论文)中描述的内容表明 MSBDS 比这更危险:faulting/assisting 加载也可能从存储缓冲区泄漏陈旧数据。英特尔的文章还表明,MSBDS 跨同级逻辑内核工作:当一个逻辑内核进入睡眠状态时,为其静态分配的存储缓冲区条目可能会被另一个逻辑内核使用。稍后,如果逻辑核心再次激活,存储缓冲区将被静态分区,这可能会使该核心从其条目中泄漏由另一个核心写入的陈旧数据。

所有这些都表明启用 KPTI 不足以缓解 MSBDS。此外,第 6 节的论文中推荐的缓解措施(在跨越安全边界时使用 MFENCE 刷新存储缓冲区)也不够充分。讨论了适当的 MDS 缓解措施 here

我不知道第 3.2 节的作者如何从英特尔专利的以下引用中得出结论:

if there is a hit at operation 302 [partial match using page offsets] and the physical address of the load or the store operations is not valid, the physical address check at operation 310 [full physical address match] may be considered as a hit

以下:

That is, if address translation of a load μOP fails and the 12 least significant bits of the load address match those of a prior store, the processor assumes that the physical addresses of the load and the store match and forwards the previously stored value to the load μOP.

整个专利没有提到比较 12 位,也没有说负载必须出错才能发生错误的存储转发。另外,结论本身也不正确,因为12个最低有效位不必完全匹配,负载也不必出错(但只有出错时攻击才有效)。

MSBDS 与 Meltdown 的不同之处在于,攻击者从位于单独虚拟地址 space 中的内核页面泄漏数据。 MSBDS 与 SSB 的不同之处在于,攻击者误训练 SMD,使其在调度负载之前的所有 STA 之前调度负载。这样一来,负载不会命中松散网络的可能性就会降低,这使得 MOB 将负载下发到 L1D 缓存,并根据程序顺序获取一个可能不是最新值的值。可以通过将 IA32_SPEC_CTRL[2] 设置为 1 来禁用 SMD。当禁用 SMD 时,调度程序会像在 Pentium Pro 中一样处理加载微指令。

值得注意的是,有些加载和存储微指令的工作方式与我上面描述的不同。示例包括来自 MFENCESFENCECLFLUSH 的内存微指令。但它们在这里不相关。