Bee*_*ope 232 security x86 assembly cpu-architecture
为了减轻内核或跨进程内存泄露(Spectre攻击),Linux内核1将使用新选项进行编译,-mindirect-branch=thunk-extern引入以gcc通过所谓的retpoline执行间接调用.
这似乎是一个新发明的术语,因为Google搜索仅在最近的使用中出现(通常都是在2018年).
什么是retpoline?它如何防止最近的内核信息泄露攻击?
1然而,它不是特定于Linux的 - 类似或相同的构造似乎被用作其他操作系统的缓解策略的一部分.
Tob*_*zel 153
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
Run Code Online (Sandbox Code Playgroud)
最后的位置call是必要的,这样当间接调用完成后,控制流程继续使用NOSPEC_CALL宏,因此它可以用来代替常规call
thunk本身如下:
call retpoline_call_target
2:
lfence /* stop speculation */
jmp 2b
retpoline_call_target:
lea 8(%rsp), %rsp
ret
Run Code Online (Sandbox Code Playgroud)
控制流程在这里会有点混乱,所以让我澄清一下:
call 将当前指令指针(标签2)推送到堆栈.lea向堆栈指针添加8 ,有效地丢弃最近推送的四字,这是最后一个返回地址(到标签2).此后,堆栈顶部再次指向实际返回地址ADDR.ret跳转*ADDR并将堆栈指针重置到调用堆栈的开头.最后,这整个行为实际上等同于直接跳到*ADDR.我们得到的一个好处是,在执行call指令时,用于返回语句的分支预测器(返回堆栈缓冲区,RSB)假定相应的ret语句将跳转到标签2.
标签2之后的部分实际上从未被执行过,它只是一个无限循环,理论上会用JMP指令填充指令流水线.通过使用LFENCE,PAUSE或者更一般的指令使指令流水线是失速停止浪费任何功率和时间对这一推测执行的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预测,这允许攻击者控制猜测.
Ros*_*dge 45
甲retpoline被设计成防止所述分支目标喷射(CVE-2017-5715)利用.这是一种攻击,其中内核中的间接分支指令用于强制推测执行任意一块代码.选择的代码是一个对攻击者有用的"小工具".例如,可以选择代码,以便通过它如何影响缓存来泄漏内核数据.retpoline通过简单地用返回指令替换所有间接分支指令来防止这种利用.
我认为关于retpoline的关键只是"ret"部分,它用返回指令替换间接分支,以便CPU使用返回堆栈预测器而不是可利用的分支预测器.如果使用简单的push和return指令,那么推测性地执行的代码将是函数最终将返回的代码,而不是对攻击者有用的一些小工具.蹦床部分的主要好处似乎是维护返回堆栈,因此当函数实际返回其调用者时,这是正确预测的.
分支目标注入背后的基本思想很简单.它利用了CPU不在其分支目标缓冲区中记录分支的源和目标的完整地址这一事实.因此,攻击者可以使用其自己的地址空间中的跳转来填充缓冲区,当在内核地址空间中执行特定的间接跳转时,该跳转将导致预测命中.
请注意,retpoline不会直接阻止内核信息泄露,它只会阻止间接分支指令用于推测性地执行会泄露信息的小工具.如果攻击者可以找到一些推测性地执行小工具的其他方法,则retpoline不会阻止攻击.
文章幽灵攻击:利用 Paul Kocher,Daniel Genkin,Daniel Gruss,Werner Haas,Mike Hamburg,Moritz Lipp,Stefan Mangard,Thomas Prescher,Michael Schwarz和Yuval Yarom进行推测性执行,给出了如何利用间接分支的以下概述:
利用间接分支.从返回导向编程(ROP)开始,在这种方法中,攻击者从受害者的地址空间中选择一个小工具,并影响受害者推测性地执行小工具.与ROP不同,攻击者不依赖于受害者代码中的漏洞.相反,攻击者训练分支目标缓冲区(BTB)将分支从间接分支指令错误地预测到小工具的地址,从而导致小工具的推测性执行.虽然抛弃了推测性执行的指令,但它们对缓存的影响不会被恢复.小工具可以使用这些效果来泄露敏感信息.我们展示了如何通过仔细选择小工具,这个方法可以用来从受害者那里读取任意内存.
为了掩盖BTB,攻击者在受害者的地址空间中找到小工具的虚拟地址,然后对该地址执行间接分支.此攻击是从攻击者的地址空间完成的,并且攻击者地址空间中的小工具地址所在的内容无关紧要; 所需要的只是用于训练分支以使用相同目标虚拟地址的分支.(事实上,只要攻击者处理异常,即使攻击者的地址空间中没有代码映射到小工具的虚拟地址,攻击也可以工作.)也不需要完全匹配源地址用于培训的分支和目标分支的地址.因此,攻击者在设置培训方面具有很大的灵活性.
Google的Project Zero团队的一篇名为" 阅读特权内存 "的博客文章提供了另一个分支目标注入可用于创建工作漏洞的示例.
小智 9
这个问题是在不久前提出的,应该得到一个新的答案。
执行摘要:
“ Retpoline”序列是一种软件构造,可将间接分支与推测执行隔离开。这可以用于保护敏感的二进制文件(例如操作系统或虚拟机管理程序实现)免受针对其间接分支的分支目标注入攻击。
“ ret poline ” 一词是“ return”和“ trampoline”一词的组合,就像对“ rel poline ” 的改进来自“ relative call”和“ trampoline”。它是使用返回操作构造的蹦床构造,它还可以比喻地确保任何相关的投机执行将无休止地“反弹”。
为了减轻内核或跨进程内存泄露(Spectre攻击)的影响,Linux内核[1]将使用新选项进行编译,并
-mindirect-branch=thunk-extern引入gcc以通过所谓的retpoline执行间接调用。[1]但是,它不是特定于Linux的-类似或相同的构造似乎已被用作其他OS的缓解策略的一部分。
在具有CVE-2017-5715所需的微代码更新的受影响处理器中,使用此编译器选项仅可防止Spectre V2。它可以在任何代码(不仅是内核)上“ 工作 ”,但是只有包含“秘密”的代码才值得攻击。
这似乎是一个新发明的术语,因为Google搜索仅在最近才使用(通常在2018年全部使用)。
自2018年1月4日起,LLVM编译器已进行了更改。该日期是该漏洞首次公开报告的时间。GCC 于 2018年1月7日发布了补丁程序。-mretpoline
CVE日期表明该漏洞是在2017 年“ 发现 ”的,但是它影响了过去二十年中制造的某些处理器(因此很可能是很久以前发现的)。
什么是retpoline?它如何防止最近发生的内核信息泄露攻击?
首先,一些定义:
蹦床 -有时称为间接跳转向量,蹦床是存储地址的内存位置,这些地址指向中断服务例程,I / O例程等。执行跳入蹦床,然后立即跳出或反弹,因此称为蹦床。传统上,GCC通过在运行时创建嵌套函数的地址时创建可执行的蹦床来支持嵌套函数。这是一小段代码,通常驻留在包含函数的堆栈框架中的堆栈上。蹦床加载静态链寄存器,然后跳转到嵌套函数的实际地址。
Thunk - Thunk是一个子例程,用于将其他计算注入到另一个子例程中。Thunk主要用于将计算延迟到需要其结果之前,或在其他子例程的开头或结尾插入操作
备注 -备注功能“记住”与某些特定输入集相对应的结果。带有记忆输入的后续调用将返回记忆结果,而不是重新计算该结果,从而消除了除具有该参数的函数的首次调用之外的所有参数中具有给定参数的调用的主要成本。
很不客气,一个retpoline是蹦床与回报为形实转换,以“ 糟蹋 ” memoization的间接分支预测。
资料来源:retpoline包含针对Intel的PAUSE指令,但对于AMD则需要LFENCE指令,因为在该处理器上,PAUSE指令不是序列化指令,因此,推测它在等待返回时将使用多余的功率错误地预测了正确的目标。
Arstechnica对问题有一个简单的解释:
“每个处理器都有一个体系结构行为(记录的行为描述了指令的工作方式以及程序员编写程序所依赖的行为)和一个微体系结构行为(该体系结构的实际实现方式的行为)。这些行为可以细微地区别开来。例如,在架构上,从内存中的特定地址加载值的程序将等待,直到知道该地址为止,然后再尝试执行加载;但是从微体系结构的角度来看,处理器可能会尝试推测性地猜测该地址,以便它可以启动即使在绝对确定应该使用哪个地址之前,也要从内存中加载值(这很慢)。
如果处理器猜错了,它将忽略猜测值并再次使用正确的地址执行加载。因此,保留了架构定义的行为。但是这种错误的猜测会打扰处理器的其他部分,尤其是缓存的内容。可以通过定时访问应该(或不应该)在缓存中的数据花费多长时间来检测和测量这些微体系结构的干扰,从而允许恶意程序对存储在内存中的值进行推断。”
摘自Intel的论文:“ Retpoline:缓解分支目标注入 ”(.PDF):
“ retpoline序列阻止处理器的推测性执行使用“间接分支预测器”(一种预测程序流的方式)来推测由漏洞利用(满足分支目标注入的五个元素中的元素4(Spectre变体2)的地址) )利用上面列出的构成)。”。
请注意,元素4是:“漏洞利用程序必须成功地影响此间接分支,以推测方式错误地预测和执行小工具。由漏洞利用程序选择的该小工具通常会通过高速缓存定时通过边信道泄漏机密数据。”