是否可以暂时抑制单个 ret 指令的 Intel CET,或者以其他方式使用 retpolines?

Jos*_*ica 5 assembly x86-64 intel spectre

Intel CET(控制流执行技术)由两部分组成:SS(影子堆栈)和 IBT(间接分支跟踪)。如果您需要间接地转移到地方,你不能把一个endbr64由于某种原因,你可以抑制IBT单个jmpcall指令用notrack。是否有等效的方法来抑制单个ret指令的SS ?

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

Jos*_*ica 0

在玩了一下程序集之后,我发现您可以将retpolines 与 CET 一起使用,但它不太理想。就是这样。作为参考,请考虑以下 C 代码:

extern void (*fp)(void);

int f(void) {
    fp();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译它会产生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
Run Code Online (Sandbox Code Playgroud)

事实证明,您只需将 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
Run Code Online (Sandbox Code Playgroud)

通过使用incsspqrdsspq、 和wrssq指令,您可以修改影子堆栈以匹配对真实堆栈的更改。我使用Intel SDE测试了这些修改后的 thunk ,它们确实使控制流错误消失了。

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

  1. 与 不同的是endbr64,我在 thunk 中使用的 CET 指令在不支持 CET 的 CPU 上不是 NOP(它们会导致SIGILL)。这意味着您需要两组不同的 thunk,并且需要使用 CPU 调度来根据 CET 是否可用来选择正确的 thunk。
  2. 使用 retpolines 意味着您不再执行任何间接分支,因此虽然您仍然可以获得 SS 的好处,但您已经完全否定了 IBT。我想你可以通过检查指令__x86_indirect_thunk_rax是否存在来解决这个问题endbr64,但这真的很不优雅,而且可能会很慢。