【eBPF 内核实现深度拆解】Helper 函数子系统:注册、类型检查与参数传递

💡 原文中文,约23900字,阅读约需57分钟。
📝

内容提要

BPF程序在沙盒中执行,无法随意调用内核函数或访问内存。它通过预定义的helper函数进行特定操作,每个helper函数有特定的调用上下文,限制了其在不同BPF程序类型中的使用。本文解析了helper函数的注册机制、参数类型编码及验证逻辑,强调了类型检查和NULL检查的重要性,以确保BPF程序的安全性。

🎯

关键要点

  • BPF程序在沙盒中执行,不能随意调用内核函数或访问内存。

  • BPF程序通过预定义的helper函数进行特定操作,每个helper函数有特定的调用上下文。

  • helper函数的注册机制和verifier的类型检查共同实施调用规则。

  • BPF helper不是系统调用,而是内核提供给BPF程序的受信函数库。

  • BPF虚拟机有11个64-bit通用寄存器,R0承载返回值,R1到R5用于传参。

  • 每个helper函数有对应的struct bpf_func_proto,描述参数和返回值类型。

  • BPF_CALL_n宏用于包装C函数为BPF helper,负责参数映射。

  • 不是每个helper对所有BPF程序类型都可用,程序类型通过get_func_proto声明支持的helper。

  • verifier在加载时逐个参数验证类型匹配、范围合法、大小正确,并设置返回值类型约束。

  • kfunc允许BPF程序调用未通过BPF_CALL_n包装的普通内核函数,通过BTF类型信息验证调用。

🔎

延伸解读

BPF Helper 函数的安全性保障

BPF helper 函数的设计强调安全性,主要通过类型检查和 NULL 检查机制来实现。每个 helper 函数在被调用前,verifier 会逐个验证参数的类型和范围,确保不发生非法内存访问。这种机制不仅提高了 BPF 程序的安全性,也防止了潜在的内核崩溃。

不同 BPF 程序类型的限制

不同类型的 BPF 程序(如 XDP 和 tracing)对可用的 helper 函数有不同的限制。这是因为每个 helper 函数的语义要求特定的调用上下文。例如,XDP 程序无法调用与进程相关的 helper 函数,这种设计确保了在特定上下文中调用的安全性和有效性。

kfunc 的新特性与优势

kfunc 的引入为 BPF 程序提供了更大的灵活性,允许直接调用未通过 BPF_CALL_n 包装的内核函数。这种方式通过 BTF 类型信息进行验证,支持更复杂的参数类型和数量,极大地扩展了 BPF 的应用场景,尤其是在需要与内核深度交互的场景中。

延伸问答

BPF程序如何通过helper函数进行特定操作?

BPF程序通过预定义的helper函数进行特定操作,这些函数在内核中注册,并有特定的调用上下文限制。

什么是BPF helper函数的注册机制?

BPF helper函数的注册机制通过BPF_CALL_n宏将C函数包装为BPF可调用形式,并通过bpf_func_proto描述其类型签名。

BPF程序在调用helper函数时需要注意哪些参数类型检查?

BPF程序在调用helper函数时,verifier会逐个检查参数的类型匹配、范围合法性和大小正确性。

BPF helper函数与系统调用有什么区别?

BPF helper函数不是系统调用,它们在内核态运行,属于同一特权级别的函数调用,而系统调用涉及用户态与内核态的切换。

如何确保BPF程序的安全性?

BPF程序的安全性通过verifier进行类型检查和NULL检查,确保参数和返回值的合法性。

kfunc在BPF中的作用是什么?

kfunc允许BPF程序调用未通过BPF_CALL_n包装的普通内核函数,并通过BTF类型信息进行验证。

🏷️

标签

➡️

继续阅读