Linux LTS 版本内核 CPU Spectre 侧信道漏洞补丁分析 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

Linux LTS 版本内核 CPU Spectre 侧信道漏洞补丁分析

h1apwn 技术 2019-09-10 11:31:37
373848
收藏

导语:通过这篇文章我将深入分析最近的一个Specter漏洞补丁程序,还会介绍我们是如何独立挖到此漏洞的以及我们的Respectre插件是如何自动修复这个底层漏洞的。

通过这篇文章我将深入分析最近的一个Specter漏洞补丁程序,其中一个补丁是手动打到Linux内核中的。我将介绍此修复程序所采取的方法,从其发出警告到向后移植到Long Term Support (LTS) kernels时被破坏。我们将研究后端漏洞的原理以及导致这种backporting失败的upstream过程中的bug。还会介绍我们是如何独立挖到此漏洞的以及我们的Respectre插件是如何自动修复这个底层漏洞的。

我能够找到的最早版本的补丁程序是来自于Dianzhang Chen的这个补丁,它是在2019年5月24日被发布的。它通过ptrace系统调用来解决具有用户控制索引的数组的推测访问。最初的解决方案还是挺好的,但Thomas Gleixner提出补丁可以被绕过,一个月后,他们又发布了补丁的第二个版本。

由于补丁的细节很重要,我做了一个比较:

 diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
 index a166c96..cbac646 100644
 --- a/arch/x86/kernel/ptrace.c
 +++ b/arch/x86/kernel/ptrace.c
 @@ -25,6 +25,7 @@
  #include <linux/rcupdate.h>
  #include <linux/export.h>
  #include <linux/context_tracking.h>
 +#include <linux/nospec.h>
  
  #include <linux/uaccess.h>
  #include <asm/pgtable.h>
 @@ -643,9 +644,11 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
  {
   struct thread_struct *thread = &tsk->thread;
   unsigned long val = 0;
 + int index = n;
  
   if (n < HBP_NUM) {
 -  struct perf_event *bp = thread->ptrace_bps[n];
 +  index = array_index_nospec(index, HBP_NUM);
 +  struct perf_event *bp = thread->ptrace_bps[index];
  
    if (bp)
     val = bp->hw.info.address;

可以看到,代码块的开头在声明bp指针之前初始化了索引变量。

这导致下面的编译器警告:

 arch/x86/kernel/ptrace.c: In function 'ptrace_get_debugreg':
 arch/x86/kernel/ptrace.c:705:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
    struct perf_event *bp = thread->ptrace_bps[index];
    ^~~~~~

尽管有这个警告,但这段代码被逐字地合并到了Thomas Gleixner的x86 / tip tree中,可以在这里看到。此前合并的5.3-RC1的补丁,Linus Torvalds发现警告的LKML邮件列表。但是,当对tree做实际合并时,没有提到对补丁的更新。

LTS内核中采用的修复方法是通过简单地交换顺序:

 +               struct perf_event *bp = thread->ptrace_bps[index];
 +               index = array_index_nospec(index, HBP_NUM);

为了解释为什么说这个补丁是失效的,先看一下array_index_nospec()API。此函数获取索引值以及索引的第一个较大的越界值。如果不使用条件控制流,它会将该索引转换为原始索引值(如果在边界内)或零(如果超出边界)。通过确保原始索引的所有后续使用改为使用从此API返回的值,可以防止具有越界索引值的推测路径的破坏。

但是,在索引array_index_nospec ()宏之前,“index”用作ptrace_bps数组的索引。此外,由于“索引”在函数中没有后续使用,整个array_index_nospec()操作通常被编译器视为dead store,并通过称为死存储消除(DSE)的优化传递消除。由于在执行array_index_nospec()的某些内联汇编中使用了“volatile”关键字,后一种效果最终不会发生。

我们可以通过查看函数的反汇编来看下一:

 .text:0000000000000648 ptrace_get_debugreg proc near           ; CODE XREF: getreg32+EF
 .text:0000000000000648                                         ; arch_ptrace+6E
 .text:0000000000000648                 push    rbp
 .text:0000000000000649                 cmp     esi, 3 ; esi = index (or n), this is the if (n < HBP_NUM) check
 .text:000000000000064C                 mov     rbp, rsp
 .text:000000000000064F                 jg      short loc_673
 .text:0000000000000651                 movsxd  rsi, esi ; rsi = sign-extended index
 .text:0000000000000654 ; rdx is 'bp' here, using rsi derived from user-provided one, with no speculation barrier
 .text:0000000000000654                 mov     rdx, [rdi+rsi*8+800h]
 .text:000000000000065C ; here begins the unused array_index_nospec()
 .text:000000000000065C                 cmp     rsi, 4 ; from arch/x86/include/asm/barrier.h asm volatile
 .text:0000000000000660                 sbb     rsi, rsi ; from arch/x86/include/asm/barrier.h asm volatile
 .text:0000000000000663                 xor     eax, eax ; fallout from arch/x86/include/asm/barrier.h asm volatile
 .text:0000000000000665                 test    rdx, rdx
 .text:0000000000000668                 jz      short loc_68F
 .text:000000000000066A                 mov     rax, [rdx+138h]
 .text:0000000000000671                 jmp     short loc_68F
 .text:0000000000000673 ; ---------------------------------------------------------------------------
 .text:0000000000000673
 .text:0000000000000673 loc_673:                                ; CODE XREF: ptrace_get_debugreg+7
 .text:0000000000000673                 cmp     esi, 6
 .text:0000000000000676                 jnz     short loc_681
 .text:0000000000000678                 mov     rax, [rdi+820h]
 .text:000000000000067F                 jmp     short loc_68F
 .text:0000000000000681 ; ---------------------------------------------------------------------------
 .text:0000000000000681
 .text:0000000000000681 loc_681:                                ; CODE XREF: ptrace_get_debugreg+2E
 .text:0000000000000681                 xor     eax, eax
 .text:0000000000000683                 cmp     esi, 7
 .text:0000000000000686                 jnz     short loc_68F
 .text:0000000000000688                 mov     rax, [rdi+828h]
 .text:000000000000068F
 .text:000000000000068F loc_68F:                                ; CODE XREF: ptrace_get_debugreg+20
 .text:000000000000068F                                         ; ptrace_get_debugreg+29 ...
 .text:000000000000068F                 pop     rbp
 .text:0000000000000690                 retn
 .text:0000000000000690 ptrace_get_debugreg endp

Respectre编译器插件是世界上最先进,最有效,最高效的防御 CPU Spectre 侧信道攻击的工具。该插件使用高级静态分析自动查找潜在的Specter实例,并通过高性能检测消除它们。值

通过获取当前代码并修复漏洞后,可以得到以下输出:

 arch/x86/kernel/ptrace.c: In function 'ptrace_get_debugreg':
 arch/x86/kernel/ptrace.c:717:22: note: Spectre v1 array index bound '3'
    struct perf_event *bp = thread->ptrace_bps[n];
                       ^
 arch/x86/kernel/ptrace.c:717:22: note: Spectre v1 array index mask adjust: inc constbound: yes

我们为客户提供像Respectre这样的防御工具可以填补个人的临时手动修补留下的空白,这确实是有好处的。

最后要说的是,上游社区的开发者并没有发现这个漏洞,这么多人都参与其中,因此应该存在很多机会来阻止这种糟糕的修复技术被引入。然而,这个补丁漏洞不仅被发布,而且传播到了所有支持的稳定内核中,这表明上游的开发社区根本不像公众所认为的那样厉害。

  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务