首页 > 解决方案 > 使用地图将数据从用户空间发送到 bpf 程序的问题

问题描述

我的 bpf 程序有问题。加载此程序时出现错误。我的 bpf 程序是:

#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <uapi/linux/bpf.h>
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <linux/fs.h>
#include <uapi/asm-generic/errno-base.h>

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __type(key,   int);
    __type(value, ino_t);
    __uint(max_entries, 256);
} qu SEC(".maps");


SEC("lsm/task_kill")
BPF_PROG(
    lsm__task_kill,
    struct task_struct* p, 
    struct kernel_siginfo* info,
    int sig,
    const struct cred* cred
) {
    int key = 0;
    int* my_pid = bpf_map_lookup_elem(&qu, &key);
    if (*my_pid == 3935991) {
        return -EPERM;
    }
    
    bpf_ringbuf_output(&rb, &data, sizeof(data), 0);

    return 0;
}

这是我的用户程序:

#include <bpf/bpf.h>
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/version.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <bpf/libbpf.h>
#include <time.h>


int main(
    int         argc, 
    const char* argv[]
) {
    if (argc <= 1) {
        return -1;
    }
    
    struct stat stat_buf = {0};
    int ret = stat(argv[1], &stat_buf);
    if (ret == -1) {
        printf("ERROR: resolving pathname failed: %s\n", strerror(errno));
        return -1;
    }

    struct bpf_object *obj = bpf_object__open("./my_prog.o");
    if (libbpf_get_error(obj)) {
        fprintf(stderr, "ERROR: opening BPF object file failed\n");
        return 0;
    }

    bpf_object__load(obj);
    if (libbpf_get_error(obj)) {
        fprintf(stderr, "ERROR: opening BPF object file failed\n");
        return 0;
    }

    int qfd = bpf_object__find_map_fd_by_name(obj, "qu");
    if (qfd < 0) {
        fprintf(stderr, "ERROR: finding a queue in obj file failed\n");
        return -1;
    }

    int val = stat_buf.st_ino;
    int key = 0;
    ret = bpf_map_update_elem(qfd, &key, &val, BPF_ANY);
    if (ret < 0) {
        printf("ERROR: updating map failed\n");
        return -1;
    }
    
    
    struct bpf_link*    links[1];
    struct bpf_program* prog;
    int j = 0;
    bpf_object__for_each_program(prog, obj) {
        links[j] = bpf_program__attach_lsm(prog);
        if (libbpf_get_error(links[j])) {
            fprintf(stderr, "ERROR: bpf_program__attach failed\n");
            links[j] = NULL;
            return -1;
        }
        j++;
    }

   
}

制作 bpf 目标文件和编译用户程序时没有错误,但是当我运行用户程序时,我在加载 bpf 程序时收到此错误:

user@host:~$ sudo ./my_prog_loader ./some_file

libbpf: elf: skipping unrecognized data section(22) .eh_frame
libbpf: elf: skipping relo section(23) .rel.eh_frame for section(22) .eh_frame
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
R1 type=ctx expected=fp
; BPF_PROG(
0: (b7) r6 = 0
; Data data = {0};
1: (7b) *(u64 *)(r10 -8) = r6
last_idx 1 first_idx 0
regs=40 stack=0 before 0: (b7) r6 = 0
2: (7b) *(u64 *)(r10 -16) = r6
3: (7b) *(u64 *)(r10 -24) = r6
4: (7b) *(u64 *)(r10 -32) = r6
5: (bf) r1 = r10
6: (07) r1 += -16
; bpf_get_current_comm(data.str, 16);
7: (b7) r2 = 16
8: (85) call bpf_get_current_comm#16
last_idx 8 first_idx 0
regs=4 stack=0 before 7: (b7) r2 = 16
; data.pid  = 000000;
9: (63) *(u32 *)(r10 -32) = r6
; int key = 0;
10: (63) *(u32 *)(r10 -36) = r6
11: (bf) r2 = r10
; 
12: (07) r2 += -36
; ino_t* my_pid = bpf_map_lookup_elem(&qu, &key);
13: (18) r1 = 0xffff8fa64edafc00
15: (85) call bpf_map_lookup_elem#1
; if (*my_pid == 3935991) {
16: (79) r1 = *(u64 *)(r0 +0)
R0 invalid mem access 'map_value_or_null'
processed 16 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 1

libbpf: -- END LOG --
libbpf: failed to load program 'lsm__task_kill'
libbpf: failed to load object './my_prog.o'
ERROR: finding a map in obj file failed

user@host:~$_

为什么我会收到此错误,我该怎么办?感谢您的任何帮助!

标签: clinuxlinux-kernelbpfebpf

解决方案


TL;博士。您应该检查返回的指针bpf_map_lookup_elem是否不是 NULL。


通过以下日志,BPF 验证器告诉您,当它达到 的取消引用时my_pid,指针可能仍具有 NULL 值。因此,它包含一个映射值或一个 NULL 值,即map_value_or_null.

; ino_t* my_pid = bpf_map_lookup_elem(&qu, &key);
13: (18) r1 = 0xffff8fa64edafc00
15: (85) call bpf_map_lookup_elem#1
; if (*my_pid == 3935991) {
16: (79) r1 = *(u64 *)(r0 +0)
R0 invalid mem access 'map_value_or_null'

bpf_map_lookup_elem每当找不到查找的键时返回 NULL 值。

您可以简单地执行以下操作:

if (my_pid && *my_pid == 3935991) {
    return -EPERM;
}

推荐阅读