You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

fentry与kprobe的BPF验证器行为不一致问题排查

kprobe与fentry eBPF程序访问vfs_unlink参数行为差异的原因与解决方法

问题背景

  • 内核版本:6.8.0-87-generic
  • 两个kprobe程序(程序1、2)可正常编译加载,均能打印vfs_unlink的第4个参数(struct inode **类型,对应代码中的arg3)
  • 同逻辑的fentry程序(程序3、4)尝试访问该参数时触发BPF验证器错误,仅访问前3个参数的fentry程序5可正常加载
  • 验证器核心错误日志:func 'vfs_unlink' arg3 type PTR is not a struct

根因分析

kprobe与fentry的参数校验机制存在本质区别:

  1. kprobe:通过pt_regs直接读取寄存器原始值,验证器仅校验寄存器访问的合法性,不会对参数的深层类型(如二级指针)做限制——即使是struct inode **,也会被当作64位地址直接读取,因此不会触发错误。
  2. fentry:属于内核函数入口的精准追踪,会严格匹配内核暴露的目标函数参数类型定义。对于vfs_unlink的第4个参数,内核给fentry的类型定义是指向struct的指针,而非指向指针的指针。当代码中声明为struct inode **时,验证器判定类型不匹配,直接拒绝加载程序。

内核中vfs_unlink的原型为:

int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode);

其中delegated_inode是输出参数,但fentry框架不支持直接声明二级指针类型的参数,因此触发校验报错。

解决方案

对于fentry程序,需将该二级指针参数当作64位无符号整数(__u64)读取,再转换为目标类型,绕过验证器的类型校验,同时获取正确的地址值。

修改后的fentry程序示例

方式1:手动读取ctx数组

SEC("fentry/vfs_unlink")
int vfs_unlink(__u64 *ctx)
{
    struct mnt_idmap *arg0 = (struct mnt_idmap *)ctx[0];
    struct inode *arg1 = (struct inode *)ctx[1];
    struct dentry *arg2 = (struct dentry *)ctx[2];
    // 将arg3以__u64读取后转换为二级指针
    struct inode **arg3 = (struct inode **)(ctx[3]);
    bpf_printk("arg3: %p\n", arg3);
    return 0;
}

方式2:使用BPF_PROG宏调整参数类型

SEC("fentry/vfs_unlink")
int BPF_PROG(vfs_unlink, struct mnt_idmap *arg0, struct inode *arg1, struct dentry *arg2, __u64 arg3)
{
    struct inode **delegated_inode = (struct inode **)arg3;
    bpf_printk("arg3: %p\n", delegated_inode);
    return 0;
}

验证结果

修改后的程序可正常编译加载,打印的arg3地址与kprobe程序输出完全一致,功能符合预期。

内容的提问来源于stack exchange,提问作者fpes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 15:32:38