mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 07:04:10 +08:00
Daniel Borkmann says: ==================== bpf 2022-08-10 We've added 23 non-merge commits during the last 7 day(s) which contain a total of 19 files changed, 424 insertions(+), 35 deletions(-). The main changes are: 1) Several fixes for BPF map iterator such as UAFs along with selftests, from Hou Tao. 2) Fix BPF syscall program's {copy,strncpy}_from_bpfptr() to not fault, from Jinghao Jia. 3) Reject BPF syscall programs calling BPF_PROG_RUN, from Alexei Starovoitov and YiFei Zhu. 4) Fix attach_btf_obj_id info to pick proper target BTF, from Stanislav Fomichev. 5) BPF design Q/A doc update to clarify what is not stable ABI, from Paul E. McKenney. 6) Fix BPF map's prealloc_lru_pop to not reinitialize, from Kumar Kartikeya Dwivedi. 7) Fix bpf_trampoline_put to avoid leaking ftrace hash, from Jiri Olsa. 8) Fix arm64 JIT to address sparse errors around BPF trampoline, from Xu Kuohai. 9) Fix arm64 JIT to use kvcalloc instead of kcalloc for internal program address offset buffer, from Aijun Sun. * https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf: (23 commits) selftests/bpf: Ensure sleepable program is rejected by hash map iter selftests/bpf: Add write tests for sk local storage map iterator selftests/bpf: Add tests for reading a dangling map iter fd bpf: Only allow sleepable program for resched-able iterator bpf: Check the validity of max_rdwr_access for sock local storage map iterator bpf: Acquire map uref in .init_seq_private for sock{map,hash} iterator bpf: Acquire map uref in .init_seq_private for sock local storage map iterator bpf: Acquire map uref in .init_seq_private for hash map iterator bpf: Acquire map uref in .init_seq_private for array map iterator bpf: Disallow bpf programs call prog_run command. bpf, arm64: Fix bpf trampoline instruction endianness selftests/bpf: Add test for prealloc_lru_pop bug bpf: Don't reinit map value in prealloc_lru_pop bpf: Allow calling bpf_prog_test kfuncs in tracing programs bpf, arm64: Allocate program buffer using kvcalloc instead of kcalloc selftests/bpf: Excercise bpf_obj_get_info_by_fd for bpf2bpf bpf: Use proper target btf when exporting attach_btf_obj_id mptcp, btf: Add struct mptcp_sock definition when CONFIG_MPTCP is disabled bpf: Cleanup ftrace hash in bpf_trampoline_put BPF: Fix potential bad pointer dereference in bpf_sys_bpf() ... ==================== Link: https://lore.kernel.org/r/20220810190624.10748-1-daniel@iogearbox.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
fbe8870f72
@ -214,6 +214,12 @@ A: NO. Tracepoints are tied to internal implementation details hence they are
|
||||
subject to change and can break with newer kernels. BPF programs need to change
|
||||
accordingly when this happens.
|
||||
|
||||
Q: Are places where kprobes can attach part of the stable ABI?
|
||||
--------------------------------------------------------------
|
||||
A: NO. The places to which kprobes can attach are internal implementation
|
||||
details, which means that they are subject to change and can break with
|
||||
newer kernels. BPF programs need to change accordingly when this happens.
|
||||
|
||||
Q: How much stack space a BPF program uses?
|
||||
-------------------------------------------
|
||||
A: Currently all program types are limited to 512 bytes of stack
|
||||
@ -273,3 +279,22 @@ cc (congestion-control) implementations. If any of these kernel
|
||||
functions has changed, both the in-tree and out-of-tree kernel tcp cc
|
||||
implementations have to be changed. The same goes for the bpf
|
||||
programs and they have to be adjusted accordingly.
|
||||
|
||||
Q: Attaching to arbitrary kernel functions is an ABI?
|
||||
-----------------------------------------------------
|
||||
Q: BPF programs can be attached to many kernel functions. Do these
|
||||
kernel functions become part of the ABI?
|
||||
|
||||
A: NO.
|
||||
|
||||
The kernel function prototypes will change, and BPF programs attaching to
|
||||
them will need to change. The BPF compile-once-run-everywhere (CO-RE)
|
||||
should be used in order to make it easier to adapt your BPF programs to
|
||||
different versions of the kernel.
|
||||
|
||||
Q: Marking a function with BTF_ID makes that function an ABI?
|
||||
-------------------------------------------------------------
|
||||
A: NO.
|
||||
|
||||
The BTF_ID macro does not cause a function to become part of the ABI
|
||||
any more than does the EXPORT_SYMBOL_GPL macro.
|
||||
|
@ -1496,7 +1496,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.prog = prog;
|
||||
|
||||
ctx.offset = kcalloc(prog->len + 1, sizeof(int), GFP_KERNEL);
|
||||
ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL);
|
||||
if (ctx.offset == NULL) {
|
||||
prog = orig_prog;
|
||||
goto out_off;
|
||||
@ -1601,7 +1601,7 @@ skip_init_ctx:
|
||||
ctx.offset[i] *= AARCH64_INSN_SIZE;
|
||||
bpf_prog_fill_jited_linfo(prog, ctx.offset + 1);
|
||||
out_off:
|
||||
kfree(ctx.offset);
|
||||
kvfree(ctx.offset);
|
||||
kfree(jit_data);
|
||||
prog->aux->jit_data = NULL;
|
||||
}
|
||||
@ -1643,7 +1643,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
|
||||
int args_off, int retval_off, int run_ctx_off,
|
||||
bool save_ret)
|
||||
{
|
||||
u32 *branch;
|
||||
__le32 *branch;
|
||||
u64 enter_prog;
|
||||
u64 exit_prog;
|
||||
struct bpf_prog *p = l->link.prog;
|
||||
@ -1698,7 +1698,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
|
||||
|
||||
if (ctx->image) {
|
||||
int offset = &ctx->image[ctx->idx] - branch;
|
||||
*branch = A64_CBZ(1, A64_R(0), offset);
|
||||
*branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset));
|
||||
}
|
||||
|
||||
/* arg1: prog */
|
||||
@ -1713,7 +1713,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
|
||||
|
||||
static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl,
|
||||
int args_off, int retval_off, int run_ctx_off,
|
||||
u32 **branches)
|
||||
__le32 **branches)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -1784,7 +1784,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];
|
||||
struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
|
||||
bool save_ret;
|
||||
u32 **branches = NULL;
|
||||
__le32 **branches = NULL;
|
||||
|
||||
/* trampoline stack layout:
|
||||
* [ parent ip ]
|
||||
@ -1892,7 +1892,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
flags & BPF_TRAMP_F_RET_FENTRY_RET);
|
||||
|
||||
if (fmod_ret->nr_links) {
|
||||
branches = kcalloc(fmod_ret->nr_links, sizeof(u32 *),
|
||||
branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *),
|
||||
GFP_KERNEL);
|
||||
if (!branches)
|
||||
return -ENOMEM;
|
||||
@ -1916,7 +1916,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
/* update the branches saved in invoke_bpf_mod_ret with cbnz */
|
||||
for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) {
|
||||
int offset = &ctx->image[ctx->idx] - branches[i];
|
||||
*branches[i] = A64_CBNZ(1, A64_R(10), offset);
|
||||
*branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset));
|
||||
}
|
||||
|
||||
for (i = 0; i < fexit->nr_links; i++)
|
||||
|
@ -49,7 +49,9 @@ static inline void bpfptr_add(bpfptr_t *bpfptr, size_t val)
|
||||
static inline int copy_from_bpfptr_offset(void *dst, bpfptr_t src,
|
||||
size_t offset, size_t size)
|
||||
{
|
||||
return copy_from_sockptr_offset(dst, (sockptr_t) src, offset, size);
|
||||
if (!bpfptr_is_kernel(src))
|
||||
return copy_from_user(dst, src.user + offset, size);
|
||||
return copy_from_kernel_nofault(dst, src.kernel + offset, size);
|
||||
}
|
||||
|
||||
static inline int copy_from_bpfptr(void *dst, bpfptr_t src, size_t size)
|
||||
@ -78,7 +80,9 @@ static inline void *kvmemdup_bpfptr(bpfptr_t src, size_t len)
|
||||
|
||||
static inline long strncpy_from_bpfptr(char *dst, bpfptr_t src, size_t count)
|
||||
{
|
||||
return strncpy_from_sockptr(dst, (sockptr_t) src, count);
|
||||
if (bpfptr_is_kernel(src))
|
||||
return strncpy_from_kernel_nofault(dst, src.kernel, count);
|
||||
return strncpy_from_user(dst, src.user, count);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_BPFPTR_H */
|
||||
|
@ -291,4 +291,8 @@ struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk);
|
||||
static inline struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk) { return NULL; }
|
||||
#endif
|
||||
|
||||
#if !IS_ENABLED(CONFIG_MPTCP)
|
||||
struct mptcp_sock { };
|
||||
#endif
|
||||
|
||||
#endif /* __NET_MPTCP_H */
|
||||
|
@ -649,6 +649,11 @@ static int bpf_iter_init_array_map(void *priv_data,
|
||||
seq_info->percpu_value_buf = value_buf;
|
||||
}
|
||||
|
||||
/* bpf_iter_attach_map() acquires a map uref, and the uref may be
|
||||
* released before or in the middle of iterating map elements, so
|
||||
* acquire an extra map uref for iterator.
|
||||
*/
|
||||
bpf_map_inc_with_uref(map);
|
||||
seq_info->map = map;
|
||||
return 0;
|
||||
}
|
||||
@ -657,6 +662,7 @@ static void bpf_iter_fini_array_map(void *priv_data)
|
||||
{
|
||||
struct bpf_iter_seq_array_map_info *seq_info = priv_data;
|
||||
|
||||
bpf_map_put_with_uref(seq_info->map);
|
||||
kfree(seq_info->percpu_value_buf);
|
||||
}
|
||||
|
||||
|
@ -68,13 +68,18 @@ static void bpf_iter_done_stop(struct seq_file *seq)
|
||||
iter_priv->done_stop = true;
|
||||
}
|
||||
|
||||
static inline bool bpf_iter_target_support_resched(const struct bpf_iter_target_info *tinfo)
|
||||
{
|
||||
return tinfo->reg_info->feature & BPF_ITER_RESCHED;
|
||||
}
|
||||
|
||||
static bool bpf_iter_support_resched(struct seq_file *seq)
|
||||
{
|
||||
struct bpf_iter_priv_data *iter_priv;
|
||||
|
||||
iter_priv = container_of(seq->private, struct bpf_iter_priv_data,
|
||||
target_private);
|
||||
return iter_priv->tinfo->reg_info->feature & BPF_ITER_RESCHED;
|
||||
return bpf_iter_target_support_resched(iter_priv->tinfo);
|
||||
}
|
||||
|
||||
/* maximum visited objects before bailing out */
|
||||
@ -537,6 +542,10 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
|
||||
if (!tinfo)
|
||||
return -ENOENT;
|
||||
|
||||
/* Only allow sleepable program for resched-able iterator */
|
||||
if (prog->aux->sleepable && !bpf_iter_target_support_resched(tinfo))
|
||||
return -EINVAL;
|
||||
|
||||
link = kzalloc(sizeof(*link), GFP_USER | __GFP_NOWARN);
|
||||
if (!link)
|
||||
return -ENOMEM;
|
||||
|
@ -311,12 +311,8 @@ static struct htab_elem *prealloc_lru_pop(struct bpf_htab *htab, void *key,
|
||||
struct htab_elem *l;
|
||||
|
||||
if (node) {
|
||||
u32 key_size = htab->map.key_size;
|
||||
|
||||
l = container_of(node, struct htab_elem, lru_node);
|
||||
memcpy(l->key, key, key_size);
|
||||
check_and_init_map_value(&htab->map,
|
||||
l->key + round_up(key_size, 8));
|
||||
memcpy(l->key, key, htab->map.key_size);
|
||||
return l;
|
||||
}
|
||||
|
||||
@ -2064,6 +2060,7 @@ static int bpf_iter_init_hash_map(void *priv_data,
|
||||
seq_info->percpu_value_buf = value_buf;
|
||||
}
|
||||
|
||||
bpf_map_inc_with_uref(map);
|
||||
seq_info->map = map;
|
||||
seq_info->htab = container_of(map, struct bpf_htab, map);
|
||||
return 0;
|
||||
@ -2073,6 +2070,7 @@ static void bpf_iter_fini_hash_map(void *priv_data)
|
||||
{
|
||||
struct bpf_iter_seq_hash_map_info *seq_info = priv_data;
|
||||
|
||||
bpf_map_put_with_uref(seq_info->map);
|
||||
kfree(seq_info->percpu_value_buf);
|
||||
}
|
||||
|
||||
|
@ -3886,6 +3886,7 @@ static int bpf_prog_get_info_by_fd(struct file *file,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
struct bpf_prog_info __user *uinfo = u64_to_user_ptr(attr->info.info);
|
||||
struct btf *attach_btf = bpf_prog_get_target_btf(prog);
|
||||
struct bpf_prog_info info;
|
||||
u32 info_len = attr->info.info_len;
|
||||
struct bpf_prog_kstats stats;
|
||||
@ -4088,10 +4089,8 @@ static int bpf_prog_get_info_by_fd(struct file *file,
|
||||
if (prog->aux->btf)
|
||||
info.btf_id = btf_obj_id(prog->aux->btf);
|
||||
info.attach_btf_id = prog->aux->attach_btf_id;
|
||||
if (prog->aux->attach_btf)
|
||||
info.attach_btf_obj_id = btf_obj_id(prog->aux->attach_btf);
|
||||
else if (prog->aux->dst_prog)
|
||||
info.attach_btf_obj_id = btf_obj_id(prog->aux->dst_prog->aux->attach_btf);
|
||||
if (attach_btf)
|
||||
info.attach_btf_obj_id = btf_obj_id(attach_btf);
|
||||
|
||||
ulen = info.nr_func_info;
|
||||
info.nr_func_info = prog->aux->func_info_cnt;
|
||||
@ -5072,9 +5071,6 @@ static bool syscall_prog_is_valid_access(int off, int size,
|
||||
|
||||
BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size)
|
||||
{
|
||||
struct bpf_prog * __maybe_unused prog;
|
||||
struct bpf_tramp_run_ctx __maybe_unused run_ctx;
|
||||
|
||||
switch (cmd) {
|
||||
case BPF_MAP_CREATE:
|
||||
case BPF_MAP_UPDATE_ELEM:
|
||||
@ -5084,6 +5080,18 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size)
|
||||
case BPF_LINK_CREATE:
|
||||
case BPF_RAW_TRACEPOINT_OPEN:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size);
|
||||
}
|
||||
|
||||
int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size)
|
||||
{
|
||||
struct bpf_prog * __maybe_unused prog;
|
||||
struct bpf_tramp_run_ctx __maybe_unused run_ctx;
|
||||
|
||||
switch (cmd) {
|
||||
#ifdef CONFIG_BPF_JIT /* __bpf_prog_enter_sleepable used by trampoline and JIT */
|
||||
case BPF_PROG_TEST_RUN:
|
||||
if (attr->test.data_in || attr->test.data_out ||
|
||||
@ -5114,11 +5122,10 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size)
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
return -EINVAL;
|
||||
return ____bpf_sys_bpf(cmd, attr, size);
|
||||
}
|
||||
return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size);
|
||||
}
|
||||
EXPORT_SYMBOL(bpf_sys_bpf);
|
||||
EXPORT_SYMBOL(kern_sys_bpf);
|
||||
|
||||
static const struct bpf_func_proto bpf_sys_bpf_proto = {
|
||||
.func = bpf_sys_bpf,
|
||||
|
@ -841,7 +841,10 @@ void bpf_trampoline_put(struct bpf_trampoline *tr)
|
||||
* multiple rcu callbacks.
|
||||
*/
|
||||
hlist_del(&tr->hlist);
|
||||
kfree(tr->fops);
|
||||
if (tr->fops) {
|
||||
ftrace_free_filter(tr->fops);
|
||||
kfree(tr->fops);
|
||||
}
|
||||
kfree(tr);
|
||||
out:
|
||||
mutex_unlock(&trampoline_mutex);
|
||||
|
@ -1628,6 +1628,7 @@ static int __init bpf_prog_test_run_init(void)
|
||||
int ret;
|
||||
|
||||
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set);
|
||||
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_prog_test_kfunc_set);
|
||||
return ret ?: register_btf_id_dtor_kfuncs(bpf_prog_test_dtor_kfunc,
|
||||
ARRAY_SIZE(bpf_prog_test_dtor_kfunc),
|
||||
THIS_MODULE);
|
||||
|
@ -875,10 +875,18 @@ static int bpf_iter_init_sk_storage_map(void *priv_data,
|
||||
{
|
||||
struct bpf_iter_seq_sk_storage_map_info *seq_info = priv_data;
|
||||
|
||||
bpf_map_inc_with_uref(aux->map);
|
||||
seq_info->map = aux->map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bpf_iter_fini_sk_storage_map(void *priv_data)
|
||||
{
|
||||
struct bpf_iter_seq_sk_storage_map_info *seq_info = priv_data;
|
||||
|
||||
bpf_map_put_with_uref(seq_info->map);
|
||||
}
|
||||
|
||||
static int bpf_iter_attach_map(struct bpf_prog *prog,
|
||||
union bpf_iter_link_info *linfo,
|
||||
struct bpf_iter_aux_info *aux)
|
||||
@ -896,7 +904,7 @@ static int bpf_iter_attach_map(struct bpf_prog *prog,
|
||||
if (map->map_type != BPF_MAP_TYPE_SK_STORAGE)
|
||||
goto put_map;
|
||||
|
||||
if (prog->aux->max_rdonly_access > map->value_size) {
|
||||
if (prog->aux->max_rdwr_access > map->value_size) {
|
||||
err = -EACCES;
|
||||
goto put_map;
|
||||
}
|
||||
@ -924,7 +932,7 @@ static const struct seq_operations bpf_sk_storage_map_seq_ops = {
|
||||
static const struct bpf_iter_seq_info iter_seq_info = {
|
||||
.seq_ops = &bpf_sk_storage_map_seq_ops,
|
||||
.init_seq_private = bpf_iter_init_sk_storage_map,
|
||||
.fini_seq_private = NULL,
|
||||
.fini_seq_private = bpf_iter_fini_sk_storage_map,
|
||||
.seq_priv_size = sizeof(struct bpf_iter_seq_sk_storage_map_info),
|
||||
};
|
||||
|
||||
|
@ -783,13 +783,22 @@ static int sock_map_init_seq_private(void *priv_data,
|
||||
{
|
||||
struct sock_map_seq_info *info = priv_data;
|
||||
|
||||
bpf_map_inc_with_uref(aux->map);
|
||||
info->map = aux->map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sock_map_fini_seq_private(void *priv_data)
|
||||
{
|
||||
struct sock_map_seq_info *info = priv_data;
|
||||
|
||||
bpf_map_put_with_uref(info->map);
|
||||
}
|
||||
|
||||
static const struct bpf_iter_seq_info sock_map_iter_seq_info = {
|
||||
.seq_ops = &sock_map_seq_ops,
|
||||
.init_seq_private = sock_map_init_seq_private,
|
||||
.fini_seq_private = sock_map_fini_seq_private,
|
||||
.seq_priv_size = sizeof(struct sock_map_seq_info),
|
||||
};
|
||||
|
||||
@ -1369,18 +1378,27 @@ static const struct seq_operations sock_hash_seq_ops = {
|
||||
};
|
||||
|
||||
static int sock_hash_init_seq_private(void *priv_data,
|
||||
struct bpf_iter_aux_info *aux)
|
||||
struct bpf_iter_aux_info *aux)
|
||||
{
|
||||
struct sock_hash_seq_info *info = priv_data;
|
||||
|
||||
bpf_map_inc_with_uref(aux->map);
|
||||
info->map = aux->map;
|
||||
info->htab = container_of(aux->map, struct bpf_shtab, map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sock_hash_fini_seq_private(void *priv_data)
|
||||
{
|
||||
struct sock_hash_seq_info *info = priv_data;
|
||||
|
||||
bpf_map_put_with_uref(info->map);
|
||||
}
|
||||
|
||||
static const struct bpf_iter_seq_info sock_hash_iter_seq_info = {
|
||||
.seq_ops = &sock_hash_seq_ops,
|
||||
.init_seq_private = sock_hash_init_seq_private,
|
||||
.fini_seq_private = sock_hash_fini_seq_private,
|
||||
.seq_priv_size = sizeof(struct sock_hash_seq_info),
|
||||
};
|
||||
|
||||
|
@ -66,13 +66,13 @@ struct bpf_load_and_run_opts {
|
||||
const char *errstr;
|
||||
};
|
||||
|
||||
long bpf_sys_bpf(__u32 cmd, void *attr, __u32 attr_size);
|
||||
long kern_sys_bpf(__u32 cmd, void *attr, __u32 attr_size);
|
||||
|
||||
static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
||||
unsigned int size)
|
||||
{
|
||||
#ifdef __KERNEL__
|
||||
return bpf_sys_bpf(cmd, attr, size);
|
||||
return kern_sys_bpf(cmd, attr, size);
|
||||
#else
|
||||
return syscall(__NR_bpf, cmd, attr, size);
|
||||
#endif
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "bpf_iter_test_kern6.skel.h"
|
||||
#include "bpf_iter_bpf_link.skel.h"
|
||||
#include "bpf_iter_ksym.skel.h"
|
||||
#include "bpf_iter_sockmap.skel.h"
|
||||
|
||||
static int duration;
|
||||
|
||||
@ -67,6 +68,50 @@ free_link:
|
||||
bpf_link__destroy(link);
|
||||
}
|
||||
|
||||
static void do_read_map_iter_fd(struct bpf_object_skeleton **skel, struct bpf_program *prog,
|
||||
struct bpf_map *map)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
|
||||
union bpf_iter_link_info linfo;
|
||||
struct bpf_link *link;
|
||||
char buf[16] = {};
|
||||
int iter_fd, len;
|
||||
|
||||
memset(&linfo, 0, sizeof(linfo));
|
||||
linfo.map.map_fd = bpf_map__fd(map);
|
||||
opts.link_info = &linfo;
|
||||
opts.link_info_len = sizeof(linfo);
|
||||
link = bpf_program__attach_iter(prog, &opts);
|
||||
if (!ASSERT_OK_PTR(link, "attach_map_iter"))
|
||||
return;
|
||||
|
||||
iter_fd = bpf_iter_create(bpf_link__fd(link));
|
||||
if (!ASSERT_GE(iter_fd, 0, "create_map_iter")) {
|
||||
bpf_link__destroy(link);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Close link and map fd prematurely */
|
||||
bpf_link__destroy(link);
|
||||
bpf_object__destroy_skeleton(*skel);
|
||||
*skel = NULL;
|
||||
|
||||
/* Try to let map free work to run first if map is freed */
|
||||
usleep(100);
|
||||
/* Memory used by both sock map and sock local storage map are
|
||||
* freed after two synchronize_rcu() calls, so wait for it
|
||||
*/
|
||||
kern_sync_rcu();
|
||||
kern_sync_rcu();
|
||||
|
||||
/* Read after both map fd and link fd are closed */
|
||||
while ((len = read(iter_fd, buf, sizeof(buf))) > 0)
|
||||
;
|
||||
ASSERT_GE(len, 0, "read_iterator");
|
||||
|
||||
close(iter_fd);
|
||||
}
|
||||
|
||||
static int read_fd_into_buffer(int fd, char *buf, int size)
|
||||
{
|
||||
int bufleft = size;
|
||||
@ -634,6 +679,12 @@ static void test_bpf_hash_map(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Sleepable program is prohibited for hash map iterator */
|
||||
linfo.map.map_fd = map_fd;
|
||||
link = bpf_program__attach_iter(skel->progs.sleepable_dummy_dump, &opts);
|
||||
if (!ASSERT_ERR_PTR(link, "attach_sleepable_prog_to_iter"))
|
||||
goto out;
|
||||
|
||||
linfo.map.map_fd = map_fd;
|
||||
link = bpf_program__attach_iter(skel->progs.dump_bpf_hash_map, &opts);
|
||||
if (!ASSERT_OK_PTR(link, "attach_iter"))
|
||||
@ -827,6 +878,20 @@ out:
|
||||
bpf_iter_bpf_array_map__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_bpf_array_map_iter_fd(void)
|
||||
{
|
||||
struct bpf_iter_bpf_array_map *skel;
|
||||
|
||||
skel = bpf_iter_bpf_array_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_array_map__open_and_load"))
|
||||
return;
|
||||
|
||||
do_read_map_iter_fd(&skel->skeleton, skel->progs.dump_bpf_array_map,
|
||||
skel->maps.arraymap1);
|
||||
|
||||
bpf_iter_bpf_array_map__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_bpf_percpu_array_map(void)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
|
||||
@ -1009,6 +1074,20 @@ out:
|
||||
bpf_iter_bpf_sk_storage_helpers__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_bpf_sk_stoarge_map_iter_fd(void)
|
||||
{
|
||||
struct bpf_iter_bpf_sk_storage_map *skel;
|
||||
|
||||
skel = bpf_iter_bpf_sk_storage_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_sk_storage_map__open_and_load"))
|
||||
return;
|
||||
|
||||
do_read_map_iter_fd(&skel->skeleton, skel->progs.rw_bpf_sk_storage_map,
|
||||
skel->maps.sk_stg_map);
|
||||
|
||||
bpf_iter_bpf_sk_storage_map__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_bpf_sk_storage_map(void)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
|
||||
@ -1044,7 +1123,15 @@ static void test_bpf_sk_storage_map(void)
|
||||
linfo.map.map_fd = map_fd;
|
||||
opts.link_info = &linfo;
|
||||
opts.link_info_len = sizeof(linfo);
|
||||
link = bpf_program__attach_iter(skel->progs.dump_bpf_sk_storage_map, &opts);
|
||||
link = bpf_program__attach_iter(skel->progs.oob_write_bpf_sk_storage_map, &opts);
|
||||
err = libbpf_get_error(link);
|
||||
if (!ASSERT_EQ(err, -EACCES, "attach_oob_write_iter")) {
|
||||
if (!err)
|
||||
bpf_link__destroy(link);
|
||||
goto out;
|
||||
}
|
||||
|
||||
link = bpf_program__attach_iter(skel->progs.rw_bpf_sk_storage_map, &opts);
|
||||
if (!ASSERT_OK_PTR(link, "attach_iter"))
|
||||
goto out;
|
||||
|
||||
@ -1052,6 +1139,7 @@ static void test_bpf_sk_storage_map(void)
|
||||
if (!ASSERT_GE(iter_fd, 0, "create_iter"))
|
||||
goto free_link;
|
||||
|
||||
skel->bss->to_add_val = time(NULL);
|
||||
/* do some tests */
|
||||
while ((len = read(iter_fd, buf, sizeof(buf))) > 0)
|
||||
;
|
||||
@ -1065,6 +1153,13 @@ static void test_bpf_sk_storage_map(void)
|
||||
if (!ASSERT_EQ(skel->bss->val_sum, expected_val, "val_sum"))
|
||||
goto close_iter;
|
||||
|
||||
for (i = 0; i < num_sockets; i++) {
|
||||
err = bpf_map_lookup_elem(map_fd, &sock_fd[i], &val);
|
||||
if (!ASSERT_OK(err, "map_lookup") ||
|
||||
!ASSERT_EQ(val, i + 1 + skel->bss->to_add_val, "check_map_value"))
|
||||
break;
|
||||
}
|
||||
|
||||
close_iter:
|
||||
close(iter_fd);
|
||||
free_link:
|
||||
@ -1217,6 +1312,19 @@ out:
|
||||
bpf_iter_task_vma__destroy(skel);
|
||||
}
|
||||
|
||||
void test_bpf_sockmap_map_iter_fd(void)
|
||||
{
|
||||
struct bpf_iter_sockmap *skel;
|
||||
|
||||
skel = bpf_iter_sockmap__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "bpf_iter_sockmap__open_and_load"))
|
||||
return;
|
||||
|
||||
do_read_map_iter_fd(&skel->skeleton, skel->progs.copy, skel->maps.sockmap);
|
||||
|
||||
bpf_iter_sockmap__destroy(skel);
|
||||
}
|
||||
|
||||
void test_bpf_iter(void)
|
||||
{
|
||||
if (test__start_subtest("btf_id_or_null"))
|
||||
@ -1267,10 +1375,14 @@ void test_bpf_iter(void)
|
||||
test_bpf_percpu_hash_map();
|
||||
if (test__start_subtest("bpf_array_map"))
|
||||
test_bpf_array_map();
|
||||
if (test__start_subtest("bpf_array_map_iter_fd"))
|
||||
test_bpf_array_map_iter_fd();
|
||||
if (test__start_subtest("bpf_percpu_array_map"))
|
||||
test_bpf_percpu_array_map();
|
||||
if (test__start_subtest("bpf_sk_storage_map"))
|
||||
test_bpf_sk_storage_map();
|
||||
if (test__start_subtest("bpf_sk_storage_map_iter_fd"))
|
||||
test_bpf_sk_stoarge_map_iter_fd();
|
||||
if (test__start_subtest("bpf_sk_storage_delete"))
|
||||
test_bpf_sk_storage_delete();
|
||||
if (test__start_subtest("bpf_sk_storage_get"))
|
||||
@ -1283,4 +1395,6 @@ void test_bpf_iter(void)
|
||||
test_link_iter();
|
||||
if (test__start_subtest("ksym"))
|
||||
test_ksym_iter();
|
||||
if (test__start_subtest("bpf_sockmap_map_iter_fd"))
|
||||
test_bpf_sockmap_map_iter_fd();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
#include <bpf/btf.h>
|
||||
#include "bind4_prog.skel.h"
|
||||
|
||||
typedef int (*test_cb)(struct bpf_object *obj);
|
||||
|
||||
@ -407,6 +408,98 @@ static void test_func_replace_global_func(void)
|
||||
prog_name, false, NULL);
|
||||
}
|
||||
|
||||
static int find_prog_btf_id(const char *name, __u32 attach_prog_fd)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 info_len = sizeof(info);
|
||||
struct btf *btf;
|
||||
int ret;
|
||||
|
||||
ret = bpf_obj_get_info_by_fd(attach_prog_fd, &info, &info_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!info.btf_id)
|
||||
return -EINVAL;
|
||||
|
||||
btf = btf__load_from_kernel_by_id(info.btf_id);
|
||||
ret = libbpf_get_error(btf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
|
||||
btf__free(btf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int load_fentry(int attach_prog_fd, int attach_btf_id)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_prog_load_opts, opts,
|
||||
.expected_attach_type = BPF_TRACE_FENTRY,
|
||||
.attach_prog_fd = attach_prog_fd,
|
||||
.attach_btf_id = attach_btf_id,
|
||||
);
|
||||
struct bpf_insn insns[] = {
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
|
||||
return bpf_prog_load(BPF_PROG_TYPE_TRACING,
|
||||
"bind4_fentry",
|
||||
"GPL",
|
||||
insns,
|
||||
ARRAY_SIZE(insns),
|
||||
&opts);
|
||||
}
|
||||
|
||||
static void test_fentry_to_cgroup_bpf(void)
|
||||
{
|
||||
struct bind4_prog *skel = NULL;
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 info_len = sizeof(info);
|
||||
int cgroup_fd = -1;
|
||||
int fentry_fd = -1;
|
||||
int btf_id;
|
||||
|
||||
cgroup_fd = test__join_cgroup("/fentry_to_cgroup_bpf");
|
||||
if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd"))
|
||||
return;
|
||||
|
||||
skel = bind4_prog__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel"))
|
||||
goto cleanup;
|
||||
|
||||
skel->links.bind_v4_prog = bpf_program__attach_cgroup(skel->progs.bind_v4_prog, cgroup_fd);
|
||||
if (!ASSERT_OK_PTR(skel->links.bind_v4_prog, "bpf_program__attach_cgroup"))
|
||||
goto cleanup;
|
||||
|
||||
btf_id = find_prog_btf_id("bind_v4_prog", bpf_program__fd(skel->progs.bind_v4_prog));
|
||||
if (!ASSERT_GE(btf_id, 0, "find_prog_btf_id"))
|
||||
goto cleanup;
|
||||
|
||||
fentry_fd = load_fentry(bpf_program__fd(skel->progs.bind_v4_prog), btf_id);
|
||||
if (!ASSERT_GE(fentry_fd, 0, "load_fentry"))
|
||||
goto cleanup;
|
||||
|
||||
/* Make sure bpf_obj_get_info_by_fd works correctly when attaching
|
||||
* to another BPF program.
|
||||
*/
|
||||
|
||||
ASSERT_OK(bpf_obj_get_info_by_fd(fentry_fd, &info, &info_len),
|
||||
"bpf_obj_get_info_by_fd");
|
||||
|
||||
ASSERT_EQ(info.btf_id, 0, "info.btf_id");
|
||||
ASSERT_EQ(info.attach_btf_id, btf_id, "info.attach_btf_id");
|
||||
ASSERT_GT(info.attach_btf_obj_id, 0, "info.attach_btf_obj_id");
|
||||
|
||||
cleanup:
|
||||
if (cgroup_fd >= 0)
|
||||
close(cgroup_fd);
|
||||
if (fentry_fd >= 0)
|
||||
close(fentry_fd);
|
||||
bind4_prog__destroy(skel);
|
||||
}
|
||||
|
||||
/* NOTE: affect other tests, must run in serial mode */
|
||||
void serial_test_fexit_bpf2bpf(void)
|
||||
{
|
||||
@ -430,4 +523,6 @@ void serial_test_fexit_bpf2bpf(void)
|
||||
test_fmod_ret_freplace();
|
||||
if (test__start_subtest("func_replace_global_func"))
|
||||
test_func_replace_global_func();
|
||||
if (test__start_subtest("fentry_to_cgroup_bpf"))
|
||||
test_fentry_to_cgroup_bpf();
|
||||
}
|
||||
|
21
tools/testing/selftests/bpf/prog_tests/lru_bug.c
Normal file
21
tools/testing/selftests/bpf/prog_tests/lru_bug.c
Normal file
@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
#include "lru_bug.skel.h"
|
||||
|
||||
void test_lru_bug(void)
|
||||
{
|
||||
struct lru_bug *skel;
|
||||
int ret;
|
||||
|
||||
skel = lru_bug__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "lru_bug__open_and_load"))
|
||||
return;
|
||||
ret = lru_bug__attach(skel);
|
||||
if (!ASSERT_OK(ret, "lru_bug__attach"))
|
||||
goto end;
|
||||
usleep(1);
|
||||
ASSERT_OK(skel->data->result, "prealloc_lru_pop doesn't call check_and_init_map_value");
|
||||
end:
|
||||
lru_bug__destroy(skel);
|
||||
}
|
@ -112,3 +112,12 @@ int dump_bpf_hash_map(struct bpf_iter__bpf_map_elem *ctx)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("iter.s/bpf_map_elem")
|
||||
int sleepable_dummy_dump(struct bpf_iter__bpf_map_elem *ctx)
|
||||
{
|
||||
if (ctx->meta->seq_num == 0)
|
||||
BPF_SEQ_PRINTF(ctx->meta->seq, "map dump starts\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -16,19 +16,37 @@ struct {
|
||||
|
||||
__u32 val_sum = 0;
|
||||
__u32 ipv6_sk_count = 0;
|
||||
__u32 to_add_val = 0;
|
||||
|
||||
SEC("iter/bpf_sk_storage_map")
|
||||
int dump_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx)
|
||||
int rw_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx)
|
||||
{
|
||||
struct sock *sk = ctx->sk;
|
||||
__u32 *val = ctx->value;
|
||||
|
||||
if (sk == (void *)0 || val == (void *)0)
|
||||
if (sk == NULL || val == NULL)
|
||||
return 0;
|
||||
|
||||
if (sk->sk_family == AF_INET6)
|
||||
ipv6_sk_count++;
|
||||
|
||||
val_sum += *val;
|
||||
|
||||
*val += to_add_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("iter/bpf_sk_storage_map")
|
||||
int oob_write_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx)
|
||||
{
|
||||
struct sock *sk = ctx->sk;
|
||||
__u32 *val = ctx->value;
|
||||
|
||||
if (sk == NULL || val == NULL)
|
||||
return 0;
|
||||
|
||||
*(val + 1) = 0xdeadbeef;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
49
tools/testing/selftests/bpf/progs/lru_bug.c
Normal file
49
tools/testing/selftests/bpf/progs/lru_bug.c
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
struct map_value {
|
||||
struct task_struct __kptr *ptr;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, int);
|
||||
__type(value, struct map_value);
|
||||
} lru_map SEC(".maps");
|
||||
|
||||
int pid = 0;
|
||||
int result = 1;
|
||||
|
||||
SEC("fentry/bpf_ktime_get_ns")
|
||||
int printk(void *ctx)
|
||||
{
|
||||
struct map_value v = {};
|
||||
|
||||
if (pid == bpf_get_current_task_btf()->pid)
|
||||
bpf_map_update_elem(&lru_map, &(int){0}, &v, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fentry/do_nanosleep")
|
||||
int nanosleep(void *ctx)
|
||||
{
|
||||
struct map_value val = {}, *v;
|
||||
struct task_struct *current;
|
||||
|
||||
bpf_map_update_elem(&lru_map, &(int){0}, &val, 0);
|
||||
v = bpf_map_lookup_elem(&lru_map, &(int){0});
|
||||
if (!v)
|
||||
return 0;
|
||||
bpf_map_delete_elem(&lru_map, &(int){0});
|
||||
current = bpf_get_current_task_btf();
|
||||
v->ptr = current;
|
||||
pid = current->pid;
|
||||
bpf_ktime_get_ns();
|
||||
result = !v->ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
Loading…
Reference in New Issue
Block a user