英特尔CET(控制流实施技术)由两部分组成:SS(影子堆栈)和IBT(间接分支跟踪)。如果您endbr64
由于某种原因需要间接分支到您不能放置的位置,可以使用取消单个jmp
或call
指令的IBT notrack
。是否有等效方法可以抑制单个ret
指令的SS ?
对于上下文,我正在考虑它将如何与retpolines交互,retpolines的关键控制流程差不多push real_target; call retpoline; pop junk; ret
。如果没有为此抑制SS的方法ret
,那么启用CET时是否还有其他方法可以使折线工作?如果没有,我们将有哪些选择?我们是否需要为所有内容维护两组二进制软件包,一组用于需要折线的旧CPU,另一组用于支持CET的新CPU?如果英特尔证明是错误的,那我们最终仍然需要在他们的新CPU上使用repoline吗?我们将不得不放弃CET来使用它们吗?
经过一段时间的组装后,我发现您可以在CET中使用retpolines,但这并不理想。这是如何做。作为参考,请考虑以下C代码:
extern void (*fp)(void);
int f(void) {
fp();
return 0;
}
编译gcc -mindirect-branch=thunk -mfunction-return=thunk -O3
产生如下结果:
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
事实证明,您可以通过将thunk修改为如下形式来使其工作:
__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
说明,您可以修改叠影,以配合您的更改应用到实际堆栈。我用Intel SDE测试了这些修改过的thunk ,它们确实使控制流错误消失了。
那是个好消息。这是个坏消息:
endbr64
,我在thunk中使用的CET指令在不支持CET的CPU上不是NOP(它们导致SIGILL
)。这意味着您需要两套不同的转换程序,并且需要使用CPU分派以根据CET是否可用来选择正确的转换程序。__x86_indirect_thunk_rax
检查endbr64
指令是否存在来解决此问题,但这确实不够优雅,而且可能会很慢。本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句