Assembly 是否可以暂时抑制单个ret指令的英特尔CET,或以其他方式使用retpolines?
Intel CET(控制流强制技术)由两部分组成:SS(阴影堆栈)和IBT(间接分支跟踪)。如果由于某种原因需要间接地分支到不能放置Assembly 是否可以暂时抑制单个ret指令的英特尔CET,或以其他方式使用retpolines?,assembly,x86-64,intel,spectre,Assembly,X86 64,Intel,Spectre,Intel CET(控制流强制技术)由两部分组成:SS(阴影堆栈)和IBT(间接分支跟踪)。如果由于某种原因需要间接地分支到不能放置endbr64的地方,可以使用notrack抑制单个jmp或call指令的IBT。对于单个ret指令,是否有等效的方法来抑制SS 对于上下文,我正在考虑这将如何与retpoline交互,retpoline的关键控制流或多或少类似于push real\u target;叫雷托林;流行垃圾;ret。如果没有一种方法可以抑制该ret的SS,那么当CET启用时,是否有其他方
endbr64
的地方,可以使用notrack
抑制单个jmp
或call
指令的IBT。对于单个ret
指令,是否有等效的方法来抑制SS
对于上下文,我正在考虑这将如何与retpoline交互,retpoline的关键控制流或多或少类似于
push real\u target;叫雷托林;流行垃圾;ret
。如果没有一种方法可以抑制该ret
的SS,那么当CET启用时,是否有其他方法可以使retpolines工作?如果没有,我们有什么选择?我们是否需要为所有内容维护两套二进制软件包,一套用于需要重新部署的旧CPU,另一套用于支持CET的新CPU?如果英特尔最终证明是错误的,而我们最终仍然需要在他们的新CPU上重新部署呢?我们必须放弃CET才能使用它们吗?在玩了一段组装之后,我发现你可以将Retpoline与CET一起使用,但这并不理想。这是怎么做的。作为参考,请考虑这个C代码:
外部无效(*fp)(无效);
int f(无效){
fp();
返回0;
}
结果如下:
f:
subq $8, %rsp
movq fp(%rip), %rax
call __x86_indirect_thunk_rax
xorl %eax, %eax
addq $8, %rsp
jmp __x86_return_thunk
__x86_return_thunk:
call .LIND1
.LIND0:
pause
lfence
jmp .LIND0
.LIND1:
lea 8(%rsp), %rsp
ret
__x86_indirect_thunk_rax:
call .LIND3
.LIND2:
pause
lfence
jmp .LIND2
.LIND3:
mov %rax, (%rsp)
ret
事实证明,只需将thunks修改为如下所示,您就可以实现这一点:
__x86_return_thunk:
call .LIND1
.LIND0:
pause
lfence
jmp .LIND0
.LIND1:
push %rdi
movl $1, %edi
incsspq %rdi
pop %rdi
lea 8(%rsp), %rsp
ret
__x86_indirect_thunk_rax:
call .LIND3
.LIND2:
pause
lfence
jmp .LIND2
.LIND3:
push %rdi
rdsspq %rdi
wrssq %rax, (%rdi)
pop %rdi
mov %rax, (%rsp)
ret
通过使用incsspq
、rdsspq
和wrssq
指令,您可以修改阴影堆栈以使更改与实际堆栈相匹配。我测试了那些修改过的thunks,它们确实消除了控制流错误
这是个好消息。坏消息是:
endbr64
不同,我在thunks中使用的CET指令不是不支持CET的CPU上的NOP(它们导致SIGILL
)。这意味着您需要两组不同的Thunk,并且您需要使用CPU调度来选择正确的Thunk,这取决于CET是否可用\uuuux86\uinternal\uthunk\urax
检查endbr64
指令的存在来解决这个问题,但这确实不雅观,而且可能非常慢R11被调用阻塞,不用于arg传递;对于
incsspq
,您应该能够使用它而不是RDI,即使在像thunks这样的透明包装器中也是如此。(x86-64 SysV有一个不用于传递/返回任何内容的调用clobbered reg的一个原因就是包装器/thunk有一个scratch reg,例如用于惰性动态链接。)@PeterCordes我考虑过这一点,但GCC用于函数调用,就像它用于,我不能100%确定它不会假设r11是通过它们保存下来的。啊,我明白了。我想这就是为什么GCC在它的thunk中使用lea
而不是add
,所以它甚至不会破坏标志。它确实已经踩到了红色区域,所以它不能在叶子函数中任意使用它作为开关。如果您修改GCC以发出此消息,希望您能告诉它它会重击R11。@PeterCordes Linux内核提供了它自己的Thunk和一些调用,但是编译器仍然会生成一些对它们的调用,并且调用方决定将哪个寄存器用于目标地址。(而且,GENERATE_THUNK
就在它的使用位置上方定义。)啊,对了。如果他们纯粹使用内联asm,他们只需要为scratch reg使用一个伪“=r”
输出,或者一个固定的选项,并且不需要为每个可能的选项发出定义。由于Linux是一个独立的内核,它需要定义通常在libgcc中的东西,GCC可以发出对这些东西的引用。