LWN: 对BPF tracing进行类型检测

点击上方蓝色“ Linux News搬运工 ”关注我们~

Type checking for BPF tracing

By  Jonathan Corbet

October 28, 2019

原文来自:https://lwn.net/Articles/803258/

译者注:

  1. LWN评论中有一位SystemTap的忠实拥簇在抱怨新的这些tracing工具都不能很好的支持user space tracing。

  2. 一位评论者说:所以kernel真的在做一个自己专有的编程语言了?Cyberax回复说:“是的没错,只要加上无限循环的支持,就能跟javascript相媲美了”。。。

BPF在内核里的虚拟机(The BPF in-kernel virtual machine)给内核中其他很多功能模块带来了不少新功能,尤其是对于tracing模块来说。BPF program运行在kernel里,因此需要采取多种措施来确保别导致系统运行中出错,因此BPF verifier会检查每个BPF program的方方面面来确保可以信赖它在kernel运行,不过这里仍有一个漏洞。最近Alexei Starovoitov提出了一组名为“变革bpf tracing”的patch,希望能补上漏洞,消除一类广泛使用的BPF program中的潜在问题。

BPF在tracing应用程序中有密集的使用,目的是从kernel中取得有价值的信息,并且在kernel space来进行data aggregation(数据聚合)。主要有两种变种:如果在kernel里我们关注的位置本身已经有了一个tracepoint,那么可以直接把BPF program挂在这个位置;另外一种情况,就是需要把kprobe放在kernel中任意的位置 (仍有例外) 用来触发BPF program。在这两种情况下,目前的BPF verifier都对传递给BPF program的数据具体是什么所知甚少。

那么我们考虑这么一种情况,假如有一个trace_kfree_skb tracepoint放在了net_tx_action()中,当这个tracepoint触发的时候,会有两个指针传递给被触发的处理函数(例如BPF program),一个指针是sk_buff结构,放置着所关心的网络数据包,另一个指针指向正在释放这个数据包的函数。这两个指针具体是什么类型的,这个信息并没有得到传递,BPF program只能看到是两个64-bit unsigned int类型的数字。要想访问kernel的对应数据,就先要把整型数字转成相应类型的指针,然后利用bpf_probe_read()之类的helper函数来把指针指向的数据读取出来。可能需要多次调用bpf_probe_read()来把数据结构内部的感兴趣数据读取出来,供tracing program使用。

这里有个问题,BPF program可以把这两个数字直接转换成任意类型,并不一定真是这两个指针的真正类型。如果转错了,BPF program就可能访问了不该访问的东西。最坏情况下,有可能访问到一个memory-mapped I/O区域,导致硬件损坏。通常不认为这是一个安全风险(security issue),因为tracing需要由特权用户才能发起,不过这是一个安全问题(safety issue),这种问题就是BPF verifier希望解决的问题。

自从kernel支持把BPF program挂载在tracepoint和kprobe的第一天起,这个问题就存在了。同时,BPF开发者一直在调查另一个完全不同的问题:BPF program的二进制格式缺乏兼容性。这些program会在kernel里面访问各种数据结构,但是这些结构的位置、格式、内容会随着kernel的config的改变而改变,也会随着底层架构的区别而有不同。可能在某个kernel里面我们关心的数据在结构中的第8个byte,而另一个kernel里面变成了偏移8个byte的位置。如果没有办法能识别出这些偏移的话,BPF使用者只能针对每个不同的目标系统来重新编译BPF program。

因此大家都希望能做到“compile once run everywhere”(只编译一次,就能在任何情况下运行)。在过去几年经过很多努力之后,利用创建一类新的紧凑的、机器可读的内核数据结构描述信息,得到了解决。这个描述信息名为“BPF type format”(BTF),由kernel自己提供,会被user-space的支持库来调用,在加载BPF program程序二进制代码之前预先做一些调整,从而解决了绝大多数的binary portability(二进制兼容性)问题。不过人们发现BTF信息还可以有其他用处。

具体来说,可以用BTF信息来标注tracepoint,注明传给处理函数的数据的类型。这样就能让verifier利用这些信息确保BPF program运行时是在按照正确的数据类型进行访问。并且也能让C handler program能直接利用指针来访问数据。在编译生成BPF program并加载进入kernel的时候,verifier就能在有访问相关数据的时候直接替换成bpf_probe_read()调用,当然也会经过类型检查来确保安全。

最终,BPF tracepoint handler program就能更加安全了,更加不容易写出bug。至于是否能称之为“变革性”的改动,这个值得商榷,不过肯定也算是重大改进了。

不过,可以确定的是,kprobe handler关联的数据访问无法从中受益。因为kprobe可以设置在正在运行的kernel中的任意位置,并且在触发的时候,也会获得访问处理器寄存器内容的权限。目前来说,verifier 还是没法知道这些寄存器里面放得是什么内容,因此没法做检查来保护之。也就是说,如果没法修改kernel在感兴趣的地方加上tracepoint,那么所用的BPF program就仍然没法进行类型检查,只能维持现状了。

不论如何,至少有了不少改善了,能够让现有的tracing代码更加安全。目前已经放入bpf-next tree了,除非有些什么最后时刻报出来的问题,否则的话应该能进入5.5 kernel了。

全文完

LWN文章遵循CC BY-SA 4.0许可协议。

极度欢迎将文章分享到朋友圈 

长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章