如何使用 GCC 和 Clang(或一般的 LLVM)减轻幽灵
How to mitigate spectre with GCC and Clang (or LLVM in general)
Microsoft 在他们的 MSVC 编译器中添加了一个方便的 /Qspectre
(尽管目前这似乎只是(试图)缓解 Spectre v1),他们将随着时间的推移进行更新。从用户的角度来看,这非常好,只需启用该标志,您就可以获得他们存储的最新和最大的缓解措施。
对于 LLVM 和 GCC,它看起来有点不同。我认为他们的缓解措施尚未正式发布。
LLVM 应该获得一个 -mretpoline
编译器标志,该标志通过 return 蹦床间接调用来缓解 Spectre v2。
另一方面,GCC 有补丁添加了三个编译器选项来缓解 Spectre v2:
-mindirect-branch
可以设置为thunk
。按照我的理解,这会为每个间接调用创建 retpolines。
-mfunction-return
可以设置为thunk
。我想这会为每个函数使用这些 retpolines return,这对于 Skylake 来说可能是必需的,因为这些处理器还可以预测 returns?
-mindirect-branch-register
这个用寄存器代替栈来存放间接调用的地址?
所以我很困惑。什么编译器选项减轻了用户 space 应用程序需要什么以及在什么情况下需要它们?
通常打开它们是个好主意吗?如果为甚至没有任何推测执行的处理器架构(如微处理器)编译,它们还会生成这些 retpolines 吗?
Spectre v1 怎么样?
更新:
让我问更准确的问题:
- 我对编译器选项的理解正确吗?
- GCC 选项是应用于所有地方还是仅应用于具有推测执行的处理器?
- LLVM 选项是应用在所有地方还是只应用在具有推测执行的处理器上?
- 这些选项究竟缓解了什么(它们是否完全缓解了 spectre v2)?
- 让我们考虑一下我提出的所有其他 "bonus" 问题”,这些问题很值得了解,但对我的问题不是必需的。
我对编译器选项的理解正确吗?
我也这么认为。
GCC 选项是应用于所有地方还是仅应用于具有推测执行的处理器?
到目前为止,这些选项是特定于 x86 的。我没有检查是否有等效的 ARM 补丁,但无论如何,这些选项至少部分取决于处理器架构。
我只看了一些 patches,但看起来并没有强加额外的运行时检查。而是更改指令选择以防止后端在设置选项时完全发出不需要的间接跳转。
因此该选项不会应用于完全不相关的体系结构,但不会尝试发现特定的 CPU 在运行时是否存在漏洞。
请注意,大多数知名的 x86 处理器自 ca 以来售出。 Pentium Pro 确实使用推测执行。
这些选项究竟缓解了什么(它们是否完全缓解了 spectre v2)?
大多数情况下,它们通过确保在相应的间接调用中不会发生攻击者控制的推测来减轻分支目标注入(Spectre v2)。
Retpolines 通过使用 return 指令跳转到目标地址来实现这一点,它使用不同的分支预测器,基本上记住最新调用的来源。这是由生成的代码操纵的,该代码在 return 指令之前执行 call 指令,通过在调用之后放置 mfence 指令确保推测的执行将进入死胡同。有关详细信息,请参阅 。
Branch Target Injection 是一个问题,因为正如在“5.1 讨论”下的 Spectre Paper 中所解释的,可能有很多代码映射供攻击者利用。讨论讨论的是 Windows,但我可以想象一定有一些来自共享库的代码也映射到 Linux。如果该代码的地址更加随机,利用可能会更加困难,但我不会在不检查的情况下指望它。特别是 Linux 内核包含大量代码,攻击者可以利用这些代码注入任意分支目标。
-mfunction-return
乍一看,通过用更复杂的 return 替换它来保护 return 似乎很愚蠢,但根据 David Woodhouse 的 this message,该选项是由 Linux 内核开发人员,因为在记住的 return 地址(基本上是调用堆栈之后的隐藏堆栈)下溢时,一些 CPU 再次拉入全局分支预测器,这可能被操纵攻击者。所以你的解释是正确的。根据同一消息,Linux 内核并未立即使用此标志。我假设性能影响会很大,因为 returns 比其他间接分支 多 更常见,并且专门的 return 预测将在练习。
-mindirect-branch-register
我不太确定 -mindirect-branch-register
减轻了什么。显然它在测试套件中需要与其他选项一起使用,但我还找不到解释。另外 Xen uses 这个选项和 -mindirect-branch=thunk-extern
一起,这意味着他们自己编写 thunk 代码,不让编译器生成它。
与我最初的猜测相反,这与加载地址时的潜在猜测无关,因为这是为了与 retpolines 一起使用,以防止这种猜测。在一些讨论中提到,在堆栈上而不是在寄存器中获取目标地址的 thunk 版本最初与 Intel Control-flow Enforcement Technology (CET). I assume since the retpolines are using returns in an unusual way, CET prevented the jumps. However, according to this discussion 有冲突,这些问题似乎已经解决,并且在支持 CET 的机器上,其他缓解措施 (IBRS_ALL) 可用,这允许 retpoline thunk 再次被间接跳转替换。我认为这个选项本身不会有太大帮助。
更新:幽灵变体 1
最近 Chandler Carruth proposed 如何在 LLVM 中缓解 Spectre V1。这仍在进行中,尚未发布(截至 2018 年 3 月)。基本思想是在错误推测的路径上屏蔽易受攻击的地址或加载的值,然后才能使用它们产生副作用。
Microsoft 在他们的 MSVC 编译器中添加了一个方便的 /Qspectre
(尽管目前这似乎只是(试图)缓解 Spectre v1),他们将随着时间的推移进行更新。从用户的角度来看,这非常好,只需启用该标志,您就可以获得他们存储的最新和最大的缓解措施。
对于 LLVM 和 GCC,它看起来有点不同。我认为他们的缓解措施尚未正式发布。
LLVM 应该获得一个 -mretpoline
编译器标志,该标志通过 return 蹦床间接调用来缓解 Spectre v2。
另一方面,GCC 有补丁添加了三个编译器选项来缓解 Spectre v2:
-mindirect-branch
可以设置为thunk
。按照我的理解,这会为每个间接调用创建 retpolines。-mfunction-return
可以设置为thunk
。我想这会为每个函数使用这些 retpolines return,这对于 Skylake 来说可能是必需的,因为这些处理器还可以预测 returns?-mindirect-branch-register
这个用寄存器代替栈来存放间接调用的地址?
所以我很困惑。什么编译器选项减轻了用户 space 应用程序需要什么以及在什么情况下需要它们?
通常打开它们是个好主意吗?如果为甚至没有任何推测执行的处理器架构(如微处理器)编译,它们还会生成这些 retpolines 吗?
Spectre v1 怎么样?
更新:
让我问更准确的问题:
- 我对编译器选项的理解正确吗?
- GCC 选项是应用于所有地方还是仅应用于具有推测执行的处理器?
- LLVM 选项是应用在所有地方还是只应用在具有推测执行的处理器上?
- 这些选项究竟缓解了什么(它们是否完全缓解了 spectre v2)?
- 让我们考虑一下我提出的所有其他 "bonus" 问题”,这些问题很值得了解,但对我的问题不是必需的。
我对编译器选项的理解正确吗?
我也这么认为。
GCC 选项是应用于所有地方还是仅应用于具有推测执行的处理器?
到目前为止,这些选项是特定于 x86 的。我没有检查是否有等效的 ARM 补丁,但无论如何,这些选项至少部分取决于处理器架构。
我只看了一些 patches,但看起来并没有强加额外的运行时检查。而是更改指令选择以防止后端在设置选项时完全发出不需要的间接跳转。
因此该选项不会应用于完全不相关的体系结构,但不会尝试发现特定的 CPU 在运行时是否存在漏洞。 请注意,大多数知名的 x86 处理器自 ca 以来售出。 Pentium Pro 确实使用推测执行。
这些选项究竟缓解了什么(它们是否完全缓解了 spectre v2)?
大多数情况下,它们通过确保在相应的间接调用中不会发生攻击者控制的推测来减轻分支目标注入(Spectre v2)。
Retpolines 通过使用 return 指令跳转到目标地址来实现这一点,它使用不同的分支预测器,基本上记住最新调用的来源。这是由生成的代码操纵的,该代码在 return 指令之前执行 call 指令,通过在调用之后放置 mfence 指令确保推测的执行将进入死胡同。有关详细信息,请参阅
Branch Target Injection 是一个问题,因为正如在“5.1 讨论”下的 Spectre Paper 中所解释的,可能有很多代码映射供攻击者利用。讨论讨论的是 Windows,但我可以想象一定有一些来自共享库的代码也映射到 Linux。如果该代码的地址更加随机,利用可能会更加困难,但我不会在不检查的情况下指望它。特别是 Linux 内核包含大量代码,攻击者可以利用这些代码注入任意分支目标。
-mfunction-return
乍一看,通过用更复杂的 return 替换它来保护 return 似乎很愚蠢,但根据 David Woodhouse 的 this message,该选项是由 Linux 内核开发人员,因为在记住的 return 地址(基本上是调用堆栈之后的隐藏堆栈)下溢时,一些 CPU 再次拉入全局分支预测器,这可能被操纵攻击者。所以你的解释是正确的。根据同一消息,Linux 内核并未立即使用此标志。我假设性能影响会很大,因为 returns 比其他间接分支 多 更常见,并且专门的 return 预测将在练习。
-mindirect-branch-register
我不太确定 -mindirect-branch-register
减轻了什么。显然它在测试套件中需要与其他选项一起使用,但我还找不到解释。另外 Xen uses 这个选项和 -mindirect-branch=thunk-extern
一起,这意味着他们自己编写 thunk 代码,不让编译器生成它。
与我最初的猜测相反,这与加载地址时的潜在猜测无关,因为这是为了与 retpolines 一起使用,以防止这种猜测。在一些讨论中提到,在堆栈上而不是在寄存器中获取目标地址的 thunk 版本最初与 Intel Control-flow Enforcement Technology (CET). I assume since the retpolines are using returns in an unusual way, CET prevented the jumps. However, according to this discussion 有冲突,这些问题似乎已经解决,并且在支持 CET 的机器上,其他缓解措施 (IBRS_ALL) 可用,这允许 retpoline thunk 再次被间接跳转替换。我认为这个选项本身不会有太大帮助。
更新:幽灵变体 1
最近 Chandler Carruth proposed 如何在 LLVM 中缓解 Spectre V1。这仍在进行中,尚未发布(截至 2018 年 3 月)。基本思想是在错误推测的路径上屏蔽易受攻击的地址或加载的值,然后才能使用它们产生副作用。