libbpf kprobe挂载sendto系统调用无法正确读取参数问题求助
问题原因分析
你遇到的参数异常问题核心是系统调用内核函数的参数结构和普通函数不同,BCC做了自动适配但原生libbpf没有,具体如下:
- Linux内核中
sys_sendto(也就是你挂载kprobe的目标函数)的实际原型只有一个参数:asmlinkage long sys_sendto(struct pt_regs *regs),所有用户态传入的系统调用参数都保存在regs结构体中,不会直接展开为函数入参。 - BCC对前缀为
syscall__的探针函数做了特殊处理:会自动从pt_regs中提取系统调用参数,赋值给你定义的sockfd、buf等形参,所以BCC版本的参数读取正常。 - 你写的libbpf版本直接用
BPF_KPROBE挂载kprobe/sendto,会把sys_sendto的第一个入参(也就是struct pt_regs *的地址)当成你定义的sockfd,后续参数自然完全错位,和你日志中sockfd值等于PT_REGS_PARM1(ctx)值的现象完全吻合。
修复方案
有两种常用的修复方式:
方案1:使用libbpf专门的系统调用探针宏
直接用BPF_SYSCALL_PROBE宏,libbpf会自动帮你完成系统调用参数的提取,和BCC的行为一致:
// 注意SEC的类型改成syscall SEC("syscall/sendto") int BPF_SYSCALL_PROBE(entry_sendto, int sockfd, const char *buf, size_t len, int flags, const struct sockaddr *dest_addr, size_t addrlen) { // 这里的sockfd、buf等参数就是正确的系统调用参数了 bpf_printk("libbpf - entry_sendto params - 1 %d", sockfd); bpf_printk("libbpf - entry_sendto params - 2 %s", buf); bpf_printk("libbpf - entry_sendto params - 3 %d", len); return 0; }
方案2:手动从pt_regs中提取参数
如果你坚持用普通kprobe挂载,自行处理参数提取即可:
SEC("kprobe/sys_sendto") int BPF_KPROBE(entry_sendto, struct pt_regs *regs) { int sockfd = (int)PT_REGS_PARM1(regs); const char *buf = (const char *)PT_REGS_PARM2(regs); size_t len = (size_t)PT_REGS_PARM3(regs); bpf_printk("libbpf - entry_sendto params - 1 %d", sockfd); // 注意:buf是用户空间地址,不能直接打印,需要用bpf_probe_read_user_str拷贝到内核栈再打印 char kbuf[256] = {0}; bpf_probe_read_user_str(kbuf, sizeof(kbuf), buf); bpf_printk("libbpf - entry_sendto params - 2 %s", kbuf); bpf_printk("libbpf - entry_sendto params - 3 %d", len); return 0; }
额外注意点
你日志中buf打印为空的问题,是因为bpf_printk不能直接读取用户空间的内存地址,需要用bpf_probe_read_user_str这类辅助函数把用户态字符串拷贝到内核空间后再打印。
内容的提问来源于stack exchange,提问作者Guy Arbitman
相关产品推荐
相关产品推荐

