挂载到getname的BPF程序调用renameat2时未触发问题排查
问题排查与解决方案
1. renameat2调用未触发getname/getname_flags的原因及排查
- 内核实现变更:较新内核版本中,
renameat2的代码逻辑可能已绕过getname封装,直接通过copy_from_user读取用户空间参数,不再依赖getname系列函数。建议直接查看当前内核linux/fs/namei.c中renameat2的实现代码,确认调用链是否包含getname。 - 符号匹配错误:
getname_flags可能存在版本后缀(如getname_flags@vmlinux),或挂载探针时符号解析失败。可执行bpftool prog show查看探针是否成功关联到正确内核符号。 - 权限过滤逻辑限制:代码中硬编码了UID=1002,若执行
mv命令的用户UID不符,会直接跳过输出。建议先注释UID过滤逻辑,测试是否有输出。
2. strncpy_from_user挂载报错的解决
- 参数签名不匹配:
strncpy_from_user的标准内核签名为long strncpy_from_user(char *dst, const char __user *src, long count),BPF程序必须严格匹配该参数列表,否则会触发EINVAL错误。 - 内核探测限制:部分内核函数因安全或稳定性原因被禁止kprobe/fentry探测,
strncpy_from_user可能属于此类。可尝试改用kretprobe,或检查内核配置中CONFIG_KPROBES、CONFIG_BPF_KPROBE_OVERRIDE是否开启。
3. 替代实现方案
- 直接探测
renameat2系统调用tracepoint:通过sched/sys_enter_renameat2和sched/sys_exit_renameat2tracepoint直接获取系统调用参数,无需依赖内部函数调用链。示例代码:
SEC("tracepoint/syscalls/sys_enter_renameat2") int trace_renameat2_enter(struct trace_event_raw_sys_enter *ctx) { uid_t uid = bpf_get_current_uid_gid() & 0xFFFFFFFF; if (uid != 1002) return 0; const char __user *oldpath = (const char __user *)ctx->args[1]; const char __user *newpath = (const char __user *)ctx->args[3]; char buf_old[256], buf_new[256]; if (bpf_probe_read_user_str(buf_old, sizeof(buf_old), oldpath) > 0) { bpf_printk("renameat2 oldpath: %s", buf_old); } if (bpf_probe_read_user_str(buf_new, sizeof(buf_new), newpath) > 0) { bpf_printk("renameat2 newpath: %s", buf_new); } return 0; }
- 验证内核实际调用链:用
perf工具查看renameat2执行时的函数栈,确认真实调用路径:
perf probe -x /usr/lib/modules/$(uname -r)/build/vmlinux renameat2 perf record -e probe:renameat2 -aR mv old.txt new.txt perf script
内容的提问来源于stack exchange,提问作者Dennis Orlando
相关产品推荐
相关产品推荐

