Merge branch 'bpf: add __user tagging support in vmlinux BTF'
Yonghong Song says: ==================== The __user attribute is currently mainly used by sparse for type checking. The attribute indicates whether a memory access is in user memory address space or not. Such information is important during tracing kernel internal functions or data structures as accessing user memory often has different mechanisms compared to accessing kernel memory. For example, the perf-probe needs explicit command line specification to indicate a particular argument or string in user-space memory ([1], [2], [3]). Currently, vmlinux BTF is available in kernel with many distributions. If __user attribute information is available in vmlinux BTF, the explicit user memory access information from users will not be necessary as the kernel can figure it out by itself with vmlinux BTF. Besides the above possible use for perf/probe, another use case is for bpf verifier. Currently, for bpf BPF_PROG_TYPE_TRACING type of bpf programs, users can write direct code like p->m1->m2 and "p" could be a function parameter. Without __user information in BTF, the verifier will assume p->m1 accessing kernel memory and will generate normal loads. Let us say "p" actually tagged with __user in the source code. In such cases, p->m1 is actually accessing user memory and direct load is not right and may produce incorrect result. For such cases, bpf_probe_read_user() will be the correct way to read p->m1. To support encoding __user information in BTF, a new attribute __attribute__((btf_type_tag("<arbitrary_string>"))) is implemented in clang ([4]). For example, if we have #define __user __attribute__((btf_type_tag("user"))) during kernel compilation, the attribute "user" information will be preserved in dwarf. After pahole converting dwarf to BTF, __user information will be available in vmlinux BTF and such information can be used by bpf verifier, perf/probe or other use cases. Currently btf_type_tag is only supported in clang (>= clang14) and pahole (>= 1.23). gcc support is also proposed and under development ([5]). In the rest of patch set, Patch 1 added support of __user btf_type_tag during compilation. Patch 2 added bpf verifier support to utilize __user tag information to reject bpf programs not using proper helper to access user memories. Patches 3-5 are for bpf selftests which demonstrate verifier can reject direct user memory accesses. [1] http://lkml.kernel.org/r/155789874562.26965.10836126971405890891.stgit@devnote2 [2] http://lkml.kernel.org/r/155789872187.26965.4468456816590888687.stgit@devnote2 [3] http://lkml.kernel.org/r/155789871009.26965.14167558859557329331.stgit@devnote2 [4] https://reviews.llvm.org/D111199 [5] https://lore.kernel.org/bpf/0cbeb2fb-1a18-f690-e360-24b1c90c2a91@fb.com/ Changelog: v2 -> v3: - remove FLAG_DONTCARE enumerator and just use 0 as dontcare flag. - explain how btf type_tag is encoded in btf type chain. v1 -> v2: - use MEM_USER flag for PTR_TO_BTF_ID reg type instead of a separate field to encode __user tag. - add a test with kernel function __sys_getsockname which has __user tagged argument. ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
50fc9786b2
@ -503,6 +503,19 @@ valid index (starting from 0) pointing to a member or an argument.
|
||||
* ``info.vlen``: 0
|
||||
* ``type``: the type with ``btf_type_tag`` attribute
|
||||
|
||||
Currently, ``BTF_KIND_TYPE_TAG`` is only emitted for pointer types.
|
||||
It has the following btf type chain:
|
||||
::
|
||||
|
||||
ptr -> [type_tag]*
|
||||
-> [const | volatile | restrict | typedef]*
|
||||
-> base_type
|
||||
|
||||
Basically, a pointer type points to zero or more
|
||||
type_tag, then zero or more const/volatile/restrict/typedef
|
||||
and finally the base type. The base type is one of
|
||||
int, ptr, array, struct, union, enum, func_proto and float types.
|
||||
|
||||
3. BTF Kernel API
|
||||
=================
|
||||
|
||||
|
@ -332,7 +332,10 @@ enum bpf_type_flag {
|
||||
*/
|
||||
MEM_ALLOC = BIT(2 + BPF_BASE_TYPE_BITS),
|
||||
|
||||
__BPF_TYPE_LAST_FLAG = MEM_ALLOC,
|
||||
/* MEM is in user address space. */
|
||||
MEM_USER = BIT(3 + BPF_BASE_TYPE_BITS),
|
||||
|
||||
__BPF_TYPE_LAST_FLAG = MEM_USER,
|
||||
};
|
||||
|
||||
/* Max number of base types. */
|
||||
@ -588,7 +591,7 @@ struct bpf_verifier_ops {
|
||||
const struct btf *btf,
|
||||
const struct btf_type *t, int off, int size,
|
||||
enum bpf_access_type atype,
|
||||
u32 *next_btf_id);
|
||||
u32 *next_btf_id, enum bpf_type_flag *flag);
|
||||
};
|
||||
|
||||
struct bpf_prog_offload_ops {
|
||||
@ -1780,7 +1783,7 @@ static inline bool bpf_tracing_btf_ctx_access(int off, int size,
|
||||
int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, int off, int size,
|
||||
enum bpf_access_type atype,
|
||||
u32 *next_btf_id);
|
||||
u32 *next_btf_id, enum bpf_type_flag *flag);
|
||||
bool btf_struct_ids_match(struct bpf_verifier_log *log,
|
||||
const struct btf *btf, u32 id, int off,
|
||||
const struct btf *need_btf, u32 need_type_id);
|
||||
|
@ -238,6 +238,11 @@ static inline bool btf_type_is_var(const struct btf_type *t)
|
||||
return BTF_INFO_KIND(t->info) == BTF_KIND_VAR;
|
||||
}
|
||||
|
||||
static inline bool btf_type_is_type_tag(const struct btf_type *t)
|
||||
{
|
||||
return BTF_INFO_KIND(t->info) == BTF_KIND_TYPE_TAG;
|
||||
}
|
||||
|
||||
/* union is only a special case of struct:
|
||||
* all its offsetof(member) == 0
|
||||
*/
|
||||
|
@ -31,6 +31,9 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
|
||||
# define __kernel
|
||||
# ifdef STRUCTLEAK_PLUGIN
|
||||
# define __user __attribute__((user))
|
||||
# elif defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \
|
||||
__has_attribute(btf_type_tag)
|
||||
# define __user __attribute__((btf_type_tag("user")))
|
||||
# else
|
||||
# define __user
|
||||
# endif
|
||||
|
@ -4886,6 +4886,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
||||
const char *tname = prog->aux->attach_func_name;
|
||||
struct bpf_verifier_log *log = info->log;
|
||||
const struct btf_param *args;
|
||||
const char *tag_value;
|
||||
u32 nr_args, arg;
|
||||
int i, ret;
|
||||
|
||||
@ -5038,6 +5039,13 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
||||
info->btf = btf;
|
||||
info->btf_id = t->type;
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
|
||||
if (btf_type_is_type_tag(t)) {
|
||||
tag_value = __btf_name_by_offset(btf, t->name_off);
|
||||
if (strcmp(tag_value, "user") == 0)
|
||||
info->reg_type |= MEM_USER;
|
||||
}
|
||||
|
||||
/* skip modifiers */
|
||||
while (btf_type_is_modifier(t)) {
|
||||
info->btf_id = t->type;
|
||||
@ -5064,12 +5072,12 @@ enum bpf_struct_walk_result {
|
||||
|
||||
static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, int off, int size,
|
||||
u32 *next_btf_id)
|
||||
u32 *next_btf_id, enum bpf_type_flag *flag)
|
||||
{
|
||||
u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
|
||||
const struct btf_type *mtype, *elem_type = NULL;
|
||||
const struct btf_member *member;
|
||||
const char *tname, *mname;
|
||||
const char *tname, *mname, *tag_value;
|
||||
u32 vlen, elem_id, mid;
|
||||
|
||||
again:
|
||||
@ -5253,7 +5261,8 @@ error:
|
||||
}
|
||||
|
||||
if (btf_type_is_ptr(mtype)) {
|
||||
const struct btf_type *stype;
|
||||
const struct btf_type *stype, *t;
|
||||
enum bpf_type_flag tmp_flag = 0;
|
||||
u32 id;
|
||||
|
||||
if (msize != size || off != moff) {
|
||||
@ -5262,9 +5271,19 @@ error:
|
||||
mname, moff, tname, off, size);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* check __user tag */
|
||||
t = btf_type_by_id(btf, mtype->type);
|
||||
if (btf_type_is_type_tag(t)) {
|
||||
tag_value = __btf_name_by_offset(btf, t->name_off);
|
||||
if (strcmp(tag_value, "user") == 0)
|
||||
tmp_flag = MEM_USER;
|
||||
}
|
||||
|
||||
stype = btf_type_skip_modifiers(btf, mtype->type, &id);
|
||||
if (btf_type_is_struct(stype)) {
|
||||
*next_btf_id = id;
|
||||
*flag = tmp_flag;
|
||||
return WALK_PTR;
|
||||
}
|
||||
}
|
||||
@ -5291,13 +5310,14 @@ error:
|
||||
int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, int off, int size,
|
||||
enum bpf_access_type atype __maybe_unused,
|
||||
u32 *next_btf_id)
|
||||
u32 *next_btf_id, enum bpf_type_flag *flag)
|
||||
{
|
||||
enum bpf_type_flag tmp_flag = 0;
|
||||
int err;
|
||||
u32 id;
|
||||
|
||||
do {
|
||||
err = btf_struct_walk(log, btf, t, off, size, &id);
|
||||
err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag);
|
||||
|
||||
switch (err) {
|
||||
case WALK_PTR:
|
||||
@ -5305,6 +5325,7 @@ int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
* we're done.
|
||||
*/
|
||||
*next_btf_id = id;
|
||||
*flag = tmp_flag;
|
||||
return PTR_TO_BTF_ID;
|
||||
case WALK_SCALAR:
|
||||
return SCALAR_VALUE;
|
||||
@ -5349,6 +5370,7 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
|
||||
const struct btf *need_btf, u32 need_type_id)
|
||||
{
|
||||
const struct btf_type *type;
|
||||
enum bpf_type_flag flag;
|
||||
int err;
|
||||
|
||||
/* Are we already done? */
|
||||
@ -5359,7 +5381,7 @@ again:
|
||||
type = btf_type_by_id(btf, id);
|
||||
if (!type)
|
||||
return false;
|
||||
err = btf_struct_walk(log, btf, type, off, 1, &id);
|
||||
err = btf_struct_walk(log, btf, type, off, 1, &id, &flag);
|
||||
if (err != WALK_STRUCT)
|
||||
return false;
|
||||
|
||||
|
@ -536,7 +536,7 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)
|
||||
static const char *reg_type_str(struct bpf_verifier_env *env,
|
||||
enum bpf_reg_type type)
|
||||
{
|
||||
char postfix[16] = {0}, prefix[16] = {0};
|
||||
char postfix[16] = {0}, prefix[32] = {0};
|
||||
static const char * const str[] = {
|
||||
[NOT_INIT] = "?",
|
||||
[SCALAR_VALUE] = "inv",
|
||||
@ -570,9 +570,11 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
|
||||
}
|
||||
|
||||
if (type & MEM_RDONLY)
|
||||
strncpy(prefix, "rdonly_", 16);
|
||||
strncpy(prefix, "rdonly_", 32);
|
||||
if (type & MEM_ALLOC)
|
||||
strncpy(prefix, "alloc_", 16);
|
||||
strncpy(prefix, "alloc_", 32);
|
||||
if (type & MEM_USER)
|
||||
strncpy(prefix, "user_", 32);
|
||||
|
||||
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
|
||||
prefix, str[base_type(type)], postfix);
|
||||
@ -1547,14 +1549,15 @@ static void mark_reg_not_init(struct bpf_verifier_env *env,
|
||||
static void mark_btf_ld_reg(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *regs, u32 regno,
|
||||
enum bpf_reg_type reg_type,
|
||||
struct btf *btf, u32 btf_id)
|
||||
struct btf *btf, u32 btf_id,
|
||||
enum bpf_type_flag flag)
|
||||
{
|
||||
if (reg_type == SCALAR_VALUE) {
|
||||
mark_reg_unknown(env, regs, regno);
|
||||
return;
|
||||
}
|
||||
mark_reg_known_zero(env, regs, regno);
|
||||
regs[regno].type = PTR_TO_BTF_ID;
|
||||
regs[regno].type = PTR_TO_BTF_ID | flag;
|
||||
regs[regno].btf = btf;
|
||||
regs[regno].btf_id = btf_id;
|
||||
}
|
||||
@ -4152,6 +4155,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *reg = regs + regno;
|
||||
const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id);
|
||||
const char *tname = btf_name_by_offset(reg->btf, t->name_off);
|
||||
enum bpf_type_flag flag = 0;
|
||||
u32 btf_id;
|
||||
int ret;
|
||||
|
||||
@ -4171,9 +4175,16 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (reg->type & MEM_USER) {
|
||||
verbose(env,
|
||||
"R%d is ptr_%s access user memory: off=%d\n",
|
||||
regno, tname, off);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (env->ops->btf_struct_access) {
|
||||
ret = env->ops->btf_struct_access(&env->log, reg->btf, t,
|
||||
off, size, atype, &btf_id);
|
||||
off, size, atype, &btf_id, &flag);
|
||||
} else {
|
||||
if (atype != BPF_READ) {
|
||||
verbose(env, "only read is supported\n");
|
||||
@ -4181,14 +4192,14 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
|
||||
}
|
||||
|
||||
ret = btf_struct_access(&env->log, reg->btf, t, off, size,
|
||||
atype, &btf_id);
|
||||
atype, &btf_id, &flag);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (atype == BPF_READ && value_regno >= 0)
|
||||
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id);
|
||||
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4201,6 +4212,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
|
||||
{
|
||||
struct bpf_reg_state *reg = regs + regno;
|
||||
struct bpf_map *map = reg->map_ptr;
|
||||
enum bpf_type_flag flag = 0;
|
||||
const struct btf_type *t;
|
||||
const char *tname;
|
||||
u32 btf_id;
|
||||
@ -4238,12 +4250,12 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
ret = btf_struct_access(&env->log, btf_vmlinux, t, off, size, atype, &btf_id);
|
||||
ret = btf_struct_access(&env->log, btf_vmlinux, t, off, size, atype, &btf_id, &flag);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (value_regno >= 0)
|
||||
mark_btf_ld_reg(env, regs, value_regno, ret, btf_vmlinux, btf_id);
|
||||
mark_btf_ld_reg(env, regs, value_regno, ret, btf_vmlinux, btf_id, flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4444,7 +4456,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = check_ctx_access(env, insn_idx, off, size, t, ®_type, &btf, &btf_id);
|
||||
err = check_ctx_access(env, insn_idx, off, size, t, ®_type, &btf,
|
||||
&btf_id);
|
||||
if (err)
|
||||
verbose_linfo(env, insn_idx, "; ");
|
||||
if (!err && t == BPF_READ && value_regno >= 0) {
|
||||
|
@ -325,6 +325,14 @@ config DEBUG_INFO_BTF
|
||||
config PAHOLE_HAS_SPLIT_BTF
|
||||
def_bool $(success, test `$(PAHOLE) --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/'` -ge "119")
|
||||
|
||||
config PAHOLE_HAS_BTF_TAG
|
||||
def_bool $(success, test `$(PAHOLE) --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/'` -ge "123")
|
||||
depends on CC_IS_CLANG
|
||||
help
|
||||
Decide whether pahole emits btf_tag attributes (btf_type_tag and
|
||||
btf_decl_tag) or not. Currently only clang compiler implements
|
||||
these attributes, so make the config depend on CC_IS_CLANG.
|
||||
|
||||
config DEBUG_INFO_BTF_MODULES
|
||||
def_bool y
|
||||
depends on DEBUG_INFO_BTF && MODULES && PAHOLE_HAS_SPLIT_BTF
|
||||
|
@ -145,7 +145,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log,
|
||||
const struct btf *btf,
|
||||
const struct btf_type *t, int off,
|
||||
int size, enum bpf_access_type atype,
|
||||
u32 *next_btf_id)
|
||||
u32 *next_btf_id,
|
||||
enum bpf_type_flag *flag)
|
||||
{
|
||||
const struct btf_type *state;
|
||||
s32 type_id;
|
||||
@ -162,7 +163,8 @@ static int bpf_dummy_ops_btf_struct_access(struct bpf_verifier_log *log,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id);
|
||||
err = btf_struct_access(log, btf, t, off, size, atype, next_btf_id,
|
||||
flag);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -96,12 +96,14 @@ static int bpf_tcp_ca_btf_struct_access(struct bpf_verifier_log *log,
|
||||
const struct btf *btf,
|
||||
const struct btf_type *t, int off,
|
||||
int size, enum bpf_access_type atype,
|
||||
u32 *next_btf_id)
|
||||
u32 *next_btf_id,
|
||||
enum bpf_type_flag *flag)
|
||||
{
|
||||
size_t end;
|
||||
|
||||
if (atype == BPF_READ)
|
||||
return btf_struct_access(log, btf, t, off, size, atype, next_btf_id);
|
||||
return btf_struct_access(log, btf, t, off, size, atype, next_btf_id,
|
||||
flag);
|
||||
|
||||
if (t != tcp_sock_type) {
|
||||
bpf_log(log, "only read is supported\n");
|
||||
|
@ -206,6 +206,8 @@ btf_tag test and Clang version
|
||||
|
||||
The btf_tag selftest requires LLVM support to recognize the btf_decl_tag and
|
||||
btf_type_tag attributes. They are introduced in `Clang 14` [0_, 1_].
|
||||
The subtests ``btf_type_tag_user_{mod1, mod2, vmlinux}`` also requires
|
||||
pahole version ``1.23``.
|
||||
|
||||
Without them, the btf_tag selftest will be skipped and you will observe:
|
||||
|
||||
|
@ -21,6 +21,24 @@ bpf_testmod_test_mod_kfunc(int i)
|
||||
*(int *)this_cpu_ptr(&bpf_testmod_ksym_percpu) = i;
|
||||
}
|
||||
|
||||
struct bpf_testmod_btf_type_tag_1 {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct bpf_testmod_btf_type_tag_2 {
|
||||
struct bpf_testmod_btf_type_tag_1 __user *p;
|
||||
};
|
||||
|
||||
noinline int
|
||||
bpf_testmod_test_btf_type_tag_user_1(struct bpf_testmod_btf_type_tag_1 __user *arg) {
|
||||
return arg->a;
|
||||
}
|
||||
|
||||
noinline int
|
||||
bpf_testmod_test_btf_type_tag_user_2(struct bpf_testmod_btf_type_tag_2 *arg) {
|
||||
return arg->p->a;
|
||||
}
|
||||
|
||||
noinline int bpf_testmod_loop_test(int n)
|
||||
{
|
||||
int i, sum = 0;
|
||||
|
@ -1,19 +1,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include "btf_decl_tag.skel.h"
|
||||
#include <bpf/btf.h>
|
||||
#include "test_btf_decl_tag.skel.h"
|
||||
|
||||
/* struct btf_type_tag_test is referenced in btf_type_tag.skel.h */
|
||||
struct btf_type_tag_test {
|
||||
int **p;
|
||||
};
|
||||
#include "btf_type_tag.skel.h"
|
||||
#include "btf_type_tag_user.skel.h"
|
||||
|
||||
static void test_btf_decl_tag(void)
|
||||
{
|
||||
struct btf_decl_tag *skel;
|
||||
struct test_btf_decl_tag *skel;
|
||||
|
||||
skel = btf_decl_tag__open_and_load();
|
||||
skel = test_btf_decl_tag__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "btf_decl_tag"))
|
||||
return;
|
||||
|
||||
@ -22,7 +24,7 @@ static void test_btf_decl_tag(void)
|
||||
test__skip();
|
||||
}
|
||||
|
||||
btf_decl_tag__destroy(skel);
|
||||
test_btf_decl_tag__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_btf_type_tag(void)
|
||||
@ -41,10 +43,101 @@ static void test_btf_type_tag(void)
|
||||
btf_type_tag__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_btf_type_tag_mod_user(bool load_test_user1)
|
||||
{
|
||||
const char *module_name = "bpf_testmod";
|
||||
struct btf *vmlinux_btf, *module_btf;
|
||||
struct btf_type_tag_user *skel;
|
||||
__s32 type_id;
|
||||
int err;
|
||||
|
||||
if (!env.has_testmod) {
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
|
||||
/* skip the test if the module does not have __user tags */
|
||||
vmlinux_btf = btf__load_vmlinux_btf();
|
||||
if (!ASSERT_OK_PTR(vmlinux_btf, "could not load vmlinux BTF"))
|
||||
return;
|
||||
|
||||
module_btf = btf__load_module_btf(module_name, vmlinux_btf);
|
||||
if (!ASSERT_OK_PTR(module_btf, "could not load module BTF"))
|
||||
goto free_vmlinux_btf;
|
||||
|
||||
type_id = btf__find_by_name_kind(module_btf, "user", BTF_KIND_TYPE_TAG);
|
||||
if (type_id <= 0) {
|
||||
printf("%s:SKIP: btf_type_tag attribute not in %s", __func__, module_name);
|
||||
test__skip();
|
||||
goto free_module_btf;
|
||||
}
|
||||
|
||||
skel = btf_type_tag_user__open();
|
||||
if (!ASSERT_OK_PTR(skel, "btf_type_tag_user"))
|
||||
goto free_module_btf;
|
||||
|
||||
bpf_program__set_autoload(skel->progs.test_sys_getsockname, false);
|
||||
if (load_test_user1)
|
||||
bpf_program__set_autoload(skel->progs.test_user2, false);
|
||||
else
|
||||
bpf_program__set_autoload(skel->progs.test_user1, false);
|
||||
|
||||
err = btf_type_tag_user__load(skel);
|
||||
ASSERT_ERR(err, "btf_type_tag_user");
|
||||
|
||||
btf_type_tag_user__destroy(skel);
|
||||
|
||||
free_module_btf:
|
||||
btf__free(module_btf);
|
||||
free_vmlinux_btf:
|
||||
btf__free(vmlinux_btf);
|
||||
}
|
||||
|
||||
static void test_btf_type_tag_vmlinux_user(void)
|
||||
{
|
||||
struct btf_type_tag_user *skel;
|
||||
struct btf *vmlinux_btf;
|
||||
__s32 type_id;
|
||||
int err;
|
||||
|
||||
/* skip the test if the vmlinux does not have __user tags */
|
||||
vmlinux_btf = btf__load_vmlinux_btf();
|
||||
if (!ASSERT_OK_PTR(vmlinux_btf, "could not load vmlinux BTF"))
|
||||
return;
|
||||
|
||||
type_id = btf__find_by_name_kind(vmlinux_btf, "user", BTF_KIND_TYPE_TAG);
|
||||
if (type_id <= 0) {
|
||||
printf("%s:SKIP: btf_type_tag attribute not in vmlinux btf", __func__);
|
||||
test__skip();
|
||||
goto free_vmlinux_btf;
|
||||
}
|
||||
|
||||
skel = btf_type_tag_user__open();
|
||||
if (!ASSERT_OK_PTR(skel, "btf_type_tag_user"))
|
||||
goto free_vmlinux_btf;
|
||||
|
||||
bpf_program__set_autoload(skel->progs.test_user2, false);
|
||||
bpf_program__set_autoload(skel->progs.test_user1, false);
|
||||
|
||||
err = btf_type_tag_user__load(skel);
|
||||
ASSERT_ERR(err, "btf_type_tag_user");
|
||||
|
||||
btf_type_tag_user__destroy(skel);
|
||||
|
||||
free_vmlinux_btf:
|
||||
btf__free(vmlinux_btf);
|
||||
}
|
||||
|
||||
void test_btf_tag(void)
|
||||
{
|
||||
if (test__start_subtest("btf_decl_tag"))
|
||||
test_btf_decl_tag();
|
||||
if (test__start_subtest("btf_type_tag"))
|
||||
test_btf_type_tag();
|
||||
if (test__start_subtest("btf_type_tag_user_mod1"))
|
||||
test_btf_type_tag_mod_user(true);
|
||||
if (test__start_subtest("btf_type_tag_user_mod2"))
|
||||
test_btf_type_tag_mod_user(false);
|
||||
if (test__start_subtest("btf_type_tag_sys_user_vmlinux"))
|
||||
test_btf_type_tag_vmlinux_user();
|
||||
}
|
||||
|
40
tools/testing/selftests/bpf/progs/btf_type_tag_user.c
Normal file
40
tools/testing/selftests/bpf/progs/btf_type_tag_user.c
Normal file
@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022 Facebook */
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
struct bpf_testmod_btf_type_tag_1 {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct bpf_testmod_btf_type_tag_2 {
|
||||
struct bpf_testmod_btf_type_tag_1 *p;
|
||||
};
|
||||
|
||||
int g;
|
||||
|
||||
SEC("fentry/bpf_testmod_test_btf_type_tag_user_1")
|
||||
int BPF_PROG(test_user1, struct bpf_testmod_btf_type_tag_1 *arg)
|
||||
{
|
||||
g = arg->a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fentry/bpf_testmod_test_btf_type_tag_user_2")
|
||||
int BPF_PROG(test_user2, struct bpf_testmod_btf_type_tag_2 *arg)
|
||||
{
|
||||
g = arg->p->a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int __sys_getsockname(int fd, struct sockaddr __user *usockaddr,
|
||||
* int __user *usockaddr_len);
|
||||
*/
|
||||
SEC("fentry/__sys_getsockname")
|
||||
int BPF_PROG(test_sys_getsockname, int fd, struct sockaddr *usockaddr,
|
||||
int *usockaddr_len)
|
||||
{
|
||||
g = usockaddr->sa_family;
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user