追踪__sys_bpf时bpfptr_t参数获取异常及相关问题求助
问题分析与解决方案
1. 直接接收bpfptr_t参数编译报错的原因
bpfptr_t在你使用的5.15内核中是sockptr_t的别名,它是一个结构体而非单纯的指针类型,定义大致如下:
typedef struct { unsigned long ptr; int is_user; } sockptr_t;
kprobe探针函数的参数默认以unsigned long类型传递原函数参数,但结构体类型无法直接用unsigned long初始化,因此编译时会报类型不兼容错误。而PT_REGS_PARM2获取的是寄存器中的单个值(仅对应结构体的部分成员),无法直接赋值给bpfptr_t结构体变量,自然无法成功。
2. 第三个参数(bpf_attr大小)始终为0的解决方法
问题核心在于参数的寄存器偏移:
x86_64的System V ABI中,当函数参数是大小≤16字节的结构体时,会用多个寄存器传递其成员。bpfptr_t大小为12字节,会占用rsi(存储ptr)和rdx(存储is_user)两个寄存器,因此原函数__sys_bpf的第三个参数size会被放到rcx寄存器,对应kprobe中的PT_REGS_PARM4(ctx)而非PT_REGS_PARM3(ctx)。
正确的参数读取代码示例:
#include <vmlinux.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> SEC("kprobe/__sys_bpf") int kprobe___sys_bpf(struct pt_regs *ctx) { int cmd = PT_REGS_PARM1(ctx); struct sockptr_t uattr; unsigned int size; // 直接从寄存器读取结构体成员 uattr.ptr = PT_REGS_PARM2(ctx); uattr.is_user = (int)PT_REGS_PARM3(ctx); // 读取第三个参数size,对应rcx寄存器 size = PT_REGS_PARM4(ctx); // 验证输出(可通过bpf_printk查看) bpf_printk("cmd: %d, uattr.ptr: %lx, uattr.is_user: %d, size: %u\n", cmd, uattr.ptr, uattr.is_user, size); return 0; } char _license[] SEC("license") = "GPL";
执行上述代码即可正确获取第三个参数的有效值。
3. 为何追踪__sys_bpf必须用bpf_probe_read_kernel,其他函数可以直接接收参数
这取决于原函数参数的类型特性:
- 若原函数参数是标量类型(如
int、unsigned long)或指针类型(本质是unsigned long),这些类型可直接与kprobe传递的unsigned long参数兼容,因此能直接在探针函数参数中接收。 - 但
__sys_bpf的第二个参数是结构体类型,结构体作为函数参数传递时,要么占用多个寄存器,要么通过栈传递,无法用单个unsigned long表示完整的结构体内容。此时必须通过bpf_probe_read_kernel(或直接读取对应寄存器组合)来获取结构体的完整成员,才能正确解析参数。
内容的提问来源于stack exchange,提问作者luke zou
相关产品推荐
相关产品推荐

