如何创建一个新的 kfunc 并将 eBPF 映射中的条目作为参数传递给它?

问题描述 投票:0回答:1

我正在探索 eBPF 的

kfunc
功能。我想知道是否可以将从映射(例如数组)获取的指针传递给我在自定义内核模块中定义的函数(我将其标记为
kfunc
)?我查看了 来自内核的文档页面,并准备了一个简单的内核模块,添加了函数包装
memcpy
作为示例。当尝试使用此
kfunc
加载 BPF 程序时,验证者会抱怨如下所示:

arg#0 pointer type UNKNOWN  must point to scalar, or struct with scalar
processed 66 insns (limit 1000000) max_states_per_insn 1 total_states 6 peak_states 6 mark_read 4
-- END PROG LOAD LOG --

查看文档,我不确定注释我的

kfunc
参数的正确方法是什么,这样它就不是未知的。所以我想知道目前是否允许将指针从地图传递到自定义
kfunc

下面是我用来测试此功能的。

内核模块

#include <linux/module.h>
#include <linux/printk.h>
#include <linux/string.h> /* memcpy */
#include <linux/btf.h>
MODULE_LICENSE("GPL");

/* Define a kfunc function */
__bpf_kfunc_start_defs();

__bpf_kfunc void *my_kfunc_memcpy(void *dst, void *src, __u32 src__sz)
{
    return memcpy(dst, src, src__sz);
}

__bpf_kfunc_end_defs();

/* Encode the function(s) into BTF */

/*
 * These will probably be the new API
 * */
/* BTF_KFUNCS_START(bpf_my_string_set) */
/* BTF_ID_FLAGS(func, my_kfunc_memcpy, 0) */
/* BTF_KFUNCS_END(bpf_my_string_set) */

BTF_SET8_START(bpf_my_string_set)
BTF_ID_FLAGS(func, my_kfunc_memcpy, 0)
BTF_SET8_END(bpf_my_string_set)

static const struct btf_kfunc_id_set my_kfunc_memcpy_kfunc_set = {
        .owner = THIS_MODULE,
        .set   = &bpf_my_string_set,
};

static int myinit(void)
{
    /* Register the BTF */
    register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &my_kfunc_memcpy_kfunc_set);
    pr_info("Load memcpy kfunc\n");
    return 0;
}

static void myexit(void)
{
    pr_info("Unloading memcpy kfunc\n");
}

module_init(myinit)
module_exit(myexit)

BPF 计划

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

extern void *my_kfunc_memcpy(void *dst, void *src, __u32 src__sz) __ksym;
#define MEMCPY(...) my_kfunc_memcpy(__VA_ARGS__)

struct item {
    char data[1000];
};

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(key,  __u32);
    __type(value, struct item);
    __uint(max_entries, REPEAT + 1);
} a_map SEC(".maps");

SEC("xdp")
int prog(struct xdp_md *xdp)
{
    int i = 0, ii = 1;
    struct item *d = bpf_map_lookup_elem(&a_map, &i);
    if (d == NULL) return XDP_PASS;
    struct item *it = bpf_map_lookup_elem(&a_map, &ii);
    if (it == NULL) return XDP_PASS;
    MEMCPY(it->data, d->data, 1000);
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
ebpf bpf
1个回答
0
投票

arg#0 指针类型 UNKNOWN 必须指向标量,或带有标量的结构

抛出此错误是因为内核默认进行严格的类型检查。因此,假设您的参数是

struct sometype*
,那么验证程序将确保该类型确实已传入,并且指针指向有效的内存区域(不允许意外溢出等)

void *my_kfunc_memcpy(void *dst, void *src, __u32 src__sz)

在函数签名中,您已经注释了

src_sz
,它通知验证者忽略对
src
的类型检查,但
dst
仍在进行类型检查。解决此问题的一种方法可能是添加目的地的大小。

void *my_kfunc_memcpy(void *dst, __u32 dst__sz, void *src, __u32 src__sz)

这将是安全选项,因为它应该确保读取和写入方面的内存安全。虽然我不确定这些检查是否适用于指向映射值的指针。

第二个选项您可以忽略类型检查。官方文档目前有点过时,在较新的内核版本中有更多后缀可用于参数。在这种情况下,在 v6.2

 中添加的 
_ign 后缀可能会起到作用。

实际上最近

添加了一个官方kfunc,与您想要做的类似,我们可以使用它作为示例。

__bpf_kfunc int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void __user *unsafe_ptr__ign, u64 标志)

因此将您的功能更改为:

void *my_kfunc_memcpy(void *dst, u32 dst__sz, void *src__ign)

也许可以解决问题。现在这并不能保证阅读方面的安全。允许内置 kfunc 执行此操作的原因是因为它从用户内存中读取。

作为最后的替代方案,如果验证者不允许

dst

成为映射值,您也可以忽略它:

void *my_kfunc_memcpy(void *dst__ign, u32 size, void *src__ign)

我认为这会通过验证程序,但再次强调,使用风险自负。

© www.soinside.com 2019 - 2024. All rights reserved.