使用ebpf-go编写sys_read kprobe程序时遇加载权限拒绝错误
解决eBPF程序加载时的R3 !read_ok错误
运行eBPF程序时出现的R3 !read_ok错误,核心原因有两个:一是eBPF验证器认为直接使用的sys_read参数指针(buf)不可访问;二是哈希表的value类型定义与实际存储的数据不匹配。以下是具体修复步骤:
一、修复eBPF C代码(logger.c)
1. 修正哈希表的value类型
原map定义中__type(value, __u64)与实际要存储的struct data_args_t类型不匹配,改为对应结构体类型:
//go:build ignore #include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> struct data_args_t { int fd; __u32 pid; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __u32); __type(value, struct data_args_t); // 修改为结构体类型 __uint(max_entries, 100); } active_read_args_map SEC(".maps"); SEC("kprobe/sys_read") int syscall__probe_entry_read(struct pt_regs* ctx) { // 通过PT_REGS宏获取系统调用参数,适配不同架构 int fd = PT_REGS_PARM1(ctx); __u32 pid = bpf_get_current_pid_tgid() >> 32; // 获取PID(高32位) struct data_args_t read_args = {}; read_args.fd = fd; read_args.pid = pid; // 用PID作为key存入哈希表 bpf_map_update_elem(&active_read_args_map, &pid, &read_args, BPF_NOEXIST); return 0; } char __license[] SEC("license") = "Dual MIT/GPL";
2. 正确获取系统调用参数
不要直接在kprobe函数参数中声明sys_read的参数,改用PT_REGS_PARM*宏获取,避免架构兼容性问题,同时让eBPF验证器认可寄存器的合法性。如果需要记录读取的内容,要使用bpf_probe_read_user读取用户空间数据到内核缓冲区再存入map,示例:
// 读取buf的前32个字节示例 char buf_data[32] = {}; bpf_probe_read_user(buf_data, sizeof(buf_data), (void*)PT_REGS_PARM2(ctx)); // 将buf_data添加到struct data_args_t中存储
二、修复Go代码(main.go)
1. 定义与eBPF结构体对应的Go结构体
在main.go中定义和C代码中struct data_args_t一致的结构体,用于map lookup:
package main import ( "log" "time" "os" "os/signal" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/rlimit" ) // 与C代码中的struct data_args_t完全对应 type dataArgsT struct { FD int32 PID uint32 } func main() { if err := rlimit.RemoveMemlock(); err != nil { log.Fatal("Removing memlock:", err) } var objs loggerObjects if err := loadLoggerObjects(&objs, nil); err != nil { log.Fatal("Loading eBPF objects:", err) } defer objs.Close() // 注意:kprobe的名称要对应内核中的sys_read入口,部分内核为__sys_read link, err := link.Kprobe("sys_read", objs.SyscallProbeEntryRead, nil) if err != nil { log.Fatal("Attaching kprobe:", err) } defer link.Close() tick := time.Tick(time.Second) stop := make(chan os.Signal, 5) signal.Notify(stop, os.Interrupt) for { select { case <-tick: // 遍历哈希表中的所有条目 var key uint32 var value dataArgsT iter := objs.ActiveReadArgsMap.Iterate() for iter.Next(&key, &value) { log.Printf("PID %d: read from FD %d", value.PID, value.FD) } if err := iter.Err(); err != nil { log.Fatal("Map iteration error:", err) } case <-stop: log.Print("Received signal, exiting...") return } } }
2. 修正Map Lookup逻辑
原代码中用uint32(0)作为key查询是错误的,改用Iterate()方法遍历所有条目,适合查看所有记录。
三、重新生成并运行
- 重新执行
go generate生成新的logger_bpfel.go - 编译:
go build - 运行:
sudo ./ebpf_logger
内容的提问来源于stack exchange,提问作者ObiWanKnewby
相关产品推荐
相关产品推荐

