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

在Rust aya-bpf的fentry程序中正确使用bpf_d_path,解决验证器“R1 type=fp expected=ptr_”报错

在Rust aya-bpf的fentry程序中正确使用bpf_d_path,解决验证器“R1 type=fp expected=ptr_”报错

我之前在使用aya编写eBPF fentry钩子时也碰到过一模一样的问题,这个报错本质是eBPF验证器对指针类型的严格检查——你传给bpf_d_path的第一个参数类型不对,大概率是误用了帧指针(fp)或者没有正确获取内核对象的指针。咱们一步步来解决:

先搞懂报错原因

R1 type=fp expected=ptr_意思是bpf_d_path的第一个参数(R1寄存器)是帧指针(frame pointer),但验证器期望的是一个合法的内核指针(比如struct path *)。这通常是因为你没有正确从filp_close的参数中获取struct file *,或者直接用了错误的指针类型调用bpf_d_path。

正确的实现步骤和代码示例

首先要明确:filp_close的第一个参数是struct file *file,我们需要从这个指针里取出f_path字段(类型是struct path),再把这个struct path的指针传给bpf_d_path。同时,所有内核内存的访问都必须用aya提供的安全方法,不能直接解引用。

下面是修正后的完整代码片段:

use aya_bpf::{
    bindings::*,
    helpers::bpf_d_path,
    maps::LruHashMap,
    macros::{map, fentry},
    programs::FEntryContext,
};
use core::ptr;

#[repr(C)]
#[derive(Copy, Clone)]
pub struct Event {
    pub pid: u32,
    pub path: [u8; 1024],
}

const INIT_EVENT: Event = Event {
    pid: 0,
    path: [0; 1024],
};

#[map]
pub static TT: LruHashMap<u64, Event> = LruHashMap::with_max_entries(16, 0);

#[fentry(name = "filp_close")]
pub fn try_ff(ctx: &FEntryContext) -> i32 {
    // 1. 从fentry上下文获取filp_close的第一个参数:struct file*
    let file_ptr = match ctx.arg(0).as_ptr::<struct_file>() {
        Ok(ptr) => ptr,
        Err(_) => return 1,
    };

    // 2. 安全读取struct file中的f_path字段(必须用read(),不能直接解引用)
    let path = match unsafe { (*file_ptr).f_path.read() } {
        Ok(p) => p,
        Err(_) => return 2,
    };

    // 3. 在栈上分配路径缓冲区(栈内存是验证器认可的合法内存区域)
    let mut path_buf = [0u8; 1024];

    // 4. 调用bpf_d_path获取文件路径
    let path_len = unsafe {
        bpf_d_path(
            &path as *const struct_path,
            path_buf.as_mut_ptr() as *mut c_char,
            path_buf.len() as u32,
        )
    };

    if path_len < 0 {
        return path_len as i32;
    }

    // 5. 准备并填充Event对象
    let mut event = INIT_EVENT;
    event.pid = ctx.pid();

    // 把路径拷贝到event的path字段,确保字符串以'\0'终止
    unsafe {
        ptr::copy_nonoverlapping(
            path_buf.as_ptr(),
            event.path.as_mut_ptr(),
            path_len as usize,
        );
        if path_len as usize < event.path.len() {
            event.path[path_len as usize] = 0;
        }
    }

    // 6. 将Event存入LruHashMap(这里用PID作为key,你可以根据需求调整)
    let key = ctx.pid() as u64;
    let _ = TT.insert(&key, &event, 0);

    0
}

关键注意事项

  • 正确获取参数指针:一定要用ctx.arg(n)来获取fentry钩子的函数参数,而不是直接使用帧指针。ctx.arg(0)对应filp_close的第一个参数struct file*,这是验证器认可的合法指针。
  • 安全读取内核结构体字段:所有内核结构体的字段访问都必须用.read()方法,aya会自动调用bpf_probe_read_kernel来安全读取内存,避免验证器报错。
  • 缓冲区的选择:这里用栈上的数组作为路径缓冲区,栈内存是eBPF程序可以安全访问的区域;你也可以用PerCpuArray之类的map,但栈上分配更高效。
  • 字符串终止处理:bpf_d_path不会自动添加'\0'终止符,所以我们需要手动添加,避免后续用户态程序读取时出现乱码。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 12:55:31