在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
相关产品推荐
相关产品推荐

