Libbpf程序访问task_struct获取父PID失败的问题求助
解决BPF程序获取PPID时的加载失败问题
问题背景
基于libbpf框架编写的execsnoop程序,通过tracepoint跟踪sys_enter_execve和sys_exit_execve获取进程启停信息,在尝试通过task_struct获取父进程PID(PPID)时加载失败。
相关代码片段:
struct task_struct *task = (struct task_struct *)bpf_get_current_task(); pid_t parent_pid = 0; if (task != NULL) { if (task->real_parent != NULL) { parent_pid = task->real_parent->tgid; } }
加载失败的错误日志:
libbpf: prog 'tracepoint__syscalls__sys_enter_execve': BPF program load failed: Permission denied libbpf: prog 'tracepoint__syscalls__sys_enter_execve': -- BEGIN PROG LOAD LOG -- 0: R1=ctx() R10=fp0 ; int tracepoint__syscalls__sys_enter_execve( @ execsnoop.bpf.c:26 0: (bf) r6 = r1 ; R1=ctx() R6_w=ctx() ; u64 id = bpf_get_current_pid_tgid(); @ execsnoop.bpf.c:31 1: (85) call bpf_get_current_pid_tgid#14 ; R0_w=scalar() ; pid_t pid = (pid_t)id; @ execsnoop.bpf.c:32 2: (63) *(u32 *)(r10 -4) = r0 ; R0_w=scalar() R10=fp0 fp-8=mmmm???? ; u64 ts = bpf_ktime_get_ns(); @ execsnoop.bpf.c:33 3: (85) call bpf_ktime_get_ns#5 ; R0_w=scalar() 4: (bf) r8 = r0 ; R0_w=scalar(id=1) R8_w=scalar(id=1) 5: (b7) r9 = 0 ; R9_w=0 ; struct task_struct *task = (struct task_struct *)bpf_get_current_task(); @ execsnoop.bpf.c:36 6: (85) call bpf_get_current_task#35 ; R0=scalar() ; if (task != NULL) { @ execsnoop.bpf.c:39 7: (15) if r0 == 0x0 goto pc+4 ; R0=scalar(umin=1) ; if (task->real_parent != NULL) { @ execsnoop.bpf.c:40 8: (79) r1 = *(u64 *)(r0 +1584) R0 invalid mem access 'scalar' processed 9 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 1 -- END PROG LOAD LOG -- libbpf: prog 'tracepoint__syscalls__sys_enter_execve': failed to load: -13 libbpf: failed to load object 'execsnoop_bpf' libbpf: failed to load BPF skeleton 'execsnoop_bpf': -13 Failed to load and verify BPF skeleton
错误原因
BPF verifier拒绝内存访问的核心原因:
bpf_get_current_task()返回的指针被标记为scalar类型,未关联task_struct的BTF元信息,verifier无法确认指针指向合法内核结构体- 直接链式访问
task->real_parent->tgid属于未验证的内核内存操作,verifier无法确保其安全性
解决方案
方案1:使用bpf_get_current_task_btf()(内核≥5.8)
Linux 5.8及以上版本提供的bpf_get_current_task_btf() helper,会返回关联了BTF类型的task_struct指针,让verifier能识别结构体成员的偏移与合法性,允许直接访问成员。
修改获取task的代码:
struct task_struct *task = bpf_get_current_task_btf();
方案2:使用bpf_probe_read_kernel()(兼容旧内核)
若需兼容更低版本内核,通过bpf_probe_read_kernel()显式读取内核内存,向verifier声明安全的内存访问:
修改PPID获取代码段:
struct task_struct *task = (struct task_struct *)bpf_get_current_task(); pid_t parent_pid = 0; if (task != NULL) { struct task_struct *real_parent; // 安全读取real_parent指针 if (bpf_probe_read_kernel(&real_parent, sizeof(real_parent), &task->real_parent) == 0 && real_parent != NULL) { // 安全读取父进程的tgid bpf_probe_read_kernel(&parent_pid, sizeof(parent_pid), &real_parent->tgid); } }
额外注意事项
- 确保
vmlinux.h是针对当前运行内核生成的,重新执行命令:bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h - 编译时链接最新版libbpf库,确保支持所用的helper函数
内容的提问来源于stack exchange,提问作者peng zhang
相关产品推荐
相关产品推荐

