使用映射将数据从用户空间发送到 bpf 程序的问题
Problem with sending data from userspace to bpf program with maps
我的 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:~$_
为什么我会收到这个错误,我该怎么办??
感谢您的帮助!
TL;DR. 您应该检查 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
returns 每当找不到查找的键时返回 NULL 值。
您可以简单地执行以下操作:
if (my_pid && *my_pid == 3935991) {
return -EPERM;
}
我的 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:~$_
为什么我会收到这个错误,我该怎么办?? 感谢您的帮助!
TL;DR. 您应该检查 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
returns 每当找不到查找的键时返回 NULL 值。
您可以简单地执行以下操作:
if (my_pid && *my_pid == 3935991) {
return -EPERM;
}