汇编语言 (MASM) 和 Spectre:变体 2 (CVE-2017-5715) 分支目标注入
Assembly language (MASM) and Spectre: Variant 2 (CVE-2017-5715) Branch Target Injection
几年前,我用下面这个宏编写并更新了我们的 MASM 代码库以对抗 Spectre V2。
NOSPEC_JMP MACRO target:REQ
PUSH target
JMP x86_indirect_thunk
ENDM
NOSPEC_CALL MACRO target:REQ
LOCAL nospec_call_start
LOCAL nospec_call_end
JMP nospec_call_end
ALIGN 16
nospec_call_start:
PUSH target
JMP x86_indirect_thunk
ALIGN 16
nospec_call_end:
CALL nospec_call_start
ENDM
.CODE
;; This is a special sequence that prevents the CPU speculating for indirect calls.
ALIGN 16
x86_indirect_thunk:
CALL retpoline_call_target
;; No benefit from aligning the capture_speculation branch target, as it is only potentially speculatively executed.
capture_speculation:
PAUSE
JMP capture_speculation
ALIGN 16
retpoline_call_target:
IFDEF WIN64
LEA RSP,[RSP+8]
ELSE
LEA ESP,[ESP+4]
ENDIF
RET
例如,下面是一些启用了推测的汇编代码 (MST_QSPECTRE=1)
main PROC NEAR C
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP
MOV EAX,OFFSET MyFun
;; Generated code to Call an indirect pointer without speculation.
IFDEF MST_QSPECTRE
NOSPEC_CALL EAX
ELSE
CALL EAX
ENDIF
POP EBP
POP EBX
POP EDI
POP ESI
RET
main ENDP
反汇编显示了推测指令是如何插入的
问题
在 2021 年,我可以安全地删除那个 MASM 宏并依靠 CPU 微代码更新等...来解决任何 Spectre 问题吗?对于 C 代码,看起来 M$ 和 Linux C 编译器已经解决了它:
- Microsoft 添加了 /Qspectre 到他们的 MSVC 编译器。
- GCC 具有通过 -mfunction-return 等来缓解 Spectre v2 的编译器选项,...
谢谢。
这些编译器选项通过生成特殊的 asm 来工作,无论是 retpolines 还是 lfence
或其他什么。当你手工编写 asm 时,显然是否手动包含特殊 asm 仍然取决于你。
对 OSes 的更改与您相关。在更新了微代码的 CPU 上,OS 可以通过告诉 CPU 不允许过去代码的分支历史影响未来代码来保护您免受其他线程的侵害。 (要求它执行此操作的能力已添加到微代码更新中,通常只需刷新分支预测缓存即可工作)。
在同一物理核心的另一个逻辑核心上执行的另一个软件线程可以在大多数 CPU 上“攻击”您的代码,因为分支预测器是共享的。至少在理论上;如果两个任务都需要为其分支目标使用相同的虚拟地址来启动预测器,ASLR 可能会使这变得难以置信。
所以在 user-space 中,我认为如果你担心同一线程中的代码 运行,你只需要保护自己免受 Spectre(例如,JIT 引擎 运行 浏览器或 JVM 中不受信任的代码必须保护自己)或在同一物理内核上。
几年前,我用下面这个宏编写并更新了我们的 MASM 代码库以对抗 Spectre V2。
NOSPEC_JMP MACRO target:REQ
PUSH target
JMP x86_indirect_thunk
ENDM
NOSPEC_CALL MACRO target:REQ
LOCAL nospec_call_start
LOCAL nospec_call_end
JMP nospec_call_end
ALIGN 16
nospec_call_start:
PUSH target
JMP x86_indirect_thunk
ALIGN 16
nospec_call_end:
CALL nospec_call_start
ENDM
.CODE
;; This is a special sequence that prevents the CPU speculating for indirect calls.
ALIGN 16
x86_indirect_thunk:
CALL retpoline_call_target
;; No benefit from aligning the capture_speculation branch target, as it is only potentially speculatively executed.
capture_speculation:
PAUSE
JMP capture_speculation
ALIGN 16
retpoline_call_target:
IFDEF WIN64
LEA RSP,[RSP+8]
ELSE
LEA ESP,[ESP+4]
ENDIF
RET
例如,下面是一些启用了推测的汇编代码 (MST_QSPECTRE=1)
main PROC NEAR C
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP
MOV EAX,OFFSET MyFun
;; Generated code to Call an indirect pointer without speculation.
IFDEF MST_QSPECTRE
NOSPEC_CALL EAX
ELSE
CALL EAX
ENDIF
POP EBP
POP EBX
POP EDI
POP ESI
RET
main ENDP
反汇编显示了推测指令是如何插入的
问题
在 2021 年,我可以安全地删除那个 MASM 宏并依靠 CPU 微代码更新等...来解决任何 Spectre 问题吗?对于 C 代码,看起来 M$ 和 Linux C 编译器已经解决了它:
- Microsoft 添加了 /Qspectre 到他们的 MSVC 编译器。
- GCC 具有通过 -mfunction-return 等来缓解 Spectre v2 的编译器选项,...
谢谢。
这些编译器选项通过生成特殊的 asm 来工作,无论是 retpolines 还是 lfence
或其他什么。当你手工编写 asm 时,显然是否手动包含特殊 asm 仍然取决于你。
对 OSes 的更改与您相关。在更新了微代码的 CPU 上,OS 可以通过告诉 CPU 不允许过去代码的分支历史影响未来代码来保护您免受其他线程的侵害。 (要求它执行此操作的能力已添加到微代码更新中,通常只需刷新分支预测缓存即可工作)。
在同一物理核心的另一个逻辑核心上执行的另一个软件线程可以在大多数 CPU 上“攻击”您的代码,因为分支预测器是共享的。至少在理论上;如果两个任务都需要为其分支目标使用相同的虚拟地址来启动预测器,ASLR 可能会使这变得难以置信。
所以在 user-space 中,我认为如果你担心同一线程中的代码 运行,你只需要保护自己免受 Spectre(例如,JIT 引擎 运行 浏览器或 JVM 中不受信任的代码必须保护自己)或在同一物理内核上。