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

使用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()方法遍历所有条目,适合查看所有记录。

三、重新生成并运行

  1. 重新执行go generate生成新的logger_bpfel.go
  2. 编译:go build
  3. 运行:sudo ./ebpf_logger

内容的提问来源于stack exchange,提问作者ObiWanKnewby

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:35:09