云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

_阿里云vnc_好用

小七 141 0

eBPF不能数数?!

如果你读过所有优秀的手册页、文档、指南和我们的一些博客,我们不太可能告诉你关于扩展伯克利包过滤器(简称eBPF)的任何新信息。但我们可以给你讲一个战争故事,谁不喜欢?这篇文章是关于eBPF如何在一段时间内丧失计数能力的。在我们德克萨斯州奥斯汀的办公室里,他们说所有的好故事都是以"你们不会相信这个……故事"开头的。不过,这篇文章是从Marek Majkowski给Linux netdev邮件列表的一篇帖子开始的,在我听到一个漫长的夜晚之后:马雷克的发现相当令人震惊——如果在eBPF中减去两个64位时间戳,结果就是垃圾。但仅当作为非特权用户运行时。从根开始一切都很好。呵呵。如果您看过Marek在Netdev 0x13会议上的演示,您就会知道我们使用BPF套接字过滤器作为抵御简单的、大容量DoS攻击的防御之一。因此,潜在地让你的包计数错误可能是件坏事™,并影响合法流量。让我们尝试用一个简化的eBPF套接字过滤器来重现这个bug,该过滤器通过BPF映射从用户空间减去两个64位无符号整数。我们的BPF程序的输入来自一个BPF数组映射,因此我们操作的值在构建时是未知的。这样可以方便地进行实验,并防止编译器优化操作。从小开始,eBPF,什么是2-1?在我们的GitHub上查看代码。$/运行bpf 2 1arg0 2 0x0000000000000002arg1 1 0x0000000000000001差异1 0x0000000000000001好吧,eBPF,2^32-1是什么?$/运行bpf$[2**32]1arg0 4294967296 0x0000000100000000arg1 1 0x0000000000000001差异18446744073709551615 0xffffffffffffff错了!但如果我们问sudo:$sudo./run bpf$[2**32]1[sudo]jkbs密码:arg0 4294967296 0x0000000100000000arg1 1 0x0000000000000001FF72940X95FF000000谁在搞我的eBPF?当电脑停止减法时,你就知道有大事发生了。我们要求增援。我们的同事Arthur Fabre在检查加载到内核中的eBPF代码时,很快就注意到有些东西关闭了。事实证明,内核实际上并没有运行它所提供的eBPF—它有时会先重写它。任何理智的程序员都希望64位减法可以用一条eBPF指令表示$llvm objdump-S-no show raw insn-section=socket1 bpf/filter.o…20: 1f 76 00 00 00 00 00 00 r6-=r7…然而,这并不是内核实际运行的内容。显然在重写之后,减法变成了一个复杂的、多步骤的操作。要查看内核实际运行的是什么,我们可以使用鲜为人知的bpftool实用程序。首先,我们需要加载BPF$./run bpf—加载2 1后停止[2] +停止/运行bpf 2 1然后用bpftool prog list列出加载到内核中的所有BPF程序$sudo bpftool程序列表…5951:套接字\过滤器名称过滤器\ alu64标记11186be60c0d0c0f gpl2019-04-05T13:01:24+0200 uid 1000装载xlated 424B jited 262B memlock 4096B地图标识28786最近加载的socket_filter必须是我们的程序(filter_alu64)。现在我们知道它的id是5951,我们可以用$sudo bpftool prog dump链接id 5951…33:(79)r7=*(u64*)(r0+0)34:(b4)(u32)r11=(u32)-135:(1f)r11-=r636:(4f)r11 |=r637:(87)r11=-r1138:(c7)r11秒>>=6339:(5f)r6&=r1140:(1f)r6-=r741:(7b)*(u64*)(r10-16)=r6…bpftool还可以显示JITed代码:bpftool prog dump JITed id 5951。如您所见,减法被一系列操作码取代。除非你是根。从根目录运行时一切都是好的$sudo./run bpf--加载0 0后停止[1] +Stopped sudo./run bpf--加载0 0后停止$sudo bpftool prog list | grep socket_过滤器659:套接字_filter name filter_alu64 tag 9e7ffb08218476f3 gpl$sudo bpftool prog dump xlated id 659…31:(79)r7=*(u64*)(r0+0)32:(1f)r6-=r733:(7b)*(u64*)(r10-16)=r6…如果您曾经使用过eBPF,那么您一定已经亲身体验过可怕的eBPF验证器。这是对所有eBPF代码的无情评判,这些代码将拒绝任何它认为不值得在内核空间运行的程序。也许之前没人告诉过你,也许会让你感到惊讶的是,同样的验证器实际上也会根据需要重写和修补eBPF代码,以确保其安全。减法的问题是通过对验证器的一个不明显的安全修复引入的。有问题的补丁首先在Linux5.0中发布,然后被后传到4.20.6稳定版本和4.19.19LTS内核。超过2000字长的commit消息并没有为您提供有关它所针对的攻击向量的任何详细信息。缓解措施源于Jann Horn在Project Zero发现的CVE-2019-7308漏洞,该漏洞利用指针算法,即向指针添加标量值,以触发来自越界地址的推测性内存加载。这种推测性负载会改变CPU缓存状态,并可用于安装Spectre variant 1攻击。为了减轻这种情况,eBPF验证器重写指针值上的任何算术运算,结果总是在边界内的内存位置。这个补丁演示了指针上的算术运算是如何重写的,我们可以在那里发现一个熟悉的模式等等……什么指针算法?我们只是想减去两个标量值。缓解措施是怎么起作用的?不应该。这是个虫子。eBPF验证器跟踪ALU操作的是什么类型的值,在这种情况下,状态被忽略。为什么以根用户身份运行BPF很好,你会问?如果您的程序具有CAP趶u SYS赜管理员权限,则不适用侧信道缓解。作为root用户,您已经可以访问内核地址空间,所以没有新的东西可以通过BPF泄漏。在我们的报告之后,修复程序很快就在v5.0内核中实现了,并被后移植到稳定的内核4.20.15和4.19.28。丹尼尔博克曼很快就解决了这个问题。然而,内核升级是很困难的,与此同时,我们只剩下运行在生产环境中的代码,而这些代码并没有做它应该做的。32位ALU救援正如eBPF维护人员所指出的,32位算术运算不受验证器错误的影响。这为解决问题打开了一扇门。eBPF寄存器r0..r10是64位宽的,但是您也可以只访问较低的32位,它们作为子寄存器w0..w10公开。您可以使用BPF ALU32指令子集对32位子寄存器进行操作。llvm7+可以生成使用此指令子集的eBPF代码。当然,您需要用简单的-Xclang-target-feature-Xclang+alu32切换来很好地询问它:$cat sub32.c#包括"common.h"u32 sub32(u32 x,u32 y){返回x-y;}$clang-O2-target bpf-Xclang-target-feature-Xclang+alu32-c sub32.c$llvm objdump-S-no show raw insn sub32.o…子32:0:bc 10 00 00 00 00 00 00宽0=w11: 1c 20 00 00 00 00 00 00 00 w0-=w22: 95 00 00 00 00 00 00 00 00出口指令#1的0x1c操作码可以分解为BPF_ALU | BPF_X | BPF|u SUB(请参阅内核文档)的0x1c操作码是我们要查找的寄存器之间的32位减法,而不是常规的64位减法操作0x1f=BPF_ALU64 | BPF_X | BPF_SUB,后者将被重写。有了这些知识,我们可以从bignum算术中借用一个页面,并使用32位运算减去64位数字:u64 sub64(u64 x,u64 y){u32×H、xl、yh、yl;u32高,低;xl=x;yl=y;lo=xl-yl;xh=x>>32;yh=y>>32;hi=xh-yh-(lo>xl);/*下溢?*/返回((u64)hi32;%8=lshr i64%1,32;tmp2=y>>32;%9=trunc i64%7至i32;xh=(u32)tmp1;%10=trunc i64%8至i32;yh=(u32)tmp2;%11=次i32%9,%10;hi=xh-yh%12=icmp ult i32%3,%5;tmp3=xl