5

英特尔 CET(控制流执行技术)由两部分组成:SS(影子堆栈)和 IBT(间接分支跟踪)。如果您需要间接分支到endbr64由于某种原因无法放置的地方,您jmp可以call使用notrack. 是否有等效的方法来抑制单个ret指令的 SS?

对于上下文,我正在考虑这将如何与 retpolines 交互,其关键控制流或多或少类似于push real_target; call retpoline; pop junk; ret. 如果没有办法为此抑制 SS ret,那么在启用 CET 时是否有其他方法可以让 retpolines 工作?如果没有,我们会有什么选择?我们是否需要为所有东西维护两组二进制包,一组用于需要 retpolines 的旧 CPU,另一组用于支持 CET 的新 CPU?如果 Intel 证明是错误的,我们最终仍然需要在他们的新 CPU 上进行 retpolines 怎么办?我们是否必须放弃 CET 才能使用它们?

4

1 回答 1

0

在玩了一会儿程序集之后,我发现您可以将 retpolines 与 CET 一起使用,但这并不理想。就是这样。作为参考,请考虑以下 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

通过使用incsspqrdsspqwrssq指令,您可以修改影子堆栈以使您的更改与实际堆栈相匹配。我用Intel SDE测试了那些修改过的 thunk ,它们确实使控制流错误消失了。

那是个好消息。这是坏消息:

  1. 与 不同endbr64,我在 thunk 中使用的 CET 指令不是不支持 CET 的 CPU 上的 NOP(它们导致SIGILL)。这意味着您需要两组不同的 thunk,并且您需要使用 CPU 调度来根据 CET 是否可用来选择正确的。
  2. Using retpolines at all means that you're no longer doing any indirect branches, so while you'll still get the benefit of SS, you've completely negated IBT. I suppose you could work around this by making __x86_indirect_thunk_rax check for the presence of the endbr64 instruction, but that's really inelegant and would probably be really slow.
于 2020-06-17T21:53:50.850 回答