22. 微架构数据采样 (MDS) 缓解¶
22.1. 概述¶
微架构数据采样 (MDS) 是 Intel CPU 中内部缓冲区的一系列侧信道攻击。变体包括
微架构存储缓冲区数据采样 (MSBDS) (CVE-2018-12126)
微架构填充缓冲区数据采样 (MFBDS) (CVE-2018-12130)
微架构加载端口数据采样 (MLPDS) (CVE-2018-12127)
微架构数据采样不可缓存内存 (MDSUM) (CVE-2019-11091)
MSBDS 会泄露存储缓冲区条目,这些条目可以被推测性地转发到依赖的加载(存储到加载转发)作为一种优化。转发也可以发生在不同内存地址的故障或辅助加载操作,这在某些条件下可以被利用。存储缓冲区在超线程之间进行分区,因此不可能进行跨线程转发。但是,如果一个线程进入或退出睡眠状态,则存储缓冲区会被重新分区,这可能会将一个线程的数据暴露给另一个线程。
MFBDS 会泄露填充缓冲区条目。填充缓冲区在内部用于管理 L1 未命中情况,并保存响应内存或 I/O 操作而返回或发送的数据。填充缓冲区可以将数据转发到加载操作,并将数据写入缓存。当填充缓冲区被释放时,它可以保留前面操作的过时数据,然后将其转发到故障或辅助加载操作,这在某些条件下可以被利用。填充缓冲区在超线程之间共享,因此可能发生跨线程泄漏。
MLPDS 会泄露加载端口数据。加载端口用于执行来自内存或 I/O 的加载操作。然后,接收到的数据被转发到寄存器文件或后续操作。在某些实现中,加载端口可能包含来自先前操作的过时数据,这些数据可以在某些条件下转发到故障或辅助加载,这最终可以被利用。加载端口在超线程之间共享,因此可能发生跨线程泄漏。
MDSUM 是 MSBDS、MFBDS 和 MLPDS 的一种特殊情况。来自内存的不可缓存加载发生故障或辅助时,可能会在微架构结构中留下数据,这些数据可能会稍后使用 MSBDS、MFBDS 或 MLPDS 使用的相同方法观察到。
22.2. 暴露假设¶
假设攻击代码驻留在用户空间或客户机中,但有一个例外。此假设背后的基本原理是,利用 MDS 所需的代码构造需要
控制加载以触发故障或辅助
具有公开推测性访问数据的披露小工具,以便通过侧信道进行消费。
控制披露小工具暴露数据的指针
不能 100% 排除内核中存在这种构造的可能性,但所涉及的复杂性使其极不可能。
有一个例外,即不受信任的 BPF。不受信任的 BPF 的功能有限,但需要彻底调查它是否可以用于创建这种构造。
22.3. 缓解策略¶
所有变体都具有相同的缓解策略,至少对于单 CPU 线程的情况(SMT 关闭):强制 CPU 清除受影响的缓冲区。
这是通过结合微码更新使用未使用的且过时的 VERW 指令来实现的。微码在执行 VERW 指令时清除受影响的 CPU 缓冲区。
对于虚拟化,有两种方法可以实现 CPU 缓冲区清除。要么是修改后的 VERW 指令,要么是通过 L1D Flush 命令。后者在启用 L1TF 缓解时发出,因此可以避免额外的 VERW。如果 CPU 不受 L1TF 的影响,则需要发出 VERW。
如果在没有微码更新的 CPU 上执行带有提供的段选择器参数的 VERW 指令,则除了少量毫无意义地浪费的 CPU 周期外,没有其他副作用。
这不会防止跨超线程攻击,除非 MSBDS,它仅在其中一个超线程进入 C 状态时才可跨超线程利用。
内核提供一个函数来调用缓冲区清除
mds_clear_cpu_buffers()
此外,宏 CLEAR_CPU_BUFFERS 可以在退出到用户路径的 ASM 中后期使用。除了 CFLAGS.ZF 之外,此宏不会破坏任何寄存器。
缓解在内核/用户空间、虚拟机管理程序/客户机和 C 状态(空闲)转换时调用。
作为解决虚拟化场景的特殊怪癖,其中主机已更新微码,但虚拟机管理程序(尚未)向客户机公开 MD_CLEAR CPUID 位,内核会发出 VERW 指令,希望它实际上可以清除缓冲区。状态会相应反映。
根据目前的知识,内核本身内部不需要额外的缓解措施,因为暴露泄露数据的必要小工具无法以允许从恶意用户空间或 VM 客户机进行利用的方式进行控制。
22.4. 内核内部缓解模式¶
关闭
缓解已禁用。要么 CPU 不受影响,要么在内核命令行上提供了 mds=off
完全
缓解已启用。CPU 受影响,并且 MD_CLEAR 在 CPUID 中通告。
vmwerv
缓解已启用。CPU 受影响,并且 MD_CLEAR 未在 CPUID 中通告。这主要用于虚拟化场景,其中主机已更新微码,但虚拟机管理程序未在 CPUID 中公开 MD_CLEAR。这是一种尽力而为的方法,没有保证。
如果 CPU 受影响,并且内核命令行上未提供 mds=off,则内核会根据 MD_CLEAR CPUID 位的可用性选择适当的缓解模式。
22.5. 缓解点¶
22.5.1. 1. 返回用户空间¶
当从内核转换到用户空间时,当缓解未在内核命令行上禁用时,受影响的 CPU 上的 CPU 缓冲区会被刷新。缓解通过特性标志 X86_FEATURE_CLEAR_CPU_BUF 启用。
在还原用户寄存器后,在转换到用户空间之前调用缓解。这样做是为了尽量减少在 VERW 之后(例如通过 VERW 之后的 NMI)访问内核数据的窗口。
未处理的特殊情况 返回到内核的中断不会清除 CPU 缓冲区,因为预计退出到用户路径会执行此操作。但是,可能存在一种情况,即在退出到用户路径清除缓冲区后,在内核中生成 NMI。这种情况未处理,返回到内核的 NMI 不会清除 CPU 缓冲区,因为
在 VERW 之后但在返回到用户空间之前,很少会获得 NMI。
对于非特权用户,没有已知的方法可以使 NMI 不那么罕见或以其为目标。
需要大量这些精确计时的 NMI 才能发起实际攻击。据推测,带宽不足。
有问题的 NMI 在 VERW 之后发生,即在还原用户状态并且大部分有趣的数据已被清理时发生。剩下的是 NMI 接触的数据,这些数据可能或可能没有任何意义。
22.5.2. 2. C 状态转换¶
当 CPU 进入空闲状态并进入 C 状态时,当 SMT 处于活动状态时,需要在受影响的 CPU 上清除 CPU 缓冲区。这解决了当其中一个超线程进入 C 状态时存储缓冲区的重新分区问题。
当 SMT 不活动时,即 CPU 不支持它或所有兄弟线程都处于脱机状态时,不需要清除 CPU 缓冲区。
空闲清除功能仅在受 MSBDS 影响而未受任何其他 MDS 变体影响的 CPU 上启用。由于填充缓冲区和加载端口是共享的,其他 MDS 变体无法防止跨超线程攻击。因此,在受其他变体影响的 CPU 上,空闲清除将是一种表面文章,因此不会被激活。
该功能的调用由静态键 mds_idle_clear 控制,该键根据所选的缓解模式和系统的 SMT 状态进行切换。
缓冲区清除仅在进入 C 状态之前调用,以防止空闲 CPU 中的陈旧数据在存储缓冲区重新分区后泄漏到超线程兄弟 CPU,并且所有条目都可供非空闲兄弟 CPU 使用。
当从空闲状态返回时,存储缓冲区会再次分区,以便每个兄弟 CPU 拥有一半的可用空间。然后,从空闲状态返回的 CPU 可能会推测性地暴露给兄弟 CPU 的内容。缓冲区会在退出用户空间或 VMENTER 时刷新,因此用户空间或客户机中的恶意代码无法推测性地访问它们。
此缓解措施已连接到所有 halt()/mwait() 变体,但不包括传统的 ACPI IO 端口机制,因为 ACPI 空闲驱动程序已在 2010 年左右被 intel_idle 驱动程序取代,并且在所有预期在微代码中获得 MD_CLEAR 功能的受影响 CPU 上都是首选。除此之外,IO 端口机制是一个传统接口,仅在较旧的系统上使用,这些系统要么不受影响,要么不再接收微代码更新。