使用kprobe监控do_filp_open函数, 参数获取失败,原因在哪里?

新手上路,请多包涵

我使用kprobe监控do_filp_open函数, 获取参数异常,原因在哪里?

代码如下

SEC("kprobe/do_filp_open")
int do_filp_open_exit(struct  pt_regs *ctx){
        struct filename * pathname = (struct filename *)PT_REGS_PARM2(ctx);
        const struct open_flags * op = (struct open_flags *)PT_REGS_PARM3(ctx);

        if(op->open_flag & O_CREAT || op->intent & LOOKUP_CREATE) { //运行时这里报错 提示invalid mem access 'inv'
            e->type = CREATEFILE;
            __builtin_memcpy(e->event_name, "createfile", sizeof("createfile"));
        }
}

内核版本是5.4.18
我确认了内核版本的该函数的参数,并无更改

求他检测的一些函数同样使用PT_REGS_PARM3命令可以获取到函数,也排除了宏的问题。

还有什么其他的方法可以分析原因吗?

另外 我在内核5.4版本上要替换我代码中的fexit监控的函数。原本的代码是运行在6.0的 所以支持fexit,同时可以获取到参数和返回值。
在5.4版本上kretprobe只能获取返回值 ,kprobe只能获取参数 有没有其他好的探测方式?

阅读 860
avatarAI BotBETA

问题分析

在您的案例中,使用 kprobe 监控 do_filp_open 函数时遇到参数获取失败的问题,特别是在访问 op->open_flagop->intent 时出现了“invalid mem access 'inv'”错误。这通常指示了几个可能的原因:

  1. 参数地址不正确:可能 PT_REGS_PARM2(ctx)PT_REGS_PARM3(ctx) 没有正确指向预期的结构体。这可能是因为 do_filp_open 函数的参数在内核版本间发生了变化,或者宏 PT_REGS_PARM2PT_REGS_PARM3 没有正确解析参数位置。
  2. 结构体定义不一致:如果 struct filenamestruct open_flags 的定义与内核中实际使用的不同,访问这些结构体的成员时会出现问题。
  3. 编译环境问题:有时编译环境(如 GCC 版本、内核头文件等)的差异可能导致不同的行为。
  4. 内存访问权限问题:内核可能出于安全考虑,对某些内存区域施加了保护,使得用户态或特定上下文中无法直接访问。

解决方案

  1. 验证参数位置

    • 检查 do_filp_open 函数的实际参数列表,确保 PT_REGS_PARM2PT_REGS_PARM3 确实指向了正确的参数。
    • 使用 printk 或其他内核日志机制打印这些参数的地址和内容,看看它们是否有效。
  2. 检查结构体定义

    • 确认 struct filenamestruct open_flags 的定义与内核源代码中的定义一致。
    • 如果这些结构体在内核中有变动(例如,通过内核补丁或不同版本的内核),请相应地更新您的代码。
  3. 使用动态调试工具

    • 使用如 SystemTap 或 eBPF 等工具,这些工具可以在不修改内核代码的情况下进行动态跟踪和调试。
    • 这些工具可以提供关于函数参数和调用堆栈的详细信息,有助于诊断问题。
  4. 考虑内核版本差异

    • 由于您从内核 6.0 迁移到 5.4,请特别注意两个版本间内核 API 和行为的变化。
    • 如果没有 fexit 支持,您可能需要使用其他方法(如 kprobe 钩子到相关的返回点)来捕获返回值。
  5. 替代方案

    • 如果 kprobe 和 kretprobe 不能满足需求,考虑使用其他类型的内核模块,如 tracepoints(如果可用)或自定义的系统调用。
    • 编写一个内核模块,在 do_filp_open 调用前后注册回调函数,通过更直接的方式访问所需的数据。
  6. 编译和测试

    • 确保使用与内核版本相匹配的编译器和内核头文件进行编译。
    • 在目标系统上充分测试您的模块,以确保它在不同的工作负载和条件下都能稳定工作。

总结

您遇到的问题可能与参数地址不正确、结构体定义不一致、编译环境问题或内存访问权限有关。通过验证参数位置、检查结构体定义、使用动态调试工具、考虑内核版本差异以及编写替代方案,您应该能够诊断并解决这个问题。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏