all

什么是 retpoline,它是如何工作的?

发布于 2022-05-26 23:01:01

为了缓解内核或跨进程内存泄露(Spectre攻击),Linux
内核1将使用一个新选项进行编译
-mindirect-branch=thunk- extern引入以gcc通过所谓的 retpoline 执行间接调用。

这似乎是一个新发明的术语,因为谷歌搜索只出现在最近的使用中(通常都是在 2018 年)。

什么是 retpoline,它如何防止最近的内核信息泄露攻击?


1但是,它不是特定于 Linux 的 -
类似或相同的构造似乎被用作其他操作系统缓解策略的一部分。

关注者
0
被浏览
18
1 个回答
  • 面试哥
    面试哥 2022-05-26
    为面试而生,有面试问题,就找面试哥。

    sgbj 在 Google 的 Paul Turner
    撰写的评论中提到的文章
    更详细地解释了以下内容,但我会试一试:

    就我目前有限的信息而言,retpoline 是一个 返回蹦床 ,它使用永远不会执行的无限循环来防止 CPU 推测间接跳转的目标。

    基本方法可以在Andi
    Kleen解决此问题的内核分支中看到:

    它引入了新__x86.indirect_thunk调用,该调用加载调用目标,其内存地址(我将调用它ADDR)存储在堆栈顶部,并使用RET指令执行跳转。然后使用NOSPEC_JMP/CALL宏调用 thunk
    本身,该宏用于替换许多(如果不是全部)间接调用和跳转。如果需要,宏只是将调用目标放在堆栈上并正确设置返回地址(注意非线性控制流):

    .macro NOSPEC_CALL target
        jmp     1221f            /* jumps to the end of the macro */
    1222:
        push    \target          /* pushes ADDR to the stack */
        jmp __x86.indirect_thunk /* executes the indirect jump */
    1221:
        call    1222b            /* pushes the return address to the stack */
    .endm
    

    最后的放置call是必要的,以便在间接调用完成时,控制流在使用NOSPEC_CALL宏之后继续,因此可以使用它来代替常规call

    thunk 本身如下所示:

        call retpoline_call_target
    2:
        lfence /* stop speculation */
        jmp 2b
    retpoline_call_target:
        lea 8(%rsp), %rsp 
        ret
    

    控制流在这里可能会有点混乱,所以让我澄清一下:

    • call将当前指令指针(标签 2)压入堆栈。
    • lea将 8 添加到 堆栈指针 ,有效地丢弃最近推送的四字,这是最后一个返回地址(到标签 2)。此后,栈顶再次指向真正的返回地址 ADDR。
    • ret跳转到*ADDR并将堆栈指针重置为调用堆栈的开头。

    最后,这整个行为实际上相当于直接跳转到*ADDR. 我们得到的一个好处是,用于返回语句的分支预测器(Return Stack
    Buffer,RSB)在执行call指令时,假设相应的ret语句将跳转到标签 2。

    标签 2
    之后的部分实际上永远不会被执行,它只是一个无限循环,理论上会用指令填充指令管道JMP。通过使用LFENCEPAUSE或更一般地,导致指令流水线停止的指令可以阻止
    CPU 在这种推测性执行上浪费任何功率和时间。这是因为如果对 retpoline_call_target
    的调用正常返回,LFENCE则将是下一条要执行的指令。这也是分支预测器将根据原始返回地址(标签 2)预测的内容

    引用英特尔的架构手册:

    LFENCE 之后的指令可能会在 LFENCE 之前从内存中获取,但它们在 LFENCE 完成之前不会执行。

    但是请注意,规范从未提到 LFENCE 和 PAUSE 会导致管道停止,所以我在这里读了几句。

    现在回到你原来的问题:内核内存信息泄露是可能的,因为结合了两个想法:

    • 即使推测错误时推测执行应该没有副作用, 推测执行仍然会影响缓存层次结构 。这意味着当推测性地执行内存加载时,它可能仍然导致缓存行被逐出。可以通过仔细测量映射到同一缓存集的内存的访问时间来识别缓存层次结构中的这种变化。
      当内存读取的源地址本身是从内核内存中读取时,您甚至可以泄漏一些任意内存。

    • Intel CPU 的间接分支预测器只使用源指令的最低 12 位,因此很容易用用户控制的内存地址毒化所有 2^12 可能的预测历史。然后,当在内核中预测到间接跳转时,可以使用内核权限推测性地执行这些操作。使用缓存定时侧通道,您可以因此泄漏任意内核内存。

    更新:在 内核邮件列表中,有一个持续的讨论让我相信 retpolines
    不能完全缓解分支预测问题,因为当返回堆栈缓冲区 (RSB) 运行为空时,更新的英特尔架构 (Skylake+)
    会退回到易受攻击的分支目标缓冲区(BTB):

    Retpoline 作为一种缓解策略,将间接分支换成回报,以避免使用来自 BTB 的预测,因为它们可能被攻击者毒害。Skylake+ 的问题在于 RSB
    下溢回退到使用 BTB 预测,这允许攻击者控制推测。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看