幽灵侧信道

幽灵是一种利用现代 CPU 上的分支预测和推测执行来读取内存的侧信道攻击,可能会绕过访问控制。推测执行侧信道漏洞利用不会修改内存,而是尝试推断内存中的特权数据。

本文档涵盖幽灵变体 1 和幽灵变体 2。

受影响的处理器

推测执行侧信道方法会影响各种现代高性能处理器,因为大多数现代高速处理器都使用分支预测和推测执行。

以下 CPU 容易受到攻击

  • 英特尔酷睿、凌动、奔腾和至强处理器

  • AMD Phenom、EPYC 和 Zen 处理器

  • IBM POWER 和 zSeries 处理器

  • 高端 ARM 处理器

  • Apple CPU

  • 高端 MIPS CPU

  • 很可能还有其他大多数高性能 CPU。请联系您的 CPU 供应商了解详情。

可以从 sysfs 中的幽灵漏洞文件中读取处理器是否受影响。请参阅幽灵系统信息

问题

CPU 使用推测操作来提高性能。这可能会在处理器的缓存、缓冲区和分支预测器中留下内存访问或计算的痕迹。恶意软件可能能够影响推测执行路径,然后使用 CPU 缓存和缓冲区中推测执行的副作用来推断在推测执行期间访问的特权数据。

幽灵变体 1 攻击利用条件分支的推测执行,而幽灵变体 2 攻击利用间接分支的推测执行来泄露特权内存。请参阅[1] [5] [6] [7] [10] [11]

幽灵变体 1(边界检查绕过)

边界检查绕过攻击[2]利用推测执行绕过了用于内存访问边界检查的条件分支指令(例如,检查数组的索引是否导致在有效范围内的内存访问)。这会导致对无效内存(具有超出范围的索引)进行内存访问,这些内存访问是在验证检查解析之前推测性地完成的。此类推测性内存访问可能会留下副作用,从而创建侧信道,从而将信息泄露给攻击者。

幽灵变体 1 攻击有一些扩展,用于通过网络读取数据,请参阅[12]。然而,此类攻击是困难的、低带宽的、脆弱的,并且被认为是低风险的。

请注意,尽管名称为“边界检查绕过”,但幽灵变体 1 不仅仅是关于用户控制的数组边界检查。它可以影响任何条件检查。内核入口代码中断、异常和 NMI 处理程序都具有条件 swapgs 检查。在幽灵 v1 的上下文中,这些可能会有问题,因为内核代码可以在用户 GS 的情况下推测性地运行。

幽灵变体 2(分支目标注入)

分支目标注入攻击利用间接分支的推测执行[3]。处理器内部用于猜测间接分支目标的间接分支预测器可能会受到攻击者的影响,从而导致小工具代码被推测性地执行,从而暴露受害者访问的敏感数据。可以测量推测执行期间留在 CPU 缓存中的副作用,以推断数据值。

在幽灵变体 2 攻击中,攻击者可以通过毒害用于预测间接分支地址的 CPU 的分支目标缓冲区,将受害者中的推测性间接分支引导到小工具代码。这种毒害可以通过间接分支到现有代码来完成,间接分支的地址偏移量在攻击者的控制之下。由于受影响硬件上的分支预测没有完全消除分支地址的歧义并使用偏移量进行预测,因此这可能会导致特权代码的间接分支跳转到具有相同偏移量的小工具代码。

最有用的工具采用攻击者控制的输入参数(例如寄存器值),以便可以控制内存读取。没有输入参数的小工具是可能的,但是攻击者对可以读取的内存的控制非常少,从而降低了攻击揭示有用数据的风险。

另一种变体 2 攻击向量是攻击者毒害返回堆栈缓冲区 (RSB) [13],以导致推测性子例程返回指令执行转到小工具。攻击者不平衡的子例程调用指令可能会“毒害”返回堆栈缓冲区中的条目,这些条目稍后会被受害者的子例程返回指令使用。可以通过在上下文切换或虚拟机 (VM) 退出时刷新返回堆栈缓冲区来缓解此攻击。

在具有同步多线程 (SMT) 的系统上,可能存在来自同级线程的攻击,因为一级缓存和分支目标缓冲区 (BTB) 可能在 CPU 核心中的硬件线程之间共享。在同级线程上运行的恶意程序可能会影响其对等方的 BTB,从而将其中间分支推测导向小工具代码,并测量一级缓存中遗留的推测执行的副作用,以推断受害者的​​数据。

另一种变体 2 攻击向量是攻击者污染分支历史缓冲区 (BHB),以推测性地将间接分支导向特定的分支目标缓冲区 (BTB) 条目,即使该条目与间接分支的源地址无关。具体而言,即使存在增强型 IBRS,BHB 也可能在特权级别之间共享。

以前,唯一已知的真实 BHB 攻击向量是通过非特权 eBPF。进一步的研究发现了不需要非特权 eBPF 的攻击。为了完全缓解 BHB 攻击,建议设置 BHI_DIS_S 或使用 BHB 清除序列。

攻击场景

已预料到以下攻击场景列表,但可能未涵盖所有可能的攻击向量。

1. 用户进程攻击内核

幽灵变体 1

攻击者在系统调用期间通过寄存器或内存中的已知地址将参数传递给内核。内核稍后可能会将此参数用作数组的索引或派生指针以进行幽灵变体 1 攻击。索引或指针无效,但在推测执行所采用的代码分支中绕过了边界检查。这可能会导致访问和泄露特权内存。

对于已识别出数据指针可能受幽灵攻击影响的内核代码,新的“nospec”访问器宏用于防止推测性加载数据。

幽灵变体 1 (swapgs)

攻击者可以训练分支预测器以推测性地跳过中断或异常的 swapgs 路径。如果他们将 GS 寄存器初始化为用户空间值,如果推测性地跳过了 swapgs,则推测窗口中后续与 GS 相关的 percpu 访问将使用攻击者控制的 GS 值完成。这可能会导致访问和泄露特权内存。

例如

if (coming from user space)
    swapgs
mov %gs:<percpu_offset>, %reg
mov (%reg), %reg1

当来自用户空间时,CPU 可以推测性地跳过 swapgs,然后使用用户 GS 值进行推测性 percpu 加载。因此,用户可以推测性地强制读取任何内核值。如果存在使用 percpu 值作为另一个加载/存储中的地址的小工具,则内核值的内容可能通过 L1 侧信道攻击变得可见。

当来自内核空间时,也存在类似的攻击。CPU 可以推测性地执行 swapgs,导致用户 GS 用于其余的推测窗口。

幽灵变体 2

幽灵变体 2 攻击者可以在发出系统调用以发起攻击之前污染分支目标缓冲区 (BTB)。进入内核后,内核可以在推测执行时在间接跳转上使用被污染的分支目标缓冲区并跳转到小工具代码。

如果攻击者试图控制推测执行期间泄露的内存地址,他还需要通过寄存器或内存中的已知地址将参数传递给小工具。小工具执行后,他可以测量副作用。

内核可以通过对所有间接分支使用返回跳板(也称为“retpoline”)[3] [9]来保护自己免受使用被污染的分支目标缓冲区条目的影响。返回跳板会捕获推测执行路径,以防止在推测执行期间跳转到小工具代码。硬件中具有增强型间接分支限制推测 (Enhanced IBRS) 的 x86 CPU 应使用此功能来缓解幽灵变体 2,而不是 retpoline。增强型 IBRS 比 retpoline 更高效。

固件中可能存在小工具代码,恶意用户进程可以通过幽灵变体 2 攻击来利用该代码。为了缓解 x86 上的此类攻击,内核调用任何固件代码之前会启用间接分支限制推测 (IBRS) 功能。

2. 用户进程攻击另一个用户进程

恶意用户进程可以尝试攻击另一个用户进程,可以通过在同一硬件线程上的上下文切换,也可以从同步多线程 (SMT) 系统上共享物理处理器核心的同级超线程进行攻击。

幽灵变体 1 攻击通常需要在进程之间传递参数,这需要数据传递关系,例如远程过程调用 (RPC)。这些参数在小工具代码中用于派生访问被攻击进程中特权内存的无效数据指针。

幽灵变体 2 攻击可以由恶意进程通过污染分支目标缓冲区来发起。这可能会影响受害者进程的间接分支目标,该进程要么稍后在同一硬件线程上运行,要么在共享同一物理核心的同级硬件线程上同时运行。

用户进程可以通过使用 prctl() 系统调用来禁用自身的间接分支推测,从而保护自己免受幽灵变体 2 攻击。管理员还可以通过禁用进程的间接分支推测来将不安全的进程与污染分支目标缓冲区隔离。这会因不使用间接分支推测和清除分支目标缓冲区而导致性能损失。当 x86 上启用 SMT 时,对于禁用间接分支推测的进程,将启用单线程间接分支预测器 (STIBP) [4],以防止同级线程控制分支目标缓冲区。此外,在上下文切换到此类进程和从此类进程切换时,将发出间接分支预测屏障 (IBPB) 以清除分支目标缓冲区。

在 x86 上,返回堆栈缓冲区会在上下文切换时被填充。这可以防止在切换到更深层的调用堆栈时返回堆栈缓冲区下溢时将分支目标缓冲区用于分支预测。上一个进程留在返回堆栈缓冲区中的任何被污染的条目也将被清除。

用户程序应使用地址空间随机化,使攻击更困难(设置 /proc/sys/kernel/randomize_va_space = 1 或 2)。

3. 虚拟化来宾攻击主机

攻击机制类似于用户进程如何攻击内核。通过超调用或其他虚拟化退出路径进入内核。

对于幽灵变体 1 攻击,恶意来宾可以通过超调用传递参数(例如,在寄存器中),以派生无效指针以在进入内核后推测进入特权内存。对于已识别出此类内核代码的位置,使用 nospec 访问器宏来停止推测性内存访问。

对于幽灵变体 2 攻击,恶意来宾可以污染分支目标缓冲区或返回堆栈缓冲区,导致内核在推测执行路径中跳转到小工具代码。

为了缓解变体 2,主机内核可以使用返回跳板进行间接分支,以绕过被污染的分支目标缓冲区,并在 VM 退出时刷新返回堆栈缓冲区。这可以防止恶意来宾影响主机内核中的间接分支。

为了保护主机进程免受恶意来宾的攻击,主机进程可以通过 prctl() 禁用间接分支推测。在上下文切换到此类进程之前,会清除分支目标缓冲区。

4. 虚拟化来宾攻击其他来宾

恶意来宾可能会攻击另一个来宾以获取其他来宾可访问的数据。

如果可以在来宾之间传递参数,则可能发生幽灵变体 1 攻击。这可以通过共享内存或消息传递等机制来完成。此类参数可用于派生指向来宾中特权数据的数据指针。可以通过受害者的推测路径中的小工具代码访问特权数据。

幽灵变体 2 攻击可以由恶意来宾通过污染分支目标缓冲区或返回堆栈缓冲区来发起。此类被污染的条目可用于影响受害者来宾中的推测执行路径。

Linux 内核通过在 VM 退出时刷新返回堆栈缓冲区,并在切换到来宾之前清除分支目标缓冲区,从而缓解对同一 CPU 硬件线程中运行的其他来宾的攻击。

如果使用 SMT,则管理员可以通过使用 prctl() 关闭不安全来宾的间接分支推测,从而缓解来自同级超线程中不受信任的来宾的幽灵变体 2 攻击。来宾还可以通过在来宾中启用基于微代码的缓解措施(例如 x86 上的 IBPB 或 STIBP)来保护自己。

幽灵系统信息

Linux 内核提供了一个 sysfs 接口来枚举系统当前针对幽灵的缓解状态:系统是否易受攻击以及哪些缓解措施处于活动状态。

显示幽灵变体 1 缓解状态的 sysfs 文件为

/sys/devices/system/cpu/vulnerabilities/spectre_v1

此文件中的可能值为

“不受影响”

处理器不易受攻击。

“易受攻击:仅 __user 指针清理和 usercopy 屏障;无 swapgs 屏障”

swapgs 保护已禁用;否则,它在内核中具有基于案例的保护,带有显式指针清理和 usercopy LFENCE 屏障。

“缓解:usercopy/swapgs 屏障和 __user 指针清理”

内核中的保护是基于具体情况进行的,采用显式指针清理、用户拷贝 LFENCE 屏障和 swapgs LFENCE 屏障。

然而,这些保护是基于具体情况实施的,不能保证覆盖 Spectre 变体 1 的所有可能的攻击途径。

spectre_v2 内核文件会报告内核是否已使用 retpoline 缓解措施编译,或者 CPU 是否具有硬件缓解措施,以及 CPU 是否支持额外的进程特定缓解措施。

此文件还报告了微代码启用的 CPU 功能,以缓解用户进程之间的攻击。

  1. 间接分支预测屏障 (IBPB) 用于在不同用户的进程之间增加额外的隔离。

  2. 单线程间接分支预测器 (STIBP) 用于在同一核心上运行的 CPU 线程之间增加额外的隔离。

这些 CPU 功能在使用时可能会影响性能,并且可以基于具体情况为每个进程启用。

显示 Spectre 变体 2 缓解状态的 sysfs 文件是

/sys/devices/system/cpu/vulnerabilities/spectre_v2

此文件中的可能值为

  • 内核状态

“不受影响”

处理器没有漏洞

“缓解:无”

存在漏洞,没有缓解措施

“缓解:Retpolines”

使用 Retpoline 桩

“缓解:LFENCE”

使用 LFENCE 指令

“缓解:增强型 IBRS”

以硬件为中心的缓解措施

“缓解:增强型 IBRS + Retpolines”

以硬件为中心 + Retpolines

“缓解:增强型 IBRS + LFENCE”

以硬件为中心 + LFENCE

  • 固件状态:显示在调用固件时是否使用间接分支限制推测 (IBRS) 来防止 Spectre 变体 2 攻击(仅限 x86)。

“IBRS_FW”

防止用户程序在调用固件时遭受攻击

  • 间接分支预测屏障 (IBPB) 状态,用于保护不同用户的进程之间。 此功能可以通过 prctl() 为每个进程控制,或者通过内核命令行选项控制。 这仅限 x86 功能。 有关更多详细信息,请参见下文。

“IBPB:禁用”

IBPB 未使用

“IBPB:始终启用”

在所有任务上使用 IBPB

“IBPB:条件性”

在 SECCOMP 或间接分支限制任务上使用 IBPB

  • 单线程间接分支预测 (STIBP) 状态,用于保护不同的超线程之间。 此功能可以通过每个进程的 prctl 或通过内核命令行选项控制。 这仅限 x86 功能。 有关更多详细信息,请参见下文。

“STIBP:禁用”

STIBP 未使用

“STIBP:强制”

在所有任务上使用 STIBP

“STIBP:条件性”

在 SECCOMP 或间接分支限制任务上使用 STIBP

  • 返回堆栈缓冲区 (RSB) 保护状态

“RSB 填充”

启用上下文切换时对 RSB 的保护

  • EIBRS 后屏障返回堆栈缓冲区 (PBRSB) 保护状态

“PBRSB-eIBRS:软件序列”

CPU 受到影响,并且启用 VMEXIT 时对 RSB 的保护

“PBRSB-eIBRS:易受攻击”

CPU 易受攻击

“PBRSB-eIBRS:未受影响”

CPU 不受 PBRSB 的影响

  • 分支历史注入 (BHI) 保护状态

BHI:未受影响

系统未受影响

BHI:Retpoline

系统受 retpoline 保护

BHI:BHI_DIS_S

系统受 BHI_DIS_S 保护

BHI:软件循环,KVM 软件循环

系统受软件清除序列保护

BHI:易受攻击

系统易受 BHI 攻击

BHI:易受攻击,KVM:软件循环

系统易受攻击;KVM 受软件清除序列保护

完全缓解可能需要 CPU 供应商提供的微代码更新。当必要的微代码不可用时,内核将报告漏洞。

启用 Spectre 变体 1 和 Spectre 变体 2 的缓解

1. 内核缓解

Spectre 变体 1

对于 Spectre 变体 1,易受攻击的内核代码(由代码审计或扫描工具确定)会根据具体情况进行注释,以使用 nospec 访问器宏进行边界裁剪 [2],以避免任何可用的泄露小工具。但是,它可能无法覆盖 Spectre 变体 1 的所有攻击途径。

从用户代码复制的代码具有 LFENCE 屏障,以防止对 access_ok() 检查进行错误推测。 屏障由 barrier_nospec() 宏完成。

对于 Spectre 变体 1 的 swapgs 变体,LFENCE 屏障会添加到需要的中断、异常和 NMI 入口点。 这些屏障由 FENCE_SWAPGS_KERNEL_ENTRY 和 FENCE_SWAPGS_USER_ENTRY 宏完成。

Spectre 变体 2

对于 Spectre 变体 2 的缓解,编译器将内核中的间接调用或跳转转换为等效的返回跳转(retpolines)[3] [9] 以转到目标地址。 retpolines 下的推测执行路径被困在一个无限循环中,以防止任何推测执行跳转到小工具。

要在易受攻击的 CPU 上启用 retpoline 缓解,内核需要使用支持 -mindirect-branch=thunk-extern -mindirect-branch-register 选项的 gcc 编译器进行编译。如果内核使用 Clang 编译器编译,则编译器需要支持 -mretpoline-external-thunk 选项。需要启用内核配置 CONFIG_MITIGATION_RETPOLINE,并且 CPU 需要使用最新的更新微代码运行。

在英特尔 Skylake 时代的系统上,该缓解措施涵盖了大多数情况,但并非所有情况。 有关更多详细信息,请参见 [3]

在具有 Spectre 变体 2 硬件缓解措施的 CPU 上(例如,x86 上的 IBRS 或增强型 IBRS),retpoline 会在运行时自动禁用。

支持增强型 IBRS (eIBRS) 的系统在启动时通过设置 IBRS 位启用一次 IBRS 保护,并且它们会自动受到一些 Spectre v2 变体攻击的保护。 BHB 仍然可以影响间接分支预测器条目的选择,并且虽然在启用 eIBRS 时模式之间的分支预测器条目是隔离的,但 BHB 本身在模式之间不是隔离的。 支持 BHI_DIS_S 的系统将设置它来防止 BHI 攻击。

在英特尔的增强型 IBRS 系统上,这包括 SMT 系统上的跨线程分支目标注入 (STIBP)。 换句话说,英特尔 eIBRS 也启用了 STIBP。

AMD 自动 IBRS 不会保护用户空间,并且旧版 IBRS 系统会在退出用户空间时清除 IBRS 位,因此两者都会显式启用 STIBP。

retpoline 缓解措施在默认情况下在易受攻击的 CPU 上启用。 管理员可以通过内核命令行和 sysfs 控制文件强制启用或禁用它。 请参阅 内核命令行上的缓解控制

在 x86 上,默认情况下,在调用任何固件代码之前,会启用间接分支限制推测,以防止使用固件的 Spectre 变体 2 漏洞。

使用内核地址空间随机化(内核配置中的 CONFIG_RANDOMIZE_BASE=y 和 CONFIG_SLAB_FREELIST_RANDOM=y)通常会使内核上的攻击更加困难。

2. 用户程序缓解

用户程序可以使用 LFENCE 或“边界裁剪”来缓解 Spectre 变体 1。 有关更多详细信息,请参见 [2]

对于 Spectre 变体 2 的缓解,可以使用返回跳转为间接分支编译各个用户程序。 这可以保护它们免受恶意软件留在分支目标缓冲区中的中毒条目的消耗。

在旧版 IBRS 系统上,在返回到用户空间时,由于内核清除了 IBRS 位,因此隐式 STIBP 被禁用。 在这种情况下,用户空间程序可以通过 prctl() 禁用间接分支推测(请参阅 Documentation/userspace-api/spec_ctrl.rst)。 在 x86 上,这将在用户程序运行时启用 STIBP 以防止来自同级线程的攻击,并在切换到/从程序时使用 IBPB 刷新分支目标缓冲区。

限制用户程序上的间接分支推测也将防止该程序在 x86 上启动变体 2 攻击。管理员可以通过内核命令行和 sysfs 控制文件更改该行为。 请参阅 内核命令行上的缓解控制

禁用其间接分支推测的程序将具有更多的开销并且运行速度更慢。

用户程序应使用地址空间随机化(/proc/sys/kernel/randomize_va_space = 1 或 2)以使攻击更加困难。

3. VM 缓解

在内核中,来自恶意访客的 Spectre 变体 1 攻击会在 VM 退出路径中根据具体情况进行缓解。易受攻击的代码使用 nospec 访问器宏进行“边界裁剪”,以避免任何可用的泄露小工具。 但是,这可能无法覆盖所有变体 1 攻击途径。

对于来自恶意访客对内核的 Spectre 变体 2 攻击,Linux 内核使用 retpoline 或增强型 IBRS 来防止消耗恶意访客留在分支目标缓冲区中的中毒条目。 它还在每次 VM 退出时刷新返回堆栈缓冲区,以防止返回堆栈缓冲区下溢,以便可以使用中毒的分支目标缓冲区,或者攻击者访客在返回堆栈缓冲区中留下中毒条目。

为了缓解同一 CPU 硬件线程中访客之间的攻击,分支目标缓冲区会在切换到 CPU 上的新访客之前通过刷新来进行清理。

默认情况下,上述缓解措施在易受攻击的 CPU 上启用。

为了缓解使用 SMT 时来自同级线程的访客之间的攻击,管理员可以通过 prctl() 禁用在同级线程中运行的不受信任的访客的间接分支推测。

内核还允许访客使用他们选择的任何基于微代码的缓解措施(例如 x86 上的 IBPB 或 STIBP)来保护自己。

内核命令行上的缓解控制

通常,内核会为当前 CPU 选择合理的默认缓解措施。

Spectre 的默认缓解措施可以在内核命令行中使用以下选项禁用或更改

  • nospectre_v1

  • nospectre_v2

  • spectre_v2={选项}

  • spectre_v2_user={选项}

  • spectre_bhi={选项}

有关可用选项的更多详细信息,请参阅内核的命令行参数

缓解措施选择指南

1. 受信任的用户空间

如果所有用户空间应用程序都来自受信任的来源,并且不执行外部提供的不可信代码,则可以禁用缓解措施。

2. 保护敏感程序

对于具有机密信息(例如加密密钥)的安全敏感程序,可以通过在程序运行时禁用间接分支预测来防止 Spectre 变体 2 的攻击(请参阅Documentation/userspace-api/spec_ctrl.rst)。

3. 沙箱化不可信程序

可以将可能成为攻击来源的不可信程序隔离起来,方法是在它们运行时禁用其间接分支预测(请参阅Documentation/userspace-api/spec_ctrl.rst)。这可以防止不可信程序污染分支目标缓冲区。此行为可以通过内核命令行和 sysfs 控制文件进行更改。请参阅内核命令行上的缓解控制

3. 高安全模式

可以强制在启动时为所有程序启用所有 Spectre 变体 2 缓解措施(请参阅内核命令行上的缓解控制中的“on”选项)。这将增加开销,因为所有程序的间接分支预测都将受到限制。

在 x86 上,当切换到新程序时,分支目标缓冲区将使用 IBPB 刷新。STIBP 始终保持启用状态,以保护程序免受来自在同级线程上运行的程序的变体 2 攻击。

或者,STIBP 可以仅在运行其间接分支预测被显式禁用的程序时使用,而 IBPB 仍然在切换到新程序时始终使用以清除分支目标缓冲区(请参阅内核命令行上的缓解控制中的“ibpb”选项)。此“ibpb”选项的性能成本低于始终启用 STIBP 的“on”选项。

关于 Spectre 的参考资料

英特尔白皮书

[1] 英特尔对推测执行侧通道的分析

[2] 边界检查绕过

[3] 深入探讨:Retpoline:一种分支目标注入缓解措施

[4] 深入探讨:单线程间接分支预测器

AMD 白皮书

[5] AMD64 技术间接分支控制扩展

[6] 管理 AMD 处理器上的推测的软件技术

ARM 白皮书

[7] 缓存推测侧通道

[8] 缓存推测问题更新

谷歌白皮书

[9] Retpoline:一种用于防止分支目标注入的软件结构

MIPS 白皮书

[10] MIPS:关于推测执行和侧通道漏洞的回应

学术论文

[11] Spectre 攻击:利用推测执行

[12] NetSpectre:通过网络读取任意内存

[13] Spectre 回归!使用返回堆栈缓冲区的推测攻击