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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 07:33:10