提交 7b16509b 编写于 作者: D David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next

Alexei Starovoitov says:

====================
pull-request: bpf-next 2021-05-19

The following pull-request contains BPF updates for your *net-next* tree.

We've added 43 non-merge commits during the last 11 day(s) which contain
a total of 74 files changed, 3717 insertions(+), 578 deletions(-).

The main changes are:

1) syscall program type, fd array, and light skeleton, from Alexei.

2) Stop emitting static variables in skeleton, from Andrii.

3) Low level tc-bpf api, from Kumar.

4) Reduce verifier kmalloc/kfree churn, from Lorenz.
====================
...@@ -178,9 +178,6 @@ static bool is_addsub_imm(u32 imm) ...@@ -178,9 +178,6 @@ static bool is_addsub_imm(u32 imm)
return !(imm & ~0xfff) || !(imm & ~0xfff000); return !(imm & ~0xfff) || !(imm & ~0xfff000);
} }
/* Stack must be multiples of 16B */
#define STACK_ALIGN(sz) (((sz) + 15) & ~15)
/* Tail call offset to jump into */ /* Tail call offset to jump into */
#if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) #if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)
#define PROLOGUE_OFFSET 8 #define PROLOGUE_OFFSET 8
...@@ -255,7 +252,8 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf) ...@@ -255,7 +252,8 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
emit(A64_BTI_J, ctx); emit(A64_BTI_J, ctx);
} }
ctx->stack_size = STACK_ALIGN(prog->aux->stack_depth); /* Stack must be multiples of 16B */
ctx->stack_size = round_up(prog->aux->stack_depth, 16);
/* Set up function call stack */ /* Set up function call stack */
emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx);
...@@ -487,17 +485,12 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, ...@@ -487,17 +485,12 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
break; break;
case BPF_ALU | BPF_DIV | BPF_X: case BPF_ALU | BPF_DIV | BPF_X:
case BPF_ALU64 | BPF_DIV | BPF_X: case BPF_ALU64 | BPF_DIV | BPF_X:
emit(A64_UDIV(is64, dst, dst, src), ctx);
break;
case BPF_ALU | BPF_MOD | BPF_X: case BPF_ALU | BPF_MOD | BPF_X:
case BPF_ALU64 | BPF_MOD | BPF_X: case BPF_ALU64 | BPF_MOD | BPF_X:
switch (BPF_OP(code)) { emit(A64_UDIV(is64, tmp, dst, src), ctx);
case BPF_DIV: emit(A64_MSUB(is64, dst, dst, tmp, src), ctx);
emit(A64_UDIV(is64, dst, dst, src), ctx);
break;
case BPF_MOD:
emit(A64_UDIV(is64, tmp, dst, src), ctx);
emit(A64_MSUB(is64, dst, dst, tmp, src), ctx);
break;
}
break; break;
case BPF_ALU | BPF_LSH | BPF_X: case BPF_ALU | BPF_LSH | BPF_X:
case BPF_ALU64 | BPF_LSH | BPF_X: case BPF_ALU64 | BPF_LSH | BPF_X:
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/percpu-refcount.h> #include <linux/percpu-refcount.h>
#include <linux/bpfptr.h>
struct bpf_verifier_env; struct bpf_verifier_env;
struct bpf_verifier_log; struct bpf_verifier_log;
...@@ -1428,7 +1429,7 @@ struct bpf_iter__bpf_map_elem { ...@@ -1428,7 +1429,7 @@ struct bpf_iter__bpf_map_elem {
int bpf_iter_reg_target(const struct bpf_iter_reg *reg_info); int bpf_iter_reg_target(const struct bpf_iter_reg *reg_info);
void bpf_iter_unreg_target(const struct bpf_iter_reg *reg_info); void bpf_iter_unreg_target(const struct bpf_iter_reg *reg_info);
bool bpf_iter_prog_supported(struct bpf_prog *prog); bool bpf_iter_prog_supported(struct bpf_prog *prog);
int bpf_iter_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_prog *prog);
int bpf_iter_new_fd(struct bpf_link *link); int bpf_iter_new_fd(struct bpf_link *link);
bool bpf_link_is_iter(struct bpf_link *link); bool bpf_link_is_iter(struct bpf_link *link);
struct bpf_prog *bpf_iter_get_info(struct bpf_iter_meta *meta, bool in_stop); struct bpf_prog *bpf_iter_get_info(struct bpf_iter_meta *meta, bool in_stop);
...@@ -1459,7 +1460,7 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, ...@@ -1459,7 +1460,7 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value); int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value);
int bpf_get_file_flag(int flags); int bpf_get_file_flag(int flags);
int bpf_check_uarg_tail_zero(void __user *uaddr, size_t expected_size, int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
size_t actual_size); size_t actual_size);
/* memcpy that is used with 8-byte aligned pointers, power-of-8 size and /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
...@@ -1479,8 +1480,7 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size) ...@@ -1479,8 +1480,7 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size)
} }
/* verify correctness of eBPF program */ /* verify correctness of eBPF program */
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr);
union bpf_attr __user *uattr);
#ifndef CONFIG_BPF_JIT_ALWAYS_ON #ifndef CONFIG_BPF_JIT_ALWAYS_ON
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth); void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
...@@ -1826,6 +1826,9 @@ static inline bool bpf_map_is_dev_bound(struct bpf_map *map) ...@@ -1826,6 +1826,9 @@ static inline bool bpf_map_is_dev_bound(struct bpf_map *map)
struct bpf_map *bpf_map_offload_map_alloc(union bpf_attr *attr); struct bpf_map *bpf_map_offload_map_alloc(union bpf_attr *attr);
void bpf_map_offload_map_free(struct bpf_map *map); void bpf_map_offload_map_free(struct bpf_map *map);
int bpf_prog_test_run_syscall(struct bpf_prog *prog,
const union bpf_attr *kattr,
union bpf_attr __user *uattr);
#else #else
static inline int bpf_prog_offload_init(struct bpf_prog *prog, static inline int bpf_prog_offload_init(struct bpf_prog *prog,
union bpf_attr *attr) union bpf_attr *attr)
...@@ -1851,6 +1854,13 @@ static inline struct bpf_map *bpf_map_offload_map_alloc(union bpf_attr *attr) ...@@ -1851,6 +1854,13 @@ static inline struct bpf_map *bpf_map_offload_map_alloc(union bpf_attr *attr)
static inline void bpf_map_offload_map_free(struct bpf_map *map) static inline void bpf_map_offload_map_free(struct bpf_map *map)
{ {
} }
static inline int bpf_prog_test_run_syscall(struct bpf_prog *prog,
const union bpf_attr *kattr,
union bpf_attr __user *uattr)
{
return -ENOTSUPP;
}
#endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */ #endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */
#if defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL) #if defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL)
...@@ -1964,6 +1974,7 @@ extern const struct bpf_func_proto bpf_get_socket_ptr_cookie_proto; ...@@ -1964,6 +1974,7 @@ extern const struct bpf_func_proto bpf_get_socket_ptr_cookie_proto;
extern const struct bpf_func_proto bpf_task_storage_get_proto; extern const struct bpf_func_proto bpf_task_storage_get_proto;
extern const struct bpf_func_proto bpf_task_storage_delete_proto; extern const struct bpf_func_proto bpf_task_storage_delete_proto;
extern const struct bpf_func_proto bpf_for_each_map_elem_proto; extern const struct bpf_func_proto bpf_for_each_map_elem_proto;
extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto;
const struct bpf_func_proto *bpf_tracing_func_proto( const struct bpf_func_proto *bpf_tracing_func_proto(
enum bpf_func_id func_id, const struct bpf_prog *prog); enum bpf_func_id func_id, const struct bpf_prog *prog);
......
...@@ -77,6 +77,8 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm, ...@@ -77,6 +77,8 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
void *, void *) void *, void *)
#endif /* CONFIG_BPF_LSM */ #endif /* CONFIG_BPF_LSM */
#endif #endif
BPF_PROG_TYPE(BPF_PROG_TYPE_SYSCALL, bpf_syscall,
void *, void *)
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
......
...@@ -215,6 +215,13 @@ struct bpf_idx_pair { ...@@ -215,6 +215,13 @@ struct bpf_idx_pair {
u32 idx; u32 idx;
}; };
struct bpf_id_pair {
u32 old;
u32 cur;
};
/* Maximum number of register states that can exist at once */
#define BPF_ID_MAP_SIZE (MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE)
#define MAX_CALL_FRAMES 8 #define MAX_CALL_FRAMES 8
struct bpf_verifier_state { struct bpf_verifier_state {
/* call stack tracking */ /* call stack tracking */
...@@ -418,6 +425,7 @@ struct bpf_verifier_env { ...@@ -418,6 +425,7 @@ struct bpf_verifier_env {
const struct bpf_line_info *prev_linfo; const struct bpf_line_info *prev_linfo;
struct bpf_verifier_log log; struct bpf_verifier_log log;
struct bpf_subprog_info subprog_info[BPF_MAX_SUBPROGS + 1]; struct bpf_subprog_info subprog_info[BPF_MAX_SUBPROGS + 1];
struct bpf_id_pair idmap_scratch[BPF_ID_MAP_SIZE];
struct { struct {
int *insn_state; int *insn_state;
int *insn_stack; int *insn_stack;
...@@ -442,6 +450,7 @@ struct bpf_verifier_env { ...@@ -442,6 +450,7 @@ struct bpf_verifier_env {
u32 peak_states; u32 peak_states;
/* longest register parentage chain walked for liveness marking */ /* longest register parentage chain walked for liveness marking */
u32 longest_mark_read_walk; u32 longest_mark_read_walk;
bpfptr_t fd_array;
}; };
__printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log, __printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log,
......
/* SPDX-License-Identifier: GPL-2.0-only */
/* A pointer that can point to either kernel or userspace memory. */
#ifndef _LINUX_BPFPTR_H
#define _LINUX_BPFPTR_H
#include <linux/sockptr.h>
typedef sockptr_t bpfptr_t;
static inline bool bpfptr_is_kernel(bpfptr_t bpfptr)
{
return bpfptr.is_kernel;
}
static inline bpfptr_t KERNEL_BPFPTR(void *p)
{
return (bpfptr_t) { .kernel = p, .is_kernel = true };
}
static inline bpfptr_t USER_BPFPTR(void __user *p)
{
return (bpfptr_t) { .user = p };
}
static inline bpfptr_t make_bpfptr(u64 addr, bool is_kernel)
{
if (is_kernel)
return KERNEL_BPFPTR((void*) (uintptr_t) addr);
else
return USER_BPFPTR(u64_to_user_ptr(addr));
}
static inline bool bpfptr_is_null(bpfptr_t bpfptr)
{
if (bpfptr_is_kernel(bpfptr))
return !bpfptr.kernel;
return !bpfptr.user;
}
static inline void bpfptr_add(bpfptr_t *bpfptr, size_t val)
{
if (bpfptr_is_kernel(*bpfptr))
bpfptr->kernel += val;
else
bpfptr->user += 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);
}
static inline int copy_from_bpfptr(void *dst, bpfptr_t src, size_t size)
{
return copy_from_bpfptr_offset(dst, src, 0, size);
}
static inline int copy_to_bpfptr_offset(bpfptr_t dst, size_t offset,
const void *src, size_t size)
{
return copy_to_sockptr_offset((sockptr_t) dst, offset, src, size);
}
static inline void *memdup_bpfptr(bpfptr_t src, size_t len)
{
return memdup_sockptr((sockptr_t) src, len);
}
static inline long strncpy_from_bpfptr(char *dst, bpfptr_t src, size_t count)
{
return strncpy_from_sockptr(dst, (sockptr_t) src, count);
}
#endif /* _LINUX_BPFPTR_H */
...@@ -21,7 +21,7 @@ extern const struct file_operations btf_fops; ...@@ -21,7 +21,7 @@ extern const struct file_operations btf_fops;
void btf_get(struct btf *btf); void btf_get(struct btf *btf);
void btf_put(struct btf *btf); void btf_put(struct btf *btf);
int btf_new_fd(const union bpf_attr *attr); int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr);
struct btf *btf_get_by_fd(int fd); struct btf *btf_get_by_fd(int fd);
int btf_get_info_by_fd(const struct btf *btf, int btf_get_info_by_fd(const struct btf *btf,
const union bpf_attr *attr, const union bpf_attr *attr,
......
...@@ -126,8 +126,7 @@ int sk_msg_zerocopy_from_iter(struct sock *sk, struct iov_iter *from, ...@@ -126,8 +126,7 @@ int sk_msg_zerocopy_from_iter(struct sock *sk, struct iov_iter *from,
struct sk_msg *msg, u32 bytes); struct sk_msg *msg, u32 bytes);
int sk_msg_memcopy_from_iter(struct sock *sk, struct iov_iter *from, int sk_msg_memcopy_from_iter(struct sock *sk, struct iov_iter *from,
struct sk_msg *msg, u32 bytes); struct sk_msg *msg, u32 bytes);
int sk_msg_wait_data(struct sock *sk, struct sk_psock *psock, int flags, int sk_msg_wait_data(struct sock *sk, struct sk_psock *psock, long timeo);
long timeo, int *err);
int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg, int sk_msg_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg,
int len, int flags); int len, int flags);
......
...@@ -837,6 +837,7 @@ enum bpf_cmd { ...@@ -837,6 +837,7 @@ enum bpf_cmd {
BPF_PROG_ATTACH, BPF_PROG_ATTACH,
BPF_PROG_DETACH, BPF_PROG_DETACH,
BPF_PROG_TEST_RUN, BPF_PROG_TEST_RUN,
BPF_PROG_RUN = BPF_PROG_TEST_RUN,
BPF_PROG_GET_NEXT_ID, BPF_PROG_GET_NEXT_ID,
BPF_MAP_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID,
BPF_PROG_GET_FD_BY_ID, BPF_PROG_GET_FD_BY_ID,
...@@ -937,6 +938,7 @@ enum bpf_prog_type { ...@@ -937,6 +938,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_EXT, BPF_PROG_TYPE_EXT,
BPF_PROG_TYPE_LSM, BPF_PROG_TYPE_LSM,
BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
}; };
enum bpf_attach_type { enum bpf_attach_type {
...@@ -1097,8 +1099,8 @@ enum bpf_link_type { ...@@ -1097,8 +1099,8 @@ enum bpf_link_type {
/* When BPF ldimm64's insn[0].src_reg != 0 then this can have /* When BPF ldimm64's insn[0].src_reg != 0 then this can have
* the following extensions: * the following extensions:
* *
* insn[0].src_reg: BPF_PSEUDO_MAP_FD * insn[0].src_reg: BPF_PSEUDO_MAP_[FD|IDX]
* insn[0].imm: map fd * insn[0].imm: map fd or fd_idx
* insn[1].imm: 0 * insn[1].imm: 0
* insn[0].off: 0 * insn[0].off: 0
* insn[1].off: 0 * insn[1].off: 0
...@@ -1106,15 +1108,19 @@ enum bpf_link_type { ...@@ -1106,15 +1108,19 @@ enum bpf_link_type {
* verifier type: CONST_PTR_TO_MAP * verifier type: CONST_PTR_TO_MAP
*/ */
#define BPF_PSEUDO_MAP_FD 1 #define BPF_PSEUDO_MAP_FD 1
/* insn[0].src_reg: BPF_PSEUDO_MAP_VALUE #define BPF_PSEUDO_MAP_IDX 5
* insn[0].imm: map fd
/* insn[0].src_reg: BPF_PSEUDO_MAP_[IDX_]VALUE
* insn[0].imm: map fd or fd_idx
* insn[1].imm: offset into value * insn[1].imm: offset into value
* insn[0].off: 0 * insn[0].off: 0
* insn[1].off: 0 * insn[1].off: 0
* ldimm64 rewrite: address of map[0]+offset * ldimm64 rewrite: address of map[0]+offset
* verifier type: PTR_TO_MAP_VALUE * verifier type: PTR_TO_MAP_VALUE
*/ */
#define BPF_PSEUDO_MAP_VALUE 2 #define BPF_PSEUDO_MAP_VALUE 2
#define BPF_PSEUDO_MAP_IDX_VALUE 6
/* insn[0].src_reg: BPF_PSEUDO_BTF_ID /* insn[0].src_reg: BPF_PSEUDO_BTF_ID
* insn[0].imm: kernel btd id of VAR * insn[0].imm: kernel btd id of VAR
* insn[1].imm: 0 * insn[1].imm: 0
...@@ -1314,6 +1320,8 @@ union bpf_attr { ...@@ -1314,6 +1320,8 @@ union bpf_attr {
/* or valid module BTF object fd or 0 to attach to vmlinux */ /* or valid module BTF object fd or 0 to attach to vmlinux */
__u32 attach_btf_obj_fd; __u32 attach_btf_obj_fd;
}; };
__u32 :32; /* pad */
__aligned_u64 fd_array; /* array of FDs */
}; };
struct { /* anonymous struct used by BPF_OBJ_* commands */ struct { /* anonymous struct used by BPF_OBJ_* commands */
...@@ -4735,6 +4743,24 @@ union bpf_attr { ...@@ -4735,6 +4743,24 @@ union bpf_attr {
* be zero-terminated except when **str_size** is 0. * be zero-terminated except when **str_size** is 0.
* *
* Or **-EBUSY** if the per-CPU memory copy buffer is busy. * Or **-EBUSY** if the per-CPU memory copy buffer is busy.
*
* long bpf_sys_bpf(u32 cmd, void *attr, u32 attr_size)
* Description
* Execute bpf syscall with given arguments.
* Return
* A syscall result.
*
* long bpf_btf_find_by_name_kind(char *name, int name_sz, u32 kind, int flags)
* Description
* Find BTF type with given name and kind in vmlinux BTF or in module's BTFs.
* Return
* Returns btf_id and btf_obj_fd in lower and upper 32 bits.
*
* long bpf_sys_close(u32 fd)
* Description
* Execute close syscall for given FD.
* Return
* A syscall result.
*/ */
#define __BPF_FUNC_MAPPER(FN) \ #define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \ FN(unspec), \
...@@ -4903,6 +4929,9 @@ union bpf_attr { ...@@ -4903,6 +4929,9 @@ union bpf_attr {
FN(check_mtu), \ FN(check_mtu), \
FN(for_each_map_elem), \ FN(for_each_map_elem), \
FN(snprintf), \ FN(snprintf), \
FN(sys_bpf), \
FN(btf_find_by_name_kind), \
FN(sys_close), \
/* */ /* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper /* integer value in 'imm' field of BPF_CALL instruction selects which helper
......
...@@ -473,15 +473,16 @@ bool bpf_link_is_iter(struct bpf_link *link) ...@@ -473,15 +473,16 @@ bool bpf_link_is_iter(struct bpf_link *link)
return link->ops == &bpf_iter_link_lops; return link->ops == &bpf_iter_link_lops;
} }
int bpf_iter_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
struct bpf_prog *prog)
{ {
union bpf_iter_link_info __user *ulinfo;
struct bpf_link_primer link_primer; struct bpf_link_primer link_primer;
struct bpf_iter_target_info *tinfo; struct bpf_iter_target_info *tinfo;
union bpf_iter_link_info linfo; union bpf_iter_link_info linfo;
struct bpf_iter_link *link; struct bpf_iter_link *link;
u32 prog_btf_id, linfo_len; u32 prog_btf_id, linfo_len;
bool existed = false; bool existed = false;
bpfptr_t ulinfo;
int err; int err;
if (attr->link_create.target_fd || attr->link_create.flags) if (attr->link_create.target_fd || attr->link_create.flags)
...@@ -489,18 +490,18 @@ int bpf_iter_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) ...@@ -489,18 +490,18 @@ int bpf_iter_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
memset(&linfo, 0, sizeof(union bpf_iter_link_info)); memset(&linfo, 0, sizeof(union bpf_iter_link_info));
ulinfo = u64_to_user_ptr(attr->link_create.iter_info); ulinfo = make_bpfptr(attr->link_create.iter_info, uattr.is_kernel);
linfo_len = attr->link_create.iter_info_len; linfo_len = attr->link_create.iter_info_len;
if (!ulinfo ^ !linfo_len) if (bpfptr_is_null(ulinfo) ^ !linfo_len)
return -EINVAL; return -EINVAL;
if (ulinfo) { if (!bpfptr_is_null(ulinfo)) {
err = bpf_check_uarg_tail_zero(ulinfo, sizeof(linfo), err = bpf_check_uarg_tail_zero(ulinfo, sizeof(linfo),
linfo_len); linfo_len);
if (err) if (err)
return err; return err;
linfo_len = min_t(u32, linfo_len, sizeof(linfo)); linfo_len = min_t(u32, linfo_len, sizeof(linfo));
if (copy_from_user(&linfo, ulinfo, linfo_len)) if (copy_from_bpfptr(&linfo, ulinfo, linfo_len))
return -EFAULT; return -EFAULT;
} }
......
...@@ -4257,7 +4257,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env) ...@@ -4257,7 +4257,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env)
return 0; return 0;
} }
static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size, static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size,
u32 log_level, char __user *log_ubuf, u32 log_size) u32 log_level, char __user *log_ubuf, u32 log_size)
{ {
struct btf_verifier_env *env = NULL; struct btf_verifier_env *env = NULL;
...@@ -4306,7 +4306,7 @@ static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size, ...@@ -4306,7 +4306,7 @@ static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size,
btf->data = data; btf->data = data;
btf->data_size = btf_data_size; btf->data_size = btf_data_size;
if (copy_from_user(data, btf_data, btf_data_size)) { if (copy_from_bpfptr(data, btf_data, btf_data_size)) {
err = -EFAULT; err = -EFAULT;
goto errout; goto errout;
} }
...@@ -5780,12 +5780,12 @@ static int __btf_new_fd(struct btf *btf) ...@@ -5780,12 +5780,12 @@ static int __btf_new_fd(struct btf *btf)
return anon_inode_getfd("btf", &btf_fops, btf, O_RDONLY | O_CLOEXEC); return anon_inode_getfd("btf", &btf_fops, btf, O_RDONLY | O_CLOEXEC);
} }
int btf_new_fd(const union bpf_attr *attr) int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr)
{ {
struct btf *btf; struct btf *btf;
int ret; int ret;
btf = btf_parse(u64_to_user_ptr(attr->btf), btf = btf_parse(make_bpfptr(attr->btf, uattr.is_kernel),
attr->btf_size, attr->btf_log_level, attr->btf_size, attr->btf_log_level,
u64_to_user_ptr(attr->btf_log_buf), u64_to_user_ptr(attr->btf_log_buf),
attr->btf_log_size); attr->btf_log_size);
...@@ -6085,3 +6085,65 @@ struct module *btf_try_get_module(const struct btf *btf) ...@@ -6085,3 +6085,65 @@ struct module *btf_try_get_module(const struct btf *btf)
return res; return res;
} }
BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int, flags)
{
struct btf *btf;
long ret;
if (flags)
return -EINVAL;
if (name_sz <= 1 || name[name_sz - 1])
return -EINVAL;
btf = bpf_get_btf_vmlinux();
if (IS_ERR(btf))
return PTR_ERR(btf);
ret = btf_find_by_name_kind(btf, name, kind);
/* ret is never zero, since btf_find_by_name_kind returns
* positive btf_id or negative error.
*/
if (ret < 0) {
struct btf *mod_btf;
int id;
/* If name is not found in vmlinux's BTF then search in module's BTFs */
spin_lock_bh(&btf_idr_lock);
idr_for_each_entry(&btf_idr, mod_btf, id) {
if (!btf_is_module(mod_btf))
continue;
/* linear search could be slow hence unlock/lock
* the IDR to avoiding holding it for too long
*/
btf_get(mod_btf);
spin_unlock_bh(&btf_idr_lock);
ret = btf_find_by_name_kind(mod_btf, name, kind);
if (ret > 0) {
int btf_obj_fd;
btf_obj_fd = __btf_new_fd(mod_btf);
if (btf_obj_fd < 0) {
btf_put(mod_btf);
return btf_obj_fd;
}
return ret | (((u64)btf_obj_fd) << 32);
}
spin_lock_bh(&btf_idr_lock);
btf_put(mod_btf);
}
spin_unlock_bh(&btf_idr_lock);
}
return ret;
}
const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.func = bpf_btf_find_by_name_kind,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM,
.arg2_type = ARG_CONST_SIZE,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_ANYTHING,
};
...@@ -72,11 +72,10 @@ static const struct bpf_map_ops * const bpf_map_types[] = { ...@@ -72,11 +72,10 @@ static const struct bpf_map_ops * const bpf_map_types[] = {
* copy_from_user() call. However, this is not a concern since this function is * copy_from_user() call. However, this is not a concern since this function is
* meant to be a future-proofing of bits. * meant to be a future-proofing of bits.
*/ */
int bpf_check_uarg_tail_zero(void __user *uaddr, int bpf_check_uarg_tail_zero(bpfptr_t uaddr,
size_t expected_size, size_t expected_size,
size_t actual_size) size_t actual_size)
{ {
unsigned char __user *addr = uaddr + expected_size;
int res; int res;
if (unlikely(actual_size > PAGE_SIZE)) /* silly large */ if (unlikely(actual_size > PAGE_SIZE)) /* silly large */
...@@ -85,7 +84,12 @@ int bpf_check_uarg_tail_zero(void __user *uaddr, ...@@ -85,7 +84,12 @@ int bpf_check_uarg_tail_zero(void __user *uaddr,
if (actual_size <= expected_size) if (actual_size <= expected_size)
return 0; return 0;
res = check_zeroed_user(addr, actual_size - expected_size); if (uaddr.is_kernel)
res = memchr_inv(uaddr.kernel + expected_size, 0,
actual_size - expected_size) == NULL;
else
res = check_zeroed_user(uaddr.user + expected_size,
actual_size - expected_size);
if (res < 0) if (res < 0)
return res; return res;
return res ? 0 : -E2BIG; return res ? 0 : -E2BIG;
...@@ -1004,6 +1008,17 @@ static void *__bpf_copy_key(void __user *ukey, u64 key_size) ...@@ -1004,6 +1008,17 @@ static void *__bpf_copy_key(void __user *ukey, u64 key_size)
return NULL; return NULL;
} }
static void *___bpf_copy_key(bpfptr_t ukey, u64 key_size)
{
if (key_size)
return memdup_bpfptr(ukey, key_size);
if (!bpfptr_is_null(ukey))
return ERR_PTR(-EINVAL);
return NULL;
}
/* last field in 'union bpf_attr' used by this command */ /* last field in 'union bpf_attr' used by this command */
#define BPF_MAP_LOOKUP_ELEM_LAST_FIELD flags #define BPF_MAP_LOOKUP_ELEM_LAST_FIELD flags
...@@ -1074,10 +1089,10 @@ static int map_lookup_elem(union bpf_attr *attr) ...@@ -1074,10 +1089,10 @@ static int map_lookup_elem(union bpf_attr *attr)
#define BPF_MAP_UPDATE_ELEM_LAST_FIELD flags #define BPF_MAP_UPDATE_ELEM_LAST_FIELD flags
static int map_update_elem(union bpf_attr *attr) static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr)
{ {
void __user *ukey = u64_to_user_ptr(attr->key); bpfptr_t ukey = make_bpfptr(attr->key, uattr.is_kernel);
void __user *uvalue = u64_to_user_ptr(attr->value); bpfptr_t uvalue = make_bpfptr(attr->value, uattr.is_kernel);
int ufd = attr->map_fd; int ufd = attr->map_fd;
struct bpf_map *map; struct bpf_map *map;
void *key, *value; void *key, *value;
...@@ -1103,7 +1118,7 @@ static int map_update_elem(union bpf_attr *attr) ...@@ -1103,7 +1118,7 @@ static int map_update_elem(union bpf_attr *attr)
goto err_put; goto err_put;
} }
key = __bpf_copy_key(ukey, map->key_size); key = ___bpf_copy_key(ukey, map->key_size);
if (IS_ERR(key)) { if (IS_ERR(key)) {
err = PTR_ERR(key); err = PTR_ERR(key);
goto err_put; goto err_put;
...@@ -1123,7 +1138,7 @@ static int map_update_elem(union bpf_attr *attr) ...@@ -1123,7 +1138,7 @@ static int map_update_elem(union bpf_attr *attr)
goto free_key; goto free_key;
err = -EFAULT; err = -EFAULT;
if (copy_from_user(value, uvalue, value_size) != 0) if (copy_from_bpfptr(value, uvalue, value_size) != 0)
goto free_value; goto free_value;
err = bpf_map_update_value(map, f, key, value, attr->flags); err = bpf_map_update_value(map, f, key, value, attr->flags);
...@@ -2014,6 +2029,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type, ...@@ -2014,6 +2029,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
if (expected_attach_type == BPF_SK_LOOKUP) if (expected_attach_type == BPF_SK_LOOKUP)
return 0; return 0;
return -EINVAL; return -EINVAL;
case BPF_PROG_TYPE_SYSCALL:
case BPF_PROG_TYPE_EXT: case BPF_PROG_TYPE_EXT:
if (expected_attach_type) if (expected_attach_type)
return -EINVAL; return -EINVAL;
...@@ -2073,9 +2089,9 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type) ...@@ -2073,9 +2089,9 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
} }
/* last field in 'union bpf_attr' used by this command */ /* last field in 'union bpf_attr' used by this command */
#define BPF_PROG_LOAD_LAST_FIELD attach_prog_fd #define BPF_PROG_LOAD_LAST_FIELD fd_array
static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr)
{ {
enum bpf_prog_type type = attr->prog_type; enum bpf_prog_type type = attr->prog_type;
struct bpf_prog *prog, *dst_prog = NULL; struct bpf_prog *prog, *dst_prog = NULL;
...@@ -2100,8 +2116,9 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) ...@@ -2100,8 +2116,9 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
return -EPERM; return -EPERM;
/* copy eBPF program license from user space */ /* copy eBPF program license from user space */
if (strncpy_from_user(license, u64_to_user_ptr(attr->license), if (strncpy_from_bpfptr(license,
sizeof(license) - 1) < 0) make_bpfptr(attr->license, uattr.is_kernel),
sizeof(license) - 1) < 0)
return -EFAULT; return -EFAULT;
license[sizeof(license) - 1] = 0; license[sizeof(license) - 1] = 0;
...@@ -2185,8 +2202,9 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) ...@@ -2185,8 +2202,9 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
prog->len = attr->insn_cnt; prog->len = attr->insn_cnt;
err = -EFAULT; err = -EFAULT;
if (copy_from_user(prog->insns, u64_to_user_ptr(attr->insns), if (copy_from_bpfptr(prog->insns,
bpf_prog_insn_size(prog)) != 0) make_bpfptr(attr->insns, uattr.is_kernel),
bpf_prog_insn_size(prog)) != 0)
goto free_prog_sec; goto free_prog_sec;
prog->orig_prog = NULL; prog->orig_prog = NULL;
...@@ -3422,7 +3440,7 @@ static int bpf_prog_get_info_by_fd(struct file *file, ...@@ -3422,7 +3440,7 @@ static int bpf_prog_get_info_by_fd(struct file *file,
u32 ulen; u32 ulen;
int err; int err;
err = bpf_check_uarg_tail_zero(uinfo, sizeof(info), info_len); err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len);
if (err) if (err)
return err; return err;
info_len = min_t(u32, sizeof(info), info_len); info_len = min_t(u32, sizeof(info), info_len);
...@@ -3701,7 +3719,7 @@ static int bpf_map_get_info_by_fd(struct file *file, ...@@ -3701,7 +3719,7 @@ static int bpf_map_get_info_by_fd(struct file *file,
u32 info_len = attr->info.info_len; u32 info_len = attr->info.info_len;
int err; int err;
err = bpf_check_uarg_tail_zero(uinfo, sizeof(info), info_len); err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len);
if (err) if (err)
return err; return err;
info_len = min_t(u32, sizeof(info), info_len); info_len = min_t(u32, sizeof(info), info_len);
...@@ -3744,7 +3762,7 @@ static int bpf_btf_get_info_by_fd(struct file *file, ...@@ -3744,7 +3762,7 @@ static int bpf_btf_get_info_by_fd(struct file *file,
u32 info_len = attr->info.info_len; u32 info_len = attr->info.info_len;
int err; int err;
err = bpf_check_uarg_tail_zero(uinfo, sizeof(*uinfo), info_len); err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(*uinfo), info_len);
if (err) if (err)
return err; return err;
...@@ -3761,7 +3779,7 @@ static int bpf_link_get_info_by_fd(struct file *file, ...@@ -3761,7 +3779,7 @@ static int bpf_link_get_info_by_fd(struct file *file,
u32 info_len = attr->info.info_len; u32 info_len = attr->info.info_len;
int err; int err;
err = bpf_check_uarg_tail_zero(uinfo, sizeof(info), info_len); err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len);
if (err) if (err)
return err; return err;
info_len = min_t(u32, sizeof(info), info_len); info_len = min_t(u32, sizeof(info), info_len);
...@@ -3824,7 +3842,7 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr, ...@@ -3824,7 +3842,7 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
#define BPF_BTF_LOAD_LAST_FIELD btf_log_level #define BPF_BTF_LOAD_LAST_FIELD btf_log_level
static int bpf_btf_load(const union bpf_attr *attr) static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr)
{ {
if (CHECK_ATTR(BPF_BTF_LOAD)) if (CHECK_ATTR(BPF_BTF_LOAD))
return -EINVAL; return -EINVAL;
...@@ -3832,7 +3850,7 @@ static int bpf_btf_load(const union bpf_attr *attr) ...@@ -3832,7 +3850,7 @@ static int bpf_btf_load(const union bpf_attr *attr)
if (!bpf_capable()) if (!bpf_capable())
return -EPERM; return -EPERM;
return btf_new_fd(attr); return btf_new_fd(attr, uattr);
} }
#define BPF_BTF_GET_FD_BY_ID_LAST_FIELD btf_id #define BPF_BTF_GET_FD_BY_ID_LAST_FIELD btf_id
...@@ -4022,13 +4040,14 @@ static int bpf_map_do_batch(const union bpf_attr *attr, ...@@ -4022,13 +4040,14 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
return err; return err;
} }
static int tracing_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) static int tracing_bpf_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
struct bpf_prog *prog)
{ {
if (attr->link_create.attach_type != prog->expected_attach_type) if (attr->link_create.attach_type != prog->expected_attach_type)
return -EINVAL; return -EINVAL;
if (prog->expected_attach_type == BPF_TRACE_ITER) if (prog->expected_attach_type == BPF_TRACE_ITER)
return bpf_iter_link_attach(attr, prog); return bpf_iter_link_attach(attr, uattr, prog);
else if (prog->type == BPF_PROG_TYPE_EXT) else if (prog->type == BPF_PROG_TYPE_EXT)
return bpf_tracing_prog_attach(prog, return bpf_tracing_prog_attach(prog,
attr->link_create.target_fd, attr->link_create.target_fd,
...@@ -4037,7 +4056,7 @@ static int tracing_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog * ...@@ -4037,7 +4056,7 @@ static int tracing_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *
} }
#define BPF_LINK_CREATE_LAST_FIELD link_create.iter_info_len #define BPF_LINK_CREATE_LAST_FIELD link_create.iter_info_len
static int link_create(union bpf_attr *attr) static int link_create(union bpf_attr *attr, bpfptr_t uattr)
{ {
enum bpf_prog_type ptype; enum bpf_prog_type ptype;
struct bpf_prog *prog; struct bpf_prog *prog;
...@@ -4056,7 +4075,7 @@ static int link_create(union bpf_attr *attr) ...@@ -4056,7 +4075,7 @@ static int link_create(union bpf_attr *attr)
goto out; goto out;
if (prog->type == BPF_PROG_TYPE_EXT) { if (prog->type == BPF_PROG_TYPE_EXT) {
ret = tracing_bpf_link_attach(attr, prog); ret = tracing_bpf_link_attach(attr, uattr, prog);
goto out; goto out;
} }
...@@ -4077,7 +4096,7 @@ static int link_create(union bpf_attr *attr) ...@@ -4077,7 +4096,7 @@ static int link_create(union bpf_attr *attr)
ret = cgroup_bpf_link_attach(attr, prog); ret = cgroup_bpf_link_attach(attr, prog);
break; break;
case BPF_PROG_TYPE_TRACING: case BPF_PROG_TYPE_TRACING:
ret = tracing_bpf_link_attach(attr, prog); ret = tracing_bpf_link_attach(attr, uattr, prog);
break; break;
case BPF_PROG_TYPE_FLOW_DISSECTOR: case BPF_PROG_TYPE_FLOW_DISSECTOR:
case BPF_PROG_TYPE_SK_LOOKUP: case BPF_PROG_TYPE_SK_LOOKUP:
...@@ -4365,7 +4384,7 @@ static int bpf_prog_bind_map(union bpf_attr *attr) ...@@ -4365,7 +4384,7 @@ static int bpf_prog_bind_map(union bpf_attr *attr)
return ret; return ret;
} }
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size)
{ {
union bpf_attr attr; union bpf_attr attr;
int err; int err;
...@@ -4380,7 +4399,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -4380,7 +4399,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
/* copy attributes from user space, may be less than sizeof(bpf_attr) */ /* copy attributes from user space, may be less than sizeof(bpf_attr) */
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
if (copy_from_user(&attr, uattr, size) != 0) if (copy_from_bpfptr(&attr, uattr, size) != 0)
return -EFAULT; return -EFAULT;
err = security_bpf(cmd, &attr, size); err = security_bpf(cmd, &attr, size);
...@@ -4395,7 +4414,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -4395,7 +4414,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
err = map_lookup_elem(&attr); err = map_lookup_elem(&attr);
break; break;
case BPF_MAP_UPDATE_ELEM: case BPF_MAP_UPDATE_ELEM:
err = map_update_elem(&attr); err = map_update_elem(&attr, uattr);
break; break;
case BPF_MAP_DELETE_ELEM: case BPF_MAP_DELETE_ELEM:
err = map_delete_elem(&attr); err = map_delete_elem(&attr);
...@@ -4422,21 +4441,21 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -4422,21 +4441,21 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
err = bpf_prog_detach(&attr); err = bpf_prog_detach(&attr);
break; break;
case BPF_PROG_QUERY: case BPF_PROG_QUERY:
err = bpf_prog_query(&attr, uattr); err = bpf_prog_query(&attr, uattr.user);
break; break;
case BPF_PROG_TEST_RUN: case BPF_PROG_TEST_RUN:
err = bpf_prog_test_run(&attr, uattr); err = bpf_prog_test_run(&attr, uattr.user);
break; break;
case BPF_PROG_GET_NEXT_ID: case BPF_PROG_GET_NEXT_ID:
err = bpf_obj_get_next_id(&attr, uattr, err = bpf_obj_get_next_id(&attr, uattr.user,
&prog_idr, &prog_idr_lock); &prog_idr, &prog_idr_lock);
break; break;
case BPF_MAP_GET_NEXT_ID: case BPF_MAP_GET_NEXT_ID:
err = bpf_obj_get_next_id(&attr, uattr, err = bpf_obj_get_next_id(&attr, uattr.user,
&map_idr, &map_idr_lock); &map_idr, &map_idr_lock);
break; break;
case BPF_BTF_GET_NEXT_ID: case BPF_BTF_GET_NEXT_ID:
err = bpf_obj_get_next_id(&attr, uattr, err = bpf_obj_get_next_id(&attr, uattr.user,
&btf_idr, &btf_idr_lock); &btf_idr, &btf_idr_lock);
break; break;
case BPF_PROG_GET_FD_BY_ID: case BPF_PROG_GET_FD_BY_ID:
...@@ -4446,38 +4465,38 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -4446,38 +4465,38 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
err = bpf_map_get_fd_by_id(&attr); err = bpf_map_get_fd_by_id(&attr);
break; break;
case BPF_OBJ_GET_INFO_BY_FD: case BPF_OBJ_GET_INFO_BY_FD:
err = bpf_obj_get_info_by_fd(&attr, uattr); err = bpf_obj_get_info_by_fd(&attr, uattr.user);
break; break;
case BPF_RAW_TRACEPOINT_OPEN: case BPF_RAW_TRACEPOINT_OPEN:
err = bpf_raw_tracepoint_open(&attr); err = bpf_raw_tracepoint_open(&attr);
break; break;
case BPF_BTF_LOAD: case BPF_BTF_LOAD:
err = bpf_btf_load(&attr); err = bpf_btf_load(&attr, uattr);
break; break;
case BPF_BTF_GET_FD_BY_ID: case BPF_BTF_GET_FD_BY_ID:
err = bpf_btf_get_fd_by_id(&attr); err = bpf_btf_get_fd_by_id(&attr);
break; break;
case BPF_TASK_FD_QUERY: case BPF_TASK_FD_QUERY:
err = bpf_task_fd_query(&attr, uattr); err = bpf_task_fd_query(&attr, uattr.user);
break; break;
case BPF_MAP_LOOKUP_AND_DELETE_ELEM: case BPF_MAP_LOOKUP_AND_DELETE_ELEM:
err = map_lookup_and_delete_elem(&attr); err = map_lookup_and_delete_elem(&attr);
break; break;
case BPF_MAP_LOOKUP_BATCH: case BPF_MAP_LOOKUP_BATCH:
err = bpf_map_do_batch(&attr, uattr, BPF_MAP_LOOKUP_BATCH); err = bpf_map_do_batch(&attr, uattr.user, BPF_MAP_LOOKUP_BATCH);
break; break;
case BPF_MAP_LOOKUP_AND_DELETE_BATCH: case BPF_MAP_LOOKUP_AND_DELETE_BATCH:
err = bpf_map_do_batch(&attr, uattr, err = bpf_map_do_batch(&attr, uattr.user,
BPF_MAP_LOOKUP_AND_DELETE_BATCH); BPF_MAP_LOOKUP_AND_DELETE_BATCH);
break; break;
case BPF_MAP_UPDATE_BATCH: case BPF_MAP_UPDATE_BATCH:
err = bpf_map_do_batch(&attr, uattr, BPF_MAP_UPDATE_BATCH); err = bpf_map_do_batch(&attr, uattr.user, BPF_MAP_UPDATE_BATCH);
break; break;
case BPF_MAP_DELETE_BATCH: case BPF_MAP_DELETE_BATCH:
err = bpf_map_do_batch(&attr, uattr, BPF_MAP_DELETE_BATCH); err = bpf_map_do_batch(&attr, uattr.user, BPF_MAP_DELETE_BATCH);
break; break;
case BPF_LINK_CREATE: case BPF_LINK_CREATE:
err = link_create(&attr); err = link_create(&attr, uattr);
break; break;
case BPF_LINK_UPDATE: case BPF_LINK_UPDATE:
err = link_update(&attr); err = link_update(&attr);
...@@ -4486,7 +4505,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -4486,7 +4505,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
err = bpf_link_get_fd_by_id(&attr); err = bpf_link_get_fd_by_id(&attr);
break; break;
case BPF_LINK_GET_NEXT_ID: case BPF_LINK_GET_NEXT_ID:
err = bpf_obj_get_next_id(&attr, uattr, err = bpf_obj_get_next_id(&attr, uattr.user,
&link_idr, &link_idr_lock); &link_idr, &link_idr_lock);
break; break;
case BPF_ENABLE_STATS: case BPF_ENABLE_STATS:
...@@ -4508,3 +4527,94 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz ...@@ -4508,3 +4527,94 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
return err; return err;
} }
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
{
return __sys_bpf(cmd, USER_BPFPTR(uattr), size);
}
static bool syscall_prog_is_valid_access(int off, int size,
enum bpf_access_type type,
const struct bpf_prog *prog,
struct bpf_insn_access_aux *info)
{
if (off < 0 || off >= U16_MAX)
return false;
if (off % size != 0)
return false;
return true;
}
BPF_CALL_3(bpf_sys_bpf, int, cmd, void *, attr, u32, attr_size)
{
switch (cmd) {
case BPF_MAP_CREATE:
case BPF_MAP_UPDATE_ELEM:
case BPF_MAP_FREEZE:
case BPF_PROG_LOAD:
case BPF_BTF_LOAD:
break;
/* case BPF_PROG_TEST_RUN:
* is not part of this list to prevent recursive test_run
*/
default:
return -EINVAL;
}
return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size);
}
static const struct bpf_func_proto bpf_sys_bpf_proto = {
.func = bpf_sys_bpf,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_ANYTHING,
.arg2_type = ARG_PTR_TO_MEM,
.arg3_type = ARG_CONST_SIZE,
};
const struct bpf_func_proto * __weak
tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
return bpf_base_func_proto(func_id);
}
BPF_CALL_1(bpf_sys_close, u32, fd)
{
/* When bpf program calls this helper there should not be
* an fdget() without matching completed fdput().
* This helper is allowed in the following callchain only:
* sys_bpf->prog_test_run->bpf_prog->bpf_sys_close
*/
return close_fd(fd);
}
static const struct bpf_func_proto bpf_sys_close_proto = {
.func = bpf_sys_close,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_ANYTHING,
};
static const struct bpf_func_proto *
syscall_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
switch (func_id) {
case BPF_FUNC_sys_bpf:
return &bpf_sys_bpf_proto;
case BPF_FUNC_btf_find_by_name_kind:
return &bpf_btf_find_by_name_kind_proto;
case BPF_FUNC_sys_close:
return &bpf_sys_close_proto;
default:
return tracing_prog_func_proto(func_id, prog);
}
}
const struct bpf_verifier_ops bpf_syscall_verifier_ops = {
.get_func_proto = syscall_prog_func_proto,
.is_valid_access = syscall_prog_is_valid_access,
};
const struct bpf_prog_ops bpf_syscall_prog_ops = {
.test_run = bpf_prog_test_run_syscall,
};
...@@ -737,81 +737,104 @@ static void print_verifier_state(struct bpf_verifier_env *env, ...@@ -737,81 +737,104 @@ static void print_verifier_state(struct bpf_verifier_env *env,
verbose(env, "\n"); verbose(env, "\n");
} }
#define COPY_STATE_FN(NAME, COUNT, FIELD, SIZE) \ /* copy array src of length n * size bytes to dst. dst is reallocated if it's too
static int copy_##NAME##_state(struct bpf_func_state *dst, \ * small to hold src. This is different from krealloc since we don't want to preserve
const struct bpf_func_state *src) \ * the contents of dst.
{ \ *
if (!src->FIELD) \ * Leaves dst untouched if src is NULL or length is zero. Returns NULL if memory could
return 0; \ * not be allocated.
if (WARN_ON_ONCE(dst->COUNT < src->COUNT)) { \
/* internal bug, make state invalid to reject the program */ \
memset(dst, 0, sizeof(*dst)); \
return -EFAULT; \
} \
memcpy(dst->FIELD, src->FIELD, \
sizeof(*src->FIELD) * (src->COUNT / SIZE)); \
return 0; \
}
/* copy_reference_state() */
COPY_STATE_FN(reference, acquired_refs, refs, 1)
/* copy_stack_state() */
COPY_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
#undef COPY_STATE_FN
#define REALLOC_STATE_FN(NAME, COUNT, FIELD, SIZE) \
static int realloc_##NAME##_state(struct bpf_func_state *state, int size, \
bool copy_old) \
{ \
u32 old_size = state->COUNT; \
struct bpf_##NAME##_state *new_##FIELD; \
int slot = size / SIZE; \
\
if (size <= old_size || !size) { \
if (copy_old) \
return 0; \
state->COUNT = slot * SIZE; \
if (!size && old_size) { \
kfree(state->FIELD); \
state->FIELD = NULL; \
} \
return 0; \
} \
new_##FIELD = kmalloc_array(slot, sizeof(struct bpf_##NAME##_state), \
GFP_KERNEL); \
if (!new_##FIELD) \
return -ENOMEM; \
if (copy_old) { \
if (state->FIELD) \
memcpy(new_##FIELD, state->FIELD, \
sizeof(*new_##FIELD) * (old_size / SIZE)); \
memset(new_##FIELD + old_size / SIZE, 0, \
sizeof(*new_##FIELD) * (size - old_size) / SIZE); \
} \
state->COUNT = slot * SIZE; \
kfree(state->FIELD); \
state->FIELD = new_##FIELD; \
return 0; \
}
/* realloc_reference_state() */
REALLOC_STATE_FN(reference, acquired_refs, refs, 1)
/* realloc_stack_state() */
REALLOC_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
#undef REALLOC_STATE_FN
/* do_check() starts with zero-sized stack in struct bpf_verifier_state to
* make it consume minimal amount of memory. check_stack_write() access from
* the program calls into realloc_func_state() to grow the stack size.
* Note there is a non-zero 'parent' pointer inside bpf_verifier_state
* which realloc_stack_state() copies over. It points to previous
* bpf_verifier_state which is never reallocated.
*/ */
static int realloc_func_state(struct bpf_func_state *state, int stack_size, static void *copy_array(void *dst, const void *src, size_t n, size_t size, gfp_t flags)
int refs_size, bool copy_old)
{ {
int err = realloc_reference_state(state, refs_size, copy_old); size_t bytes;
if (err)
return err; if (ZERO_OR_NULL_PTR(src))
return realloc_stack_state(state, stack_size, copy_old); goto out;
if (unlikely(check_mul_overflow(n, size, &bytes)))
return NULL;
if (ksize(dst) < bytes) {
kfree(dst);
dst = kmalloc_track_caller(bytes, flags);
if (!dst)
return NULL;
}
memcpy(dst, src, bytes);
out:
return dst ? dst : ZERO_SIZE_PTR;
}
/* resize an array from old_n items to new_n items. the array is reallocated if it's too
* small to hold new_n items. new items are zeroed out if the array grows.
*
* Contrary to krealloc_array, does not free arr if new_n is zero.
*/
static void *realloc_array(void *arr, size_t old_n, size_t new_n, size_t size)
{
if (!new_n || old_n == new_n)
goto out;
arr = krealloc_array(arr, new_n, size, GFP_KERNEL);
if (!arr)
return NULL;
if (new_n > old_n)
memset(arr + old_n * size, 0, (new_n - old_n) * size);
out:
return arr ? arr : ZERO_SIZE_PTR;
}
static int copy_reference_state(struct bpf_func_state *dst, const struct bpf_func_state *src)
{
dst->refs = copy_array(dst->refs, src->refs, src->acquired_refs,
sizeof(struct bpf_reference_state), GFP_KERNEL);
if (!dst->refs)
return -ENOMEM;
dst->acquired_refs = src->acquired_refs;
return 0;
}
static int copy_stack_state(struct bpf_func_state *dst, const struct bpf_func_state *src)
{
size_t n = src->allocated_stack / BPF_REG_SIZE;
dst->stack = copy_array(dst->stack, src->stack, n, sizeof(struct bpf_stack_state),
GFP_KERNEL);
if (!dst->stack)
return -ENOMEM;
dst->allocated_stack = src->allocated_stack;
return 0;
}
static int resize_reference_state(struct bpf_func_state *state, size_t n)
{
state->refs = realloc_array(state->refs, state->acquired_refs, n,
sizeof(struct bpf_reference_state));
if (!state->refs)
return -ENOMEM;
state->acquired_refs = n;
return 0;
}
static int grow_stack_state(struct bpf_func_state *state, int size)
{
size_t old_n = state->allocated_stack / BPF_REG_SIZE, n = size / BPF_REG_SIZE;
if (old_n >= n)
return 0;
state->stack = realloc_array(state->stack, old_n, n, sizeof(struct bpf_stack_state));
if (!state->stack)
return -ENOMEM;
state->allocated_stack = size;
return 0;
} }
/* Acquire a pointer id from the env and update the state->refs to include /* Acquire a pointer id from the env and update the state->refs to include
...@@ -825,7 +848,7 @@ static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx) ...@@ -825,7 +848,7 @@ static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx)
int new_ofs = state->acquired_refs; int new_ofs = state->acquired_refs;
int id, err; int id, err;
err = realloc_reference_state(state, state->acquired_refs + 1, true); err = resize_reference_state(state, state->acquired_refs + 1);
if (err) if (err)
return err; return err;
id = ++env->id_gen; id = ++env->id_gen;
...@@ -854,18 +877,6 @@ static int release_reference_state(struct bpf_func_state *state, int ptr_id) ...@@ -854,18 +877,6 @@ static int release_reference_state(struct bpf_func_state *state, int ptr_id)
return -EINVAL; return -EINVAL;
} }
static int transfer_reference_state(struct bpf_func_state *dst,
struct bpf_func_state *src)
{
int err = realloc_reference_state(dst, src->acquired_refs, false);
if (err)
return err;
err = copy_reference_state(dst, src);
if (err)
return err;
return 0;
}
static void free_func_state(struct bpf_func_state *state) static void free_func_state(struct bpf_func_state *state)
{ {
if (!state) if (!state)
...@@ -904,10 +915,6 @@ static int copy_func_state(struct bpf_func_state *dst, ...@@ -904,10 +915,6 @@ static int copy_func_state(struct bpf_func_state *dst,
{ {
int err; int err;
err = realloc_func_state(dst, src->allocated_stack, src->acquired_refs,
false);
if (err)
return err;
memcpy(dst, src, offsetof(struct bpf_func_state, acquired_refs)); memcpy(dst, src, offsetof(struct bpf_func_state, acquired_refs));
err = copy_reference_state(dst, src); err = copy_reference_state(dst, src);
if (err) if (err)
...@@ -919,16 +926,13 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state, ...@@ -919,16 +926,13 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
const struct bpf_verifier_state *src) const struct bpf_verifier_state *src)
{ {
struct bpf_func_state *dst; struct bpf_func_state *dst;
u32 jmp_sz = sizeof(struct bpf_idx_pair) * src->jmp_history_cnt;
int i, err; int i, err;
if (dst_state->jmp_history_cnt < src->jmp_history_cnt) { dst_state->jmp_history = copy_array(dst_state->jmp_history, src->jmp_history,
kfree(dst_state->jmp_history); src->jmp_history_cnt, sizeof(struct bpf_idx_pair),
dst_state->jmp_history = kmalloc(jmp_sz, GFP_USER); GFP_USER);
if (!dst_state->jmp_history) if (!dst_state->jmp_history)
return -ENOMEM; return -ENOMEM;
}
memcpy(dst_state->jmp_history, src->jmp_history, jmp_sz);
dst_state->jmp_history_cnt = src->jmp_history_cnt; dst_state->jmp_history_cnt = src->jmp_history_cnt;
/* if dst has more stack frames then src frame, free them */ /* if dst has more stack frames then src frame, free them */
...@@ -2590,8 +2594,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, ...@@ -2590,8 +2594,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
u32 dst_reg = env->prog->insnsi[insn_idx].dst_reg; u32 dst_reg = env->prog->insnsi[insn_idx].dst_reg;
struct bpf_reg_state *reg = NULL; struct bpf_reg_state *reg = NULL;
err = realloc_func_state(state, round_up(slot + 1, BPF_REG_SIZE), err = grow_stack_state(state, round_up(slot + 1, BPF_REG_SIZE));
state->acquired_refs, true);
if (err) if (err)
return err; return err;
/* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0, /* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0,
...@@ -2753,8 +2756,7 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env, ...@@ -2753,8 +2756,7 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,
if (value_reg && register_is_null(value_reg)) if (value_reg && register_is_null(value_reg))
writing_zero = true; writing_zero = true;
err = realloc_func_state(state, round_up(-min_off, BPF_REG_SIZE), err = grow_stack_state(state, round_up(-min_off, BPF_REG_SIZE));
state->acquired_refs, true);
if (err) if (err)
return err; return err;
...@@ -5629,7 +5631,7 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn ...@@ -5629,7 +5631,7 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn
subprog /* subprog number within this prog */); subprog /* subprog number within this prog */);
/* Transfer references to the callee */ /* Transfer references to the callee */
err = transfer_reference_state(callee, caller); err = copy_reference_state(callee, caller);
if (err) if (err)
return err; return err;
...@@ -5780,7 +5782,7 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) ...@@ -5780,7 +5782,7 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
} }
/* Transfer references to the caller */ /* Transfer references to the caller */
err = transfer_reference_state(caller, callee); err = copy_reference_state(caller, callee);
if (err) if (err)
return err; return err;
...@@ -8913,12 +8915,14 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) ...@@ -8913,12 +8915,14 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)
mark_reg_known_zero(env, regs, insn->dst_reg); mark_reg_known_zero(env, regs, insn->dst_reg);
dst_reg->map_ptr = map; dst_reg->map_ptr = map;
if (insn->src_reg == BPF_PSEUDO_MAP_VALUE) { if (insn->src_reg == BPF_PSEUDO_MAP_VALUE ||
insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE) {
dst_reg->type = PTR_TO_MAP_VALUE; dst_reg->type = PTR_TO_MAP_VALUE;
dst_reg->off = aux->map_off; dst_reg->off = aux->map_off;
if (map_value_has_spin_lock(map)) if (map_value_has_spin_lock(map))
dst_reg->id = ++env->id_gen; dst_reg->id = ++env->id_gen;
} else if (insn->src_reg == BPF_PSEUDO_MAP_FD) { } else if (insn->src_reg == BPF_PSEUDO_MAP_FD ||
insn->src_reg == BPF_PSEUDO_MAP_IDX) {
dst_reg->type = CONST_PTR_TO_MAP; dst_reg->type = CONST_PTR_TO_MAP;
} else { } else {
verbose(env, "bpf verifier is misconfigured\n"); verbose(env, "bpf verifier is misconfigured\n");
...@@ -9434,7 +9438,7 @@ static int check_abnormal_return(struct bpf_verifier_env *env) ...@@ -9434,7 +9438,7 @@ static int check_abnormal_return(struct bpf_verifier_env *env)
static int check_btf_func(struct bpf_verifier_env *env, static int check_btf_func(struct bpf_verifier_env *env,
const union bpf_attr *attr, const union bpf_attr *attr,
union bpf_attr __user *uattr) bpfptr_t uattr)
{ {
const struct btf_type *type, *func_proto, *ret_type; const struct btf_type *type, *func_proto, *ret_type;
u32 i, nfuncs, urec_size, min_size; u32 i, nfuncs, urec_size, min_size;
...@@ -9443,7 +9447,7 @@ static int check_btf_func(struct bpf_verifier_env *env, ...@@ -9443,7 +9447,7 @@ static int check_btf_func(struct bpf_verifier_env *env,
struct bpf_func_info_aux *info_aux = NULL; struct bpf_func_info_aux *info_aux = NULL;
struct bpf_prog *prog; struct bpf_prog *prog;
const struct btf *btf; const struct btf *btf;
void __user *urecord; bpfptr_t urecord;
u32 prev_offset = 0; u32 prev_offset = 0;
bool scalar_return; bool scalar_return;
int ret = -ENOMEM; int ret = -ENOMEM;
...@@ -9471,7 +9475,7 @@ static int check_btf_func(struct bpf_verifier_env *env, ...@@ -9471,7 +9475,7 @@ static int check_btf_func(struct bpf_verifier_env *env,
prog = env->prog; prog = env->prog;
btf = prog->aux->btf; btf = prog->aux->btf;
urecord = u64_to_user_ptr(attr->func_info); urecord = make_bpfptr(attr->func_info, uattr.is_kernel);
min_size = min_t(u32, krec_size, urec_size); min_size = min_t(u32, krec_size, urec_size);
krecord = kvcalloc(nfuncs, krec_size, GFP_KERNEL | __GFP_NOWARN); krecord = kvcalloc(nfuncs, krec_size, GFP_KERNEL | __GFP_NOWARN);
...@@ -9489,13 +9493,15 @@ static int check_btf_func(struct bpf_verifier_env *env, ...@@ -9489,13 +9493,15 @@ static int check_btf_func(struct bpf_verifier_env *env,
/* set the size kernel expects so loader can zero /* set the size kernel expects so loader can zero
* out the rest of the record. * out the rest of the record.
*/ */
if (put_user(min_size, &uattr->func_info_rec_size)) if (copy_to_bpfptr_offset(uattr,
offsetof(union bpf_attr, func_info_rec_size),
&min_size, sizeof(min_size)))
ret = -EFAULT; ret = -EFAULT;
} }
goto err_free; goto err_free;
} }
if (copy_from_user(&krecord[i], urecord, min_size)) { if (copy_from_bpfptr(&krecord[i], urecord, min_size)) {
ret = -EFAULT; ret = -EFAULT;
goto err_free; goto err_free;
} }
...@@ -9547,7 +9553,7 @@ static int check_btf_func(struct bpf_verifier_env *env, ...@@ -9547,7 +9553,7 @@ static int check_btf_func(struct bpf_verifier_env *env,
} }
prev_offset = krecord[i].insn_off; prev_offset = krecord[i].insn_off;
urecord += urec_size; bpfptr_add(&urecord, urec_size);
} }
prog->aux->func_info = krecord; prog->aux->func_info = krecord;
...@@ -9579,14 +9585,14 @@ static void adjust_btf_func(struct bpf_verifier_env *env) ...@@ -9579,14 +9585,14 @@ static void adjust_btf_func(struct bpf_verifier_env *env)
static int check_btf_line(struct bpf_verifier_env *env, static int check_btf_line(struct bpf_verifier_env *env,
const union bpf_attr *attr, const union bpf_attr *attr,
union bpf_attr __user *uattr) bpfptr_t uattr)
{ {
u32 i, s, nr_linfo, ncopy, expected_size, rec_size, prev_offset = 0; u32 i, s, nr_linfo, ncopy, expected_size, rec_size, prev_offset = 0;
struct bpf_subprog_info *sub; struct bpf_subprog_info *sub;
struct bpf_line_info *linfo; struct bpf_line_info *linfo;
struct bpf_prog *prog; struct bpf_prog *prog;
const struct btf *btf; const struct btf *btf;
void __user *ulinfo; bpfptr_t ulinfo;
int err; int err;
nr_linfo = attr->line_info_cnt; nr_linfo = attr->line_info_cnt;
...@@ -9612,7 +9618,7 @@ static int check_btf_line(struct bpf_verifier_env *env, ...@@ -9612,7 +9618,7 @@ static int check_btf_line(struct bpf_verifier_env *env,
s = 0; s = 0;
sub = env->subprog_info; sub = env->subprog_info;
ulinfo = u64_to_user_ptr(attr->line_info); ulinfo = make_bpfptr(attr->line_info, uattr.is_kernel);
expected_size = sizeof(struct bpf_line_info); expected_size = sizeof(struct bpf_line_info);
ncopy = min_t(u32, expected_size, rec_size); ncopy = min_t(u32, expected_size, rec_size);
for (i = 0; i < nr_linfo; i++) { for (i = 0; i < nr_linfo; i++) {
...@@ -9620,14 +9626,15 @@ static int check_btf_line(struct bpf_verifier_env *env, ...@@ -9620,14 +9626,15 @@ static int check_btf_line(struct bpf_verifier_env *env,
if (err) { if (err) {
if (err == -E2BIG) { if (err == -E2BIG) {
verbose(env, "nonzero tailing record in line_info"); verbose(env, "nonzero tailing record in line_info");
if (put_user(expected_size, if (copy_to_bpfptr_offset(uattr,
&uattr->line_info_rec_size)) offsetof(union bpf_attr, line_info_rec_size),
&expected_size, sizeof(expected_size)))
err = -EFAULT; err = -EFAULT;
} }
goto err_free; goto err_free;
} }
if (copy_from_user(&linfo[i], ulinfo, ncopy)) { if (copy_from_bpfptr(&linfo[i], ulinfo, ncopy)) {
err = -EFAULT; err = -EFAULT;
goto err_free; goto err_free;
} }
...@@ -9679,7 +9686,7 @@ static int check_btf_line(struct bpf_verifier_env *env, ...@@ -9679,7 +9686,7 @@ static int check_btf_line(struct bpf_verifier_env *env,
} }
prev_offset = linfo[i].insn_off; prev_offset = linfo[i].insn_off;
ulinfo += rec_size; bpfptr_add(&ulinfo, rec_size);
} }
if (s != env->subprog_cnt) { if (s != env->subprog_cnt) {
...@@ -9701,7 +9708,7 @@ static int check_btf_line(struct bpf_verifier_env *env, ...@@ -9701,7 +9708,7 @@ static int check_btf_line(struct bpf_verifier_env *env,
static int check_btf_info(struct bpf_verifier_env *env, static int check_btf_info(struct bpf_verifier_env *env,
const union bpf_attr *attr, const union bpf_attr *attr,
union bpf_attr __user *uattr) bpfptr_t uattr)
{ {
struct btf *btf; struct btf *btf;
int err; int err;
...@@ -9746,13 +9753,6 @@ static bool range_within(struct bpf_reg_state *old, ...@@ -9746,13 +9753,6 @@ static bool range_within(struct bpf_reg_state *old,
old->s32_max_value >= cur->s32_max_value; old->s32_max_value >= cur->s32_max_value;
} }
/* Maximum number of register states that can exist at once */
#define ID_MAP_SIZE (MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE)
struct idpair {
u32 old;
u32 cur;
};
/* If in the old state two registers had the same id, then they need to have /* If in the old state two registers had the same id, then they need to have
* the same id in the new state as well. But that id could be different from * the same id in the new state as well. But that id could be different from
* the old state, so we need to track the mapping from old to new ids. * the old state, so we need to track the mapping from old to new ids.
...@@ -9763,11 +9763,11 @@ struct idpair { ...@@ -9763,11 +9763,11 @@ struct idpair {
* So we look through our idmap to see if this old id has been seen before. If * So we look through our idmap to see if this old id has been seen before. If
* so, we require the new id to match; otherwise, we add the id pair to the map. * so, we require the new id to match; otherwise, we add the id pair to the map.
*/ */
static bool check_ids(u32 old_id, u32 cur_id, struct idpair *idmap) static bool check_ids(u32 old_id, u32 cur_id, struct bpf_id_pair *idmap)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < ID_MAP_SIZE; i++) { for (i = 0; i < BPF_ID_MAP_SIZE; i++) {
if (!idmap[i].old) { if (!idmap[i].old) {
/* Reached an empty slot; haven't seen this id before */ /* Reached an empty slot; haven't seen this id before */
idmap[i].old = old_id; idmap[i].old = old_id;
...@@ -9880,7 +9880,7 @@ static void clean_live_states(struct bpf_verifier_env *env, int insn, ...@@ -9880,7 +9880,7 @@ static void clean_live_states(struct bpf_verifier_env *env, int insn,
/* Returns true if (rold safe implies rcur safe) */ /* Returns true if (rold safe implies rcur safe) */
static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur, static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
struct idpair *idmap) struct bpf_id_pair *idmap)
{ {
bool equal; bool equal;
...@@ -9998,7 +9998,7 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur, ...@@ -9998,7 +9998,7 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
static bool stacksafe(struct bpf_func_state *old, static bool stacksafe(struct bpf_func_state *old,
struct bpf_func_state *cur, struct bpf_func_state *cur,
struct idpair *idmap) struct bpf_id_pair *idmap)
{ {
int i, spi; int i, spi;
...@@ -10095,32 +10095,23 @@ static bool refsafe(struct bpf_func_state *old, struct bpf_func_state *cur) ...@@ -10095,32 +10095,23 @@ static bool refsafe(struct bpf_func_state *old, struct bpf_func_state *cur)
* whereas register type in current state is meaningful, it means that * whereas register type in current state is meaningful, it means that
* the current state will reach 'bpf_exit' instruction safely * the current state will reach 'bpf_exit' instruction safely
*/ */
static bool func_states_equal(struct bpf_func_state *old, static bool func_states_equal(struct bpf_verifier_env *env, struct bpf_func_state *old,
struct bpf_func_state *cur) struct bpf_func_state *cur)
{ {
struct idpair *idmap;
bool ret = false;
int i; int i;
idmap = kcalloc(ID_MAP_SIZE, sizeof(struct idpair), GFP_KERNEL); memset(env->idmap_scratch, 0, sizeof(env->idmap_scratch));
/* If we failed to allocate the idmap, just say it's not safe */ for (i = 0; i < MAX_BPF_REG; i++)
if (!idmap) if (!regsafe(&old->regs[i], &cur->regs[i], env->idmap_scratch))
return false; return false;
for (i = 0; i < MAX_BPF_REG; i++) {
if (!regsafe(&old->regs[i], &cur->regs[i], idmap))
goto out_free;
}
if (!stacksafe(old, cur, idmap)) if (!stacksafe(old, cur, env->idmap_scratch))
goto out_free; return false;
if (!refsafe(old, cur)) if (!refsafe(old, cur))
goto out_free; return false;
ret = true;
out_free: return true;
kfree(idmap);
return ret;
} }
static bool states_equal(struct bpf_verifier_env *env, static bool states_equal(struct bpf_verifier_env *env,
...@@ -10147,7 +10138,7 @@ static bool states_equal(struct bpf_verifier_env *env, ...@@ -10147,7 +10138,7 @@ static bool states_equal(struct bpf_verifier_env *env,
for (i = 0; i <= old->curframe; i++) { for (i = 0; i <= old->curframe; i++) {
if (old->frame[i]->callsite != cur->frame[i]->callsite) if (old->frame[i]->callsite != cur->frame[i]->callsite)
return false; return false;
if (!func_states_equal(old->frame[i], cur->frame[i])) if (!func_states_equal(env, old->frame[i], cur->frame[i]))
return false; return false;
} }
return true; return true;
...@@ -11184,6 +11175,7 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env) ...@@ -11184,6 +11175,7 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
struct bpf_map *map; struct bpf_map *map;
struct fd f; struct fd f;
u64 addr; u64 addr;
u32 fd;
if (i == insn_cnt - 1 || insn[1].code != 0 || if (i == insn_cnt - 1 || insn[1].code != 0 ||
insn[1].dst_reg != 0 || insn[1].src_reg != 0 || insn[1].dst_reg != 0 || insn[1].src_reg != 0 ||
...@@ -11213,16 +11205,38 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env) ...@@ -11213,16 +11205,38 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
/* In final convert_pseudo_ld_imm64() step, this is /* In final convert_pseudo_ld_imm64() step, this is
* converted into regular 64-bit imm load insn. * converted into regular 64-bit imm load insn.
*/ */
if ((insn[0].src_reg != BPF_PSEUDO_MAP_FD && switch (insn[0].src_reg) {
insn[0].src_reg != BPF_PSEUDO_MAP_VALUE) || case BPF_PSEUDO_MAP_VALUE:
(insn[0].src_reg == BPF_PSEUDO_MAP_FD && case BPF_PSEUDO_MAP_IDX_VALUE:
insn[1].imm != 0)) { break;
verbose(env, case BPF_PSEUDO_MAP_FD:
"unrecognized bpf_ld_imm64 insn\n"); case BPF_PSEUDO_MAP_IDX:
if (insn[1].imm == 0)
break;
fallthrough;
default:
verbose(env, "unrecognized bpf_ld_imm64 insn\n");
return -EINVAL; return -EINVAL;
} }
f = fdget(insn[0].imm); switch (insn[0].src_reg) {
case BPF_PSEUDO_MAP_IDX_VALUE:
case BPF_PSEUDO_MAP_IDX:
if (bpfptr_is_null(env->fd_array)) {
verbose(env, "fd_idx without fd_array is invalid\n");
return -EPROTO;
}
if (copy_from_bpfptr_offset(&fd, env->fd_array,
insn[0].imm * sizeof(fd),
sizeof(fd)))
return -EFAULT;
break;
default:
fd = insn[0].imm;
break;
}
f = fdget(fd);
map = __bpf_map_get(f); map = __bpf_map_get(f);
if (IS_ERR(map)) { if (IS_ERR(map)) {
verbose(env, "fd %d is not pointing to valid bpf_map\n", verbose(env, "fd %d is not pointing to valid bpf_map\n",
...@@ -11237,7 +11251,8 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env) ...@@ -11237,7 +11251,8 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
} }
aux = &env->insn_aux_data[i]; aux = &env->insn_aux_data[i];
if (insn->src_reg == BPF_PSEUDO_MAP_FD) { if (insn[0].src_reg == BPF_PSEUDO_MAP_FD ||
insn[0].src_reg == BPF_PSEUDO_MAP_IDX) {
addr = (unsigned long)map; addr = (unsigned long)map;
} else { } else {
u32 off = insn[1].imm; u32 off = insn[1].imm;
...@@ -13210,6 +13225,14 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -13210,6 +13225,14 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
int ret; int ret;
u64 key; u64 key;
if (prog->type == BPF_PROG_TYPE_SYSCALL) {
if (prog->aux->sleepable)
/* attach_btf_id checked to be zero already */
return 0;
verbose(env, "Syscall programs can only be sleepable\n");
return -EINVAL;
}
if (prog->aux->sleepable && prog->type != BPF_PROG_TYPE_TRACING && if (prog->aux->sleepable && prog->type != BPF_PROG_TYPE_TRACING &&
prog->type != BPF_PROG_TYPE_LSM) { prog->type != BPF_PROG_TYPE_LSM) {
verbose(env, "Only fentry/fexit/fmod_ret and lsm programs can be sleepable\n"); verbose(env, "Only fentry/fexit/fmod_ret and lsm programs can be sleepable\n");
...@@ -13281,8 +13304,7 @@ struct btf *bpf_get_btf_vmlinux(void) ...@@ -13281,8 +13304,7 @@ struct btf *bpf_get_btf_vmlinux(void)
return btf_vmlinux; return btf_vmlinux;
} }
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr)
union bpf_attr __user *uattr)
{ {
u64 start_time = ktime_get_ns(); u64 start_time = ktime_get_ns();
struct bpf_verifier_env *env; struct bpf_verifier_env *env;
...@@ -13312,6 +13334,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, ...@@ -13312,6 +13334,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
env->insn_aux_data[i].orig_idx = i; env->insn_aux_data[i].orig_idx = i;
env->prog = *prog; env->prog = *prog;
env->ops = bpf_verifier_ops[env->prog->type]; env->ops = bpf_verifier_ops[env->prog->type];
env->fd_array = make_bpfptr(attr->fd_array, uattr.is_kernel);
is_priv = bpf_capable(); is_priv = bpf_capable();
bpf_get_btf_vmlinux(); bpf_get_btf_vmlinux();
......
...@@ -409,7 +409,7 @@ static void *bpf_ctx_init(const union bpf_attr *kattr, u32 max_size) ...@@ -409,7 +409,7 @@ static void *bpf_ctx_init(const union bpf_attr *kattr, u32 max_size)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (data_in) { if (data_in) {
err = bpf_check_uarg_tail_zero(data_in, max_size, size); err = bpf_check_uarg_tail_zero(USER_BPFPTR(data_in), max_size, size);
if (err) { if (err) {
kfree(data); kfree(data);
return ERR_PTR(err); return ERR_PTR(err);
...@@ -918,3 +918,46 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat ...@@ -918,3 +918,46 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog, const union bpf_attr *kat
kfree(user_ctx); kfree(user_ctx);
return ret; return ret;
} }
int bpf_prog_test_run_syscall(struct bpf_prog *prog,
const union bpf_attr *kattr,
union bpf_attr __user *uattr)
{
void __user *ctx_in = u64_to_user_ptr(kattr->test.ctx_in);
__u32 ctx_size_in = kattr->test.ctx_size_in;
void *ctx = NULL;
u32 retval;
int err = 0;
/* doesn't support data_in/out, ctx_out, duration, or repeat or flags */
if (kattr->test.data_in || kattr->test.data_out ||
kattr->test.ctx_out || kattr->test.duration ||
kattr->test.repeat || kattr->test.flags)
return -EINVAL;
if (ctx_size_in < prog->aux->max_ctx_offset ||
ctx_size_in > U16_MAX)
return -EINVAL;
if (ctx_size_in) {
ctx = kzalloc(ctx_size_in, GFP_USER);
if (!ctx)
return -ENOMEM;
if (copy_from_user(ctx, ctx_in, ctx_size_in)) {
err = -EFAULT;
goto out;
}
}
retval = bpf_prog_run_pin_on_cpu(prog, ctx);
if (copy_to_user(&uattr->test.retval, &retval, sizeof(u32))) {
err = -EFAULT;
goto out;
}
if (ctx_size_in)
if (copy_to_user(ctx_in, ctx, ctx_size_in))
err = -EFAULT;
out:
kfree(ctx);
return err;
}
...@@ -3235,7 +3235,7 @@ static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len) ...@@ -3235,7 +3235,7 @@ static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len)
return ret; return ret;
} }
static int bpf_skb_proto_4_to_6(struct sk_buff *skb) static int bpf_skb_proto_4_to_6(struct sk_buff *skb, u64 flags)
{ {
const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr); const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
u32 off = skb_mac_header_len(skb); u32 off = skb_mac_header_len(skb);
...@@ -3264,7 +3264,9 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb) ...@@ -3264,7 +3264,9 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
} }
/* Due to IPv6 header, MSS needs to be downgraded. */ /* Due to IPv6 header, MSS needs to be downgraded. */
skb_decrease_gso_size(shinfo, len_diff); if (!(flags & BPF_F_ADJ_ROOM_FIXED_GSO))
skb_decrease_gso_size(shinfo, len_diff);
/* Header must be checked, and gso_segs recomputed. */ /* Header must be checked, and gso_segs recomputed. */
shinfo->gso_type |= SKB_GSO_DODGY; shinfo->gso_type |= SKB_GSO_DODGY;
shinfo->gso_segs = 0; shinfo->gso_segs = 0;
...@@ -3276,7 +3278,7 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb) ...@@ -3276,7 +3278,7 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
return 0; return 0;
} }
static int bpf_skb_proto_6_to_4(struct sk_buff *skb) static int bpf_skb_proto_6_to_4(struct sk_buff *skb, u64 flags)
{ {
const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr); const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
u32 off = skb_mac_header_len(skb); u32 off = skb_mac_header_len(skb);
...@@ -3305,7 +3307,9 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb) ...@@ -3305,7 +3307,9 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
} }
/* Due to IPv4 header, MSS can be upgraded. */ /* Due to IPv4 header, MSS can be upgraded. */
skb_increase_gso_size(shinfo, len_diff); if (!(flags & BPF_F_ADJ_ROOM_FIXED_GSO))
skb_increase_gso_size(shinfo, len_diff);
/* Header must be checked, and gso_segs recomputed. */ /* Header must be checked, and gso_segs recomputed. */
shinfo->gso_type |= SKB_GSO_DODGY; shinfo->gso_type |= SKB_GSO_DODGY;
shinfo->gso_segs = 0; shinfo->gso_segs = 0;
...@@ -3317,17 +3321,17 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb) ...@@ -3317,17 +3321,17 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
return 0; return 0;
} }
static int bpf_skb_proto_xlat(struct sk_buff *skb, __be16 to_proto) static int bpf_skb_proto_xlat(struct sk_buff *skb, __be16 to_proto, u64 flags)
{ {
__be16 from_proto = skb->protocol; __be16 from_proto = skb->protocol;
if (from_proto == htons(ETH_P_IP) && if (from_proto == htons(ETH_P_IP) &&
to_proto == htons(ETH_P_IPV6)) to_proto == htons(ETH_P_IPV6))
return bpf_skb_proto_4_to_6(skb); return bpf_skb_proto_4_to_6(skb, flags);
if (from_proto == htons(ETH_P_IPV6) && if (from_proto == htons(ETH_P_IPV6) &&
to_proto == htons(ETH_P_IP)) to_proto == htons(ETH_P_IP))
return bpf_skb_proto_6_to_4(skb); return bpf_skb_proto_6_to_4(skb, flags);
return -ENOTSUPP; return -ENOTSUPP;
} }
...@@ -3337,7 +3341,7 @@ BPF_CALL_3(bpf_skb_change_proto, struct sk_buff *, skb, __be16, proto, ...@@ -3337,7 +3341,7 @@ BPF_CALL_3(bpf_skb_change_proto, struct sk_buff *, skb, __be16, proto,
{ {
int ret; int ret;
if (unlikely(flags)) if (unlikely(flags & ~(BPF_F_ADJ_ROOM_FIXED_GSO)))
return -EINVAL; return -EINVAL;
/* General idea is that this helper does the basic groundwork /* General idea is that this helper does the basic groundwork
...@@ -3357,7 +3361,7 @@ BPF_CALL_3(bpf_skb_change_proto, struct sk_buff *, skb, __be16, proto, ...@@ -3357,7 +3361,7 @@ BPF_CALL_3(bpf_skb_change_proto, struct sk_buff *, skb, __be16, proto,
* that. For offloads, we mark packet as dodgy, so that headers * that. For offloads, we mark packet as dodgy, so that headers
* need to be verified first. * need to be verified first.
*/ */
ret = bpf_skb_proto_xlat(skb, proto); ret = bpf_skb_proto_xlat(skb, proto, flags);
bpf_compute_data_pointers(skb); bpf_compute_data_pointers(skb);
return ret; return ret;
} }
......
...@@ -399,8 +399,7 @@ int sk_msg_memcopy_from_iter(struct sock *sk, struct iov_iter *from, ...@@ -399,8 +399,7 @@ int sk_msg_memcopy_from_iter(struct sock *sk, struct iov_iter *from,
} }
EXPORT_SYMBOL_GPL(sk_msg_memcopy_from_iter); EXPORT_SYMBOL_GPL(sk_msg_memcopy_from_iter);
int sk_msg_wait_data(struct sock *sk, struct sk_psock *psock, int flags, int sk_msg_wait_data(struct sock *sk, struct sk_psock *psock, long timeo)
long timeo, int *err)
{ {
DEFINE_WAIT_FUNC(wait, woken_wake_function); DEFINE_WAIT_FUNC(wait, woken_wake_function);
int ret = 0; int ret = 0;
......
...@@ -184,11 +184,11 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, ...@@ -184,11 +184,11 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
msg_bytes_ready: msg_bytes_ready:
copied = sk_msg_recvmsg(sk, psock, msg, len, flags); copied = sk_msg_recvmsg(sk, psock, msg, len, flags);
if (!copied) { if (!copied) {
int data, err = 0;
long timeo; long timeo;
int data;
timeo = sock_rcvtimeo(sk, nonblock); timeo = sock_rcvtimeo(sk, nonblock);
data = sk_msg_wait_data(sk, psock, flags, timeo, &err); data = sk_msg_wait_data(sk, psock, timeo);
if (data) { if (data) {
if (!sk_psock_queue_empty(psock)) if (!sk_psock_queue_empty(psock))
goto msg_bytes_ready; goto msg_bytes_ready;
...@@ -196,14 +196,9 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, ...@@ -196,14 +196,9 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
sk_psock_put(sk, psock); sk_psock_put(sk, psock);
return tcp_recvmsg(sk, msg, len, nonblock, flags, addr_len); return tcp_recvmsg(sk, msg, len, nonblock, flags, addr_len);
} }
if (err) {
ret = err;
goto out;
}
copied = -EAGAIN; copied = -EAGAIN;
} }
ret = copied; ret = copied;
out:
release_sock(sk); release_sock(sk);
sk_psock_put(sk, psock); sk_psock_put(sk, psock);
return ret; return ret;
......
...@@ -43,21 +43,17 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, ...@@ -43,21 +43,17 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
msg_bytes_ready: msg_bytes_ready:
copied = sk_msg_recvmsg(sk, psock, msg, len, flags); copied = sk_msg_recvmsg(sk, psock, msg, len, flags);
if (!copied) { if (!copied) {
int data, err = 0;
long timeo; long timeo;
int data;
timeo = sock_rcvtimeo(sk, nonblock); timeo = sock_rcvtimeo(sk, nonblock);
data = sk_msg_wait_data(sk, psock, flags, timeo, &err); data = sk_msg_wait_data(sk, psock, timeo);
if (data) { if (data) {
if (!sk_psock_queue_empty(psock)) if (!sk_psock_queue_empty(psock))
goto msg_bytes_ready; goto msg_bytes_ready;
ret = sk_udp_recvmsg(sk, msg, len, nonblock, flags, addr_len); ret = sk_udp_recvmsg(sk, msg, len, nonblock, flags, addr_len);
goto out; goto out;
} }
if (err) {
ret = err;
goto out;
}
copied = -EAGAIN; copied = -EAGAIN;
} }
ret = copied; ret = copied;
......
...@@ -396,7 +396,7 @@ int main(int argc, char **argv) ...@@ -396,7 +396,7 @@ int main(int argc, char **argv)
* on different systems with different compilers. The right way is * on different systems with different compilers. The right way is
* to parse the ELF file. We took a shortcut here. * to parse the ELF file. We took a shortcut here.
*/ */
uprobe_file_offset = (__u64)main - (__u64)&__executable_start; uprobe_file_offset = (unsigned long)main - (unsigned long)&__executable_start;
CHECK_AND_RET(test_nondebug_fs_probe("uprobe", (char *)argv[0], CHECK_AND_RET(test_nondebug_fs_probe("uprobe", (char *)argv[0],
uprobe_file_offset, 0x0, false, uprobe_file_offset, 0x0, false,
BPF_FD_TYPE_UPROBE, BPF_FD_TYPE_UPROBE,
......
...@@ -136,7 +136,7 @@ endif ...@@ -136,7 +136,7 @@ endif
BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o) BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o xlated_dumper.o btf_dumper.o) $(OUTPUT)disasm.o
OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \ VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <bpf/btf.h> #include <bpf/btf.h>
#include <bpf/bpf_gen_internal.h>
#include "json_writer.h" #include "json_writer.h"
#include "main.h" #include "main.h"
...@@ -106,8 +107,10 @@ static int codegen_datasec_def(struct bpf_object *obj, ...@@ -106,8 +107,10 @@ static int codegen_datasec_def(struct bpf_object *obj,
if (strcmp(sec_name, ".data") == 0) { if (strcmp(sec_name, ".data") == 0) {
sec_ident = "data"; sec_ident = "data";
strip_mods = true;
} else if (strcmp(sec_name, ".bss") == 0) { } else if (strcmp(sec_name, ".bss") == 0) {
sec_ident = "bss"; sec_ident = "bss";
strip_mods = true;
} else if (strcmp(sec_name, ".rodata") == 0) { } else if (strcmp(sec_name, ".rodata") == 0) {
sec_ident = "rodata"; sec_ident = "rodata";
strip_mods = true; strip_mods = true;
...@@ -129,6 +132,10 @@ static int codegen_datasec_def(struct bpf_object *obj, ...@@ -129,6 +132,10 @@ static int codegen_datasec_def(struct bpf_object *obj,
int need_off = sec_var->offset, align_off, align; int need_off = sec_var->offset, align_off, align;
__u32 var_type_id = var->type; __u32 var_type_id = var->type;
/* static variables are not exposed through BPF skeleton */
if (btf_var(var)->linkage == BTF_VAR_STATIC)
continue;
if (off > need_off) { if (off > need_off) {
p_err("Something is wrong for %s's variable #%d: need offset %d, already at %d.\n", p_err("Something is wrong for %s's variable #%d: need offset %d, already at %d.\n",
sec_name, i, need_off, off); sec_name, i, need_off, off);
...@@ -268,6 +275,327 @@ static void codegen(const char *template, ...) ...@@ -268,6 +275,327 @@ static void codegen(const char *template, ...)
free(s); free(s);
} }
static void print_hex(const char *data, int data_sz)
{
int i, len;
for (i = 0, len = 0; i < data_sz; i++) {
int w = data[i] ? 4 : 2;
len += w;
if (len > 78) {
printf("\\\n");
len = w;
}
if (!data[i])
printf("\\0");
else
printf("\\x%02x", (unsigned char)data[i]);
}
}
static size_t bpf_map_mmap_sz(const struct bpf_map *map)
{
long page_sz = sysconf(_SC_PAGE_SIZE);
size_t map_sz;
map_sz = (size_t)roundup(bpf_map__value_size(map), 8) * bpf_map__max_entries(map);
map_sz = roundup(map_sz, page_sz);
return map_sz;
}
static void codegen_attach_detach(struct bpf_object *obj, const char *obj_name)
{
struct bpf_program *prog;
bpf_object__for_each_program(prog, obj) {
const char *tp_name;
codegen("\
\n\
\n\
static inline int \n\
%1$s__%2$s__attach(struct %1$s *skel) \n\
{ \n\
int prog_fd = skel->progs.%2$s.prog_fd; \n\
", obj_name, bpf_program__name(prog));
switch (bpf_program__get_type(prog)) {
case BPF_PROG_TYPE_RAW_TRACEPOINT:
tp_name = strchr(bpf_program__section_name(prog), '/') + 1;
printf("\tint fd = bpf_raw_tracepoint_open(\"%s\", prog_fd);\n", tp_name);
break;
case BPF_PROG_TYPE_TRACING:
printf("\tint fd = bpf_raw_tracepoint_open(NULL, prog_fd);\n");
break;
default:
printf("\tint fd = ((void)prog_fd, 0); /* auto-attach not supported */\n");
break;
}
codegen("\
\n\
\n\
if (fd > 0) \n\
skel->links.%1$s_fd = fd; \n\
return fd; \n\
} \n\
", bpf_program__name(prog));
}
codegen("\
\n\
\n\
static inline int \n\
%1$s__attach(struct %1$s *skel) \n\
{ \n\
int ret = 0; \n\
\n\
", obj_name);
bpf_object__for_each_program(prog, obj) {
codegen("\
\n\
ret = ret < 0 ? ret : %1$s__%2$s__attach(skel); \n\
", obj_name, bpf_program__name(prog));
}
codegen("\
\n\
return ret < 0 ? ret : 0; \n\
} \n\
\n\
static inline void \n\
%1$s__detach(struct %1$s *skel) \n\
{ \n\
", obj_name);
bpf_object__for_each_program(prog, obj) {
codegen("\
\n\
skel_closenz(skel->links.%1$s_fd); \n\
", bpf_program__name(prog));
}
codegen("\
\n\
} \n\
");
}
static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
{
struct bpf_program *prog;
struct bpf_map *map;
codegen("\
\n\
static void \n\
%1$s__destroy(struct %1$s *skel) \n\
{ \n\
if (!skel) \n\
return; \n\
%1$s__detach(skel); \n\
",
obj_name);
bpf_object__for_each_program(prog, obj) {
codegen("\
\n\
skel_closenz(skel->progs.%1$s.prog_fd); \n\
", bpf_program__name(prog));
}
bpf_object__for_each_map(map, obj) {
const char * ident;
ident = get_map_ident(map);
if (!ident)
continue;
if (bpf_map__is_internal(map) &&
(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE))
printf("\tmunmap(skel->%1$s, %2$zd);\n",
ident, bpf_map_mmap_sz(map));
codegen("\
\n\
skel_closenz(skel->maps.%1$s.map_fd); \n\
", ident);
}
codegen("\
\n\
free(skel); \n\
} \n\
",
obj_name);
}
static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard)
{
struct bpf_object_load_attr load_attr = {};
DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
struct bpf_map *map;
int err = 0;
err = bpf_object__gen_loader(obj, &opts);
if (err)
return err;
load_attr.obj = obj;
if (verifier_logs)
/* log_level1 + log_level2 + stats, but not stable UAPI */
load_attr.log_level = 1 + 2 + 4;
err = bpf_object__load_xattr(&load_attr);
if (err) {
p_err("failed to load object file");
goto out;
}
/* If there was no error during load then gen_loader_opts
* are populated with the loader program.
*/
/* finish generating 'struct skel' */
codegen("\
\n\
}; \n\
", obj_name);
codegen_attach_detach(obj, obj_name);
codegen_destroy(obj, obj_name);
codegen("\
\n\
static inline struct %1$s * \n\
%1$s__open(void) \n\
{ \n\
struct %1$s *skel; \n\
\n\
skel = calloc(sizeof(*skel), 1); \n\
if (!skel) \n\
goto cleanup; \n\
skel->ctx.sz = (void *)&skel->links - (void *)skel; \n\
",
obj_name, opts.data_sz);
bpf_object__for_each_map(map, obj) {
const char *ident;
const void *mmap_data = NULL;
size_t mmap_size = 0;
ident = get_map_ident(map);
if (!ident)
continue;
if (!bpf_map__is_internal(map) ||
!(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE))
continue;
codegen("\
\n\
skel->%1$s = \n\
mmap(NULL, %2$zd, PROT_READ | PROT_WRITE,\n\
MAP_SHARED | MAP_ANONYMOUS, -1, 0); \n\
if (skel->%1$s == (void *) -1) \n\
goto cleanup; \n\
memcpy(skel->%1$s, (void *)\"\\ \n\
", ident, bpf_map_mmap_sz(map));
mmap_data = bpf_map__initial_value(map, &mmap_size);
print_hex(mmap_data, mmap_size);
printf("\", %2$zd);\n"
"\tskel->maps.%1$s.initial_value = (__u64)(long)skel->%1$s;\n",
ident, mmap_size);
}
codegen("\
\n\
return skel; \n\
cleanup: \n\
%1$s__destroy(skel); \n\
return NULL; \n\
} \n\
\n\
static inline int \n\
%1$s__load(struct %1$s *skel) \n\
{ \n\
struct bpf_load_and_run_opts opts = {}; \n\
int err; \n\
\n\
opts.ctx = (struct bpf_loader_ctx *)skel; \n\
opts.data_sz = %2$d; \n\
opts.data = (void *)\"\\ \n\
",
obj_name, opts.data_sz);
print_hex(opts.data, opts.data_sz);
codegen("\
\n\
\"; \n\
");
codegen("\
\n\
opts.insns_sz = %d; \n\
opts.insns = (void *)\"\\ \n\
",
opts.insns_sz);
print_hex(opts.insns, opts.insns_sz);
codegen("\
\n\
\"; \n\
err = bpf_load_and_run(&opts); \n\
if (err < 0) \n\
return err; \n\
", obj_name);
bpf_object__for_each_map(map, obj) {
const char *ident, *mmap_flags;
ident = get_map_ident(map);
if (!ident)
continue;
if (!bpf_map__is_internal(map) ||
!(bpf_map__def(map)->map_flags & BPF_F_MMAPABLE))
continue;
if (bpf_map__def(map)->map_flags & BPF_F_RDONLY_PROG)
mmap_flags = "PROT_READ";
else
mmap_flags = "PROT_READ | PROT_WRITE";
printf("\tskel->%1$s =\n"
"\t\tmmap(skel->%1$s, %2$zd, %3$s, MAP_SHARED | MAP_FIXED,\n"
"\t\t\tskel->maps.%1$s.map_fd, 0);\n",
ident, bpf_map_mmap_sz(map), mmap_flags);
}
codegen("\
\n\
return 0; \n\
} \n\
\n\
static inline struct %1$s * \n\
%1$s__open_and_load(void) \n\
{ \n\
struct %1$s *skel; \n\
\n\
skel = %1$s__open(); \n\
if (!skel) \n\
return NULL; \n\
if (%1$s__load(skel)) { \n\
%1$s__destroy(skel); \n\
return NULL; \n\
} \n\
return skel; \n\
} \n\
", obj_name);
codegen("\
\n\
\n\
#endif /* %s */ \n\
",
header_guard);
err = 0;
out:
return err;
}
static int do_skeleton(int argc, char **argv) static int do_skeleton(int argc, char **argv)
{ {
char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")]; char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
...@@ -277,7 +605,7 @@ static int do_skeleton(int argc, char **argv) ...@@ -277,7 +605,7 @@ static int do_skeleton(int argc, char **argv)
struct bpf_object *obj = NULL; struct bpf_object *obj = NULL;
const char *file, *ident; const char *file, *ident;
struct bpf_program *prog; struct bpf_program *prog;
int fd, len, err = -1; int fd, err = -1;
struct bpf_map *map; struct bpf_map *map;
struct btf *btf; struct btf *btf;
struct stat st; struct stat st;
...@@ -359,7 +687,25 @@ static int do_skeleton(int argc, char **argv) ...@@ -359,7 +687,25 @@ static int do_skeleton(int argc, char **argv)
} }
get_header_guard(header_guard, obj_name); get_header_guard(header_guard, obj_name);
codegen("\ if (use_loader) {
codegen("\
\n\
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ \n\
/* THIS FILE IS AUTOGENERATED! */ \n\
#ifndef %2$s \n\
#define %2$s \n\
\n\
#include <stdlib.h> \n\
#include <bpf/bpf.h> \n\
#include <bpf/skel_internal.h> \n\
\n\
struct %1$s { \n\
struct bpf_loader_ctx ctx; \n\
",
obj_name, header_guard
);
} else {
codegen("\
\n\ \n\
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ \n\ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ \n\
\n\ \n\
...@@ -375,7 +721,8 @@ static int do_skeleton(int argc, char **argv) ...@@ -375,7 +721,8 @@ static int do_skeleton(int argc, char **argv)
struct bpf_object *obj; \n\ struct bpf_object *obj; \n\
", ",
obj_name, header_guard obj_name, header_guard
); );
}
if (map_cnt) { if (map_cnt) {
printf("\tstruct {\n"); printf("\tstruct {\n");
...@@ -383,7 +730,10 @@ static int do_skeleton(int argc, char **argv) ...@@ -383,7 +730,10 @@ static int do_skeleton(int argc, char **argv)
ident = get_map_ident(map); ident = get_map_ident(map);
if (!ident) if (!ident)
continue; continue;
printf("\t\tstruct bpf_map *%s;\n", ident); if (use_loader)
printf("\t\tstruct bpf_map_desc %s;\n", ident);
else
printf("\t\tstruct bpf_map *%s;\n", ident);
} }
printf("\t} maps;\n"); printf("\t} maps;\n");
} }
...@@ -391,14 +741,22 @@ static int do_skeleton(int argc, char **argv) ...@@ -391,14 +741,22 @@ static int do_skeleton(int argc, char **argv)
if (prog_cnt) { if (prog_cnt) {
printf("\tstruct {\n"); printf("\tstruct {\n");
bpf_object__for_each_program(prog, obj) { bpf_object__for_each_program(prog, obj) {
printf("\t\tstruct bpf_program *%s;\n", if (use_loader)
bpf_program__name(prog)); printf("\t\tstruct bpf_prog_desc %s;\n",
bpf_program__name(prog));
else
printf("\t\tstruct bpf_program *%s;\n",
bpf_program__name(prog));
} }
printf("\t} progs;\n"); printf("\t} progs;\n");
printf("\tstruct {\n"); printf("\tstruct {\n");
bpf_object__for_each_program(prog, obj) { bpf_object__for_each_program(prog, obj) {
printf("\t\tstruct bpf_link *%s;\n", if (use_loader)
bpf_program__name(prog)); printf("\t\tint %s_fd;\n",
bpf_program__name(prog));
else
printf("\t\tstruct bpf_link *%s;\n",
bpf_program__name(prog));
} }
printf("\t} links;\n"); printf("\t} links;\n");
} }
...@@ -409,6 +767,10 @@ static int do_skeleton(int argc, char **argv) ...@@ -409,6 +767,10 @@ static int do_skeleton(int argc, char **argv)
if (err) if (err)
goto out; goto out;
} }
if (use_loader) {
err = gen_trace(obj, obj_name, header_guard);
goto out;
}
codegen("\ codegen("\
\n\ \n\
...@@ -578,19 +940,7 @@ static int do_skeleton(int argc, char **argv) ...@@ -578,19 +940,7 @@ static int do_skeleton(int argc, char **argv)
file_sz); file_sz);
/* embed contents of BPF object file */ /* embed contents of BPF object file */
for (i = 0, len = 0; i < file_sz; i++) { print_hex(obj_data, file_sz);
int w = obj_data[i] ? 4 : 2;
len += w;
if (len > 78) {
printf("\\\n");
len = w;
}
if (!obj_data[i])
printf("\\0");
else
printf("\\x%02x", (unsigned char)obj_data[i]);
}
codegen("\ codegen("\
\n\ \n\
...@@ -636,7 +986,7 @@ static int do_object(int argc, char **argv) ...@@ -636,7 +986,7 @@ static int do_object(int argc, char **argv)
while (argc) { while (argc) {
file = GET_ARG(); file = GET_ARG();
err = bpf_linker__add_file(linker, file); err = bpf_linker__add_file(linker, file, NULL);
if (err) { if (err) {
p_err("failed to link '%s': %s (%d)", file, strerror(err), err); p_err("failed to link '%s': %s (%d)", file, strerror(err), err);
goto out; goto out;
......
...@@ -29,6 +29,7 @@ bool show_pinned; ...@@ -29,6 +29,7 @@ bool show_pinned;
bool block_mount; bool block_mount;
bool verifier_logs; bool verifier_logs;
bool relaxed_maps; bool relaxed_maps;
bool use_loader;
struct btf *base_btf; struct btf *base_btf;
struct pinned_obj_table prog_table; struct pinned_obj_table prog_table;
struct pinned_obj_table map_table; struct pinned_obj_table map_table;
...@@ -392,6 +393,7 @@ int main(int argc, char **argv) ...@@ -392,6 +393,7 @@ int main(int argc, char **argv)
{ "mapcompat", no_argument, NULL, 'm' }, { "mapcompat", no_argument, NULL, 'm' },
{ "nomount", no_argument, NULL, 'n' }, { "nomount", no_argument, NULL, 'n' },
{ "debug", no_argument, NULL, 'd' }, { "debug", no_argument, NULL, 'd' },
{ "use-loader", no_argument, NULL, 'L' },
{ "base-btf", required_argument, NULL, 'B' }, { "base-btf", required_argument, NULL, 'B' },
{ 0 } { 0 }
}; };
...@@ -409,7 +411,7 @@ int main(int argc, char **argv) ...@@ -409,7 +411,7 @@ int main(int argc, char **argv)
hash_init(link_table.table); hash_init(link_table.table);
opterr = 0; opterr = 0;
while ((opt = getopt_long(argc, argv, "VhpjfmndB:", while ((opt = getopt_long(argc, argv, "VhpjfLmndB:",
options, NULL)) >= 0) { options, NULL)) >= 0) {
switch (opt) { switch (opt) {
case 'V': case 'V':
...@@ -452,6 +454,9 @@ int main(int argc, char **argv) ...@@ -452,6 +454,9 @@ int main(int argc, char **argv)
return -1; return -1;
} }
break; break;
case 'L':
use_loader = true;
break;
default: default:
p_err("unrecognized option '%s'", argv[optind - 1]); p_err("unrecognized option '%s'", argv[optind - 1]);
if (json_output) if (json_output)
......
...@@ -90,6 +90,7 @@ extern bool show_pids; ...@@ -90,6 +90,7 @@ extern bool show_pids;
extern bool block_mount; extern bool block_mount;
extern bool verifier_logs; extern bool verifier_logs;
extern bool relaxed_maps; extern bool relaxed_maps;
extern bool use_loader;
extern struct btf *base_btf; extern struct btf *base_btf;
extern struct pinned_obj_table prog_table; extern struct pinned_obj_table prog_table;
extern struct pinned_obj_table map_table; extern struct pinned_obj_table map_table;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <dirent.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
...@@ -24,6 +25,8 @@ ...@@ -24,6 +25,8 @@
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include <bpf/btf.h> #include <bpf/btf.h>
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include <bpf/bpf_gen_internal.h>
#include <bpf/skel_internal.h>
#include "cfg.h" #include "cfg.h"
#include "main.h" #include "main.h"
...@@ -1499,7 +1502,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) ...@@ -1499,7 +1502,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
set_max_rlimit(); set_max_rlimit();
obj = bpf_object__open_file(file, &open_opts); obj = bpf_object__open_file(file, &open_opts);
if (IS_ERR_OR_NULL(obj)) { if (libbpf_get_error(obj)) {
p_err("failed to open object file"); p_err("failed to open object file");
goto err_free_reuse_maps; goto err_free_reuse_maps;
} }
...@@ -1645,8 +1648,110 @@ static int load_with_options(int argc, char **argv, bool first_prog_only) ...@@ -1645,8 +1648,110 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
return -1; return -1;
} }
static int count_open_fds(void)
{
DIR *dp = opendir("/proc/self/fd");
struct dirent *de;
int cnt = -3;
if (!dp)
return -1;
while ((de = readdir(dp)))
cnt++;
closedir(dp);
return cnt;
}
static int try_loader(struct gen_loader_opts *gen)
{
struct bpf_load_and_run_opts opts = {};
struct bpf_loader_ctx *ctx;
int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
sizeof(struct bpf_prog_desc));
int log_buf_sz = (1u << 24) - 1;
int err, fds_before, fd_delta;
char *log_buf;
ctx = alloca(ctx_sz);
memset(ctx, 0, ctx_sz);
ctx->sz = ctx_sz;
ctx->log_level = 1;
ctx->log_size = log_buf_sz;
log_buf = malloc(log_buf_sz);
if (!log_buf)
return -ENOMEM;
ctx->log_buf = (long) log_buf;
opts.ctx = ctx;
opts.data = gen->data;
opts.data_sz = gen->data_sz;
opts.insns = gen->insns;
opts.insns_sz = gen->insns_sz;
fds_before = count_open_fds();
err = bpf_load_and_run(&opts);
fd_delta = count_open_fds() - fds_before;
if (err < 0) {
fprintf(stderr, "err %d\n%s\n%s", err, opts.errstr, log_buf);
if (fd_delta)
fprintf(stderr, "loader prog leaked %d FDs\n",
fd_delta);
}
free(log_buf);
return err;
}
static int do_loader(int argc, char **argv)
{
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts);
DECLARE_LIBBPF_OPTS(gen_loader_opts, gen);
struct bpf_object_load_attr load_attr = {};
struct bpf_object *obj;
const char *file;
int err = 0;
if (!REQ_ARGS(1))
return -1;
file = GET_ARG();
obj = bpf_object__open_file(file, &open_opts);
if (libbpf_get_error(obj)) {
p_err("failed to open object file");
goto err_close_obj;
}
err = bpf_object__gen_loader(obj, &gen);
if (err)
goto err_close_obj;
load_attr.obj = obj;
if (verifier_logs)
/* log_level1 + log_level2 + stats, but not stable UAPI */
load_attr.log_level = 1 + 2 + 4;
err = bpf_object__load_xattr(&load_attr);
if (err) {
p_err("failed to load object file");
goto err_close_obj;
}
if (verifier_logs) {
struct dump_data dd = {};
kernel_syms_load(&dd);
dump_xlated_plain(&dd, (void *)gen.insns, gen.insns_sz, false, false);
kernel_syms_destroy(&dd);
}
err = try_loader(&gen);
err_close_obj:
bpf_object__close(obj);
return err;
}
static int do_load(int argc, char **argv) static int do_load(int argc, char **argv)
{ {
if (use_loader)
return do_loader(argc, argv);
return load_with_options(argc, argv, true); return load_with_options(argc, argv, true);
} }
......
...@@ -196,6 +196,9 @@ static const char *print_imm(void *private_data, ...@@ -196,6 +196,9 @@ static const char *print_imm(void *private_data,
else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE) else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"map[id:%u][0]+%u", insn->imm, (insn + 1)->imm); "map[id:%u][0]+%u", insn->imm, (insn + 1)->imm);
else if (insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"map[idx:%u]+%u", insn->imm, (insn + 1)->imm);
else if (insn->src_reg == BPF_PSEUDO_FUNC) else if (insn->src_reg == BPF_PSEUDO_FUNC)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"subprog[%+d]", insn->imm); "subprog[%+d]", insn->imm);
......
...@@ -837,6 +837,7 @@ enum bpf_cmd { ...@@ -837,6 +837,7 @@ enum bpf_cmd {
BPF_PROG_ATTACH, BPF_PROG_ATTACH,
BPF_PROG_DETACH, BPF_PROG_DETACH,
BPF_PROG_TEST_RUN, BPF_PROG_TEST_RUN,
BPF_PROG_RUN = BPF_PROG_TEST_RUN,
BPF_PROG_GET_NEXT_ID, BPF_PROG_GET_NEXT_ID,
BPF_MAP_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID,
BPF_PROG_GET_FD_BY_ID, BPF_PROG_GET_FD_BY_ID,
...@@ -937,6 +938,7 @@ enum bpf_prog_type { ...@@ -937,6 +938,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_EXT, BPF_PROG_TYPE_EXT,
BPF_PROG_TYPE_LSM, BPF_PROG_TYPE_LSM,
BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
}; };
enum bpf_attach_type { enum bpf_attach_type {
...@@ -1097,8 +1099,8 @@ enum bpf_link_type { ...@@ -1097,8 +1099,8 @@ enum bpf_link_type {
/* When BPF ldimm64's insn[0].src_reg != 0 then this can have /* When BPF ldimm64's insn[0].src_reg != 0 then this can have
* the following extensions: * the following extensions:
* *
* insn[0].src_reg: BPF_PSEUDO_MAP_FD * insn[0].src_reg: BPF_PSEUDO_MAP_[FD|IDX]
* insn[0].imm: map fd * insn[0].imm: map fd or fd_idx
* insn[1].imm: 0 * insn[1].imm: 0
* insn[0].off: 0 * insn[0].off: 0
* insn[1].off: 0 * insn[1].off: 0
...@@ -1106,15 +1108,19 @@ enum bpf_link_type { ...@@ -1106,15 +1108,19 @@ enum bpf_link_type {
* verifier type: CONST_PTR_TO_MAP * verifier type: CONST_PTR_TO_MAP
*/ */
#define BPF_PSEUDO_MAP_FD 1 #define BPF_PSEUDO_MAP_FD 1
/* insn[0].src_reg: BPF_PSEUDO_MAP_VALUE #define BPF_PSEUDO_MAP_IDX 5
* insn[0].imm: map fd
/* insn[0].src_reg: BPF_PSEUDO_MAP_[IDX_]VALUE
* insn[0].imm: map fd or fd_idx
* insn[1].imm: offset into value * insn[1].imm: offset into value
* insn[0].off: 0 * insn[0].off: 0
* insn[1].off: 0 * insn[1].off: 0
* ldimm64 rewrite: address of map[0]+offset * ldimm64 rewrite: address of map[0]+offset
* verifier type: PTR_TO_MAP_VALUE * verifier type: PTR_TO_MAP_VALUE
*/ */
#define BPF_PSEUDO_MAP_VALUE 2 #define BPF_PSEUDO_MAP_VALUE 2
#define BPF_PSEUDO_MAP_IDX_VALUE 6
/* insn[0].src_reg: BPF_PSEUDO_BTF_ID /* insn[0].src_reg: BPF_PSEUDO_BTF_ID
* insn[0].imm: kernel btd id of VAR * insn[0].imm: kernel btd id of VAR
* insn[1].imm: 0 * insn[1].imm: 0
...@@ -1314,6 +1320,8 @@ union bpf_attr { ...@@ -1314,6 +1320,8 @@ union bpf_attr {
/* or valid module BTF object fd or 0 to attach to vmlinux */ /* or valid module BTF object fd or 0 to attach to vmlinux */
__u32 attach_btf_obj_fd; __u32 attach_btf_obj_fd;
}; };
__u32 :32; /* pad */
__aligned_u64 fd_array; /* array of FDs */
}; };
struct { /* anonymous struct used by BPF_OBJ_* commands */ struct { /* anonymous struct used by BPF_OBJ_* commands */
...@@ -4735,6 +4743,24 @@ union bpf_attr { ...@@ -4735,6 +4743,24 @@ union bpf_attr {
* be zero-terminated except when **str_size** is 0. * be zero-terminated except when **str_size** is 0.
* *
* Or **-EBUSY** if the per-CPU memory copy buffer is busy. * Or **-EBUSY** if the per-CPU memory copy buffer is busy.
*
* long bpf_sys_bpf(u32 cmd, void *attr, u32 attr_size)
* Description
* Execute bpf syscall with given arguments.
* Return
* A syscall result.
*
* long bpf_btf_find_by_name_kind(char *name, int name_sz, u32 kind, int flags)
* Description
* Find BTF type with given name and kind in vmlinux BTF or in module's BTFs.
* Return
* Returns btf_id and btf_obj_fd in lower and upper 32 bits.
*
* long bpf_sys_close(u32 fd)
* Description
* Execute close syscall for given FD.
* Return
* A syscall result.
*/ */
#define __BPF_FUNC_MAPPER(FN) \ #define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \ FN(unspec), \
...@@ -4903,6 +4929,9 @@ union bpf_attr { ...@@ -4903,6 +4929,9 @@ union bpf_attr {
FN(check_mtu), \ FN(check_mtu), \
FN(for_each_map_elem), \ FN(for_each_map_elem), \
FN(snprintf), \ FN(snprintf), \
FN(sys_bpf), \
FN(btf_find_by_name_kind), \
FN(sys_close), \
/* */ /* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper /* integer value in 'imm' field of BPF_CALL instruction selects which helper
......
libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \ libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \
netlink.o bpf_prog_linfo.o libbpf_probes.o xsk.o hashmap.o \ netlink.o bpf_prog_linfo.o libbpf_probes.o xsk.o hashmap.o \
btf_dump.o ringbuf.o strset.o linker.o btf_dump.o ringbuf.o strset.o linker.o gen_loader.o
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* Copyright (c) 2021 Facebook */
#ifndef __BPF_GEN_INTERNAL_H
#define __BPF_GEN_INTERNAL_H
struct ksym_relo_desc {
const char *name;
int kind;
int insn_idx;
};
struct bpf_gen {
struct gen_loader_opts *opts;
void *data_start;
void *data_cur;
void *insn_start;
void *insn_cur;
ssize_t cleanup_label;
__u32 nr_progs;
__u32 nr_maps;
int log_level;
int error;
struct ksym_relo_desc *relos;
int relo_cnt;
char attach_target[128];
int attach_kind;
};
void bpf_gen__init(struct bpf_gen *gen, int log_level);
int bpf_gen__finish(struct bpf_gen *gen);
void bpf_gen__free(struct bpf_gen *gen);
void bpf_gen__load_btf(struct bpf_gen *gen, const void *raw_data, __u32 raw_size);
void bpf_gen__map_create(struct bpf_gen *gen, struct bpf_create_map_attr *map_attr, int map_idx);
struct bpf_prog_load_params;
void bpf_gen__prog_load(struct bpf_gen *gen, struct bpf_prog_load_params *load_attr, int prog_idx);
void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *value, __u32 value_size);
void bpf_gen__map_freeze(struct bpf_gen *gen, int map_idx);
void bpf_gen__record_attach_target(struct bpf_gen *gen, const char *name, enum bpf_attach_type type);
void bpf_gen__record_extern(struct bpf_gen *gen, const char *name, int kind, int insn_idx);
#endif
此差异已折叠。
此差异已折叠。
...@@ -471,6 +471,7 @@ LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv, ...@@ -471,6 +471,7 @@ LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv,
LIBBPF_API void *bpf_map__priv(const struct bpf_map *map); LIBBPF_API void *bpf_map__priv(const struct bpf_map *map);
LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map, LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map,
const void *data, size_t size); const void *data, size_t size);
LIBBPF_API const void *bpf_map__initial_value(struct bpf_map *map, size_t *psize);
LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map); LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map);
LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map); LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map);
LIBBPF_API int bpf_map__set_pin_path(struct bpf_map *map, const char *path); LIBBPF_API int bpf_map__set_pin_path(struct bpf_map *map, const char *path);
...@@ -498,6 +499,7 @@ LIBBPF_API int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, ...@@ -498,6 +499,7 @@ LIBBPF_API int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type, LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type,
struct bpf_object **pobj, int *prog_fd); struct bpf_object **pobj, int *prog_fd);
/* XDP related API */
struct xdp_link_info { struct xdp_link_info {
__u32 prog_id; __u32 prog_id;
__u32 drv_prog_id; __u32 drv_prog_id;
...@@ -520,6 +522,49 @@ LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags); ...@@ -520,6 +522,49 @@ LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
size_t info_size, __u32 flags); size_t info_size, __u32 flags);
/* TC related API */
enum bpf_tc_attach_point {
BPF_TC_INGRESS = 1 << 0,
BPF_TC_EGRESS = 1 << 1,
BPF_TC_CUSTOM = 1 << 2,
};
#define BPF_TC_PARENT(a, b) \
((((a) << 16) & 0xFFFF0000U) | ((b) & 0x0000FFFFU))
enum bpf_tc_flags {
BPF_TC_F_REPLACE = 1 << 0,
};
struct bpf_tc_hook {
size_t sz;
int ifindex;
enum bpf_tc_attach_point attach_point;
__u32 parent;
size_t :0;
};
#define bpf_tc_hook__last_field parent
struct bpf_tc_opts {
size_t sz;
int prog_fd;
__u32 flags;
__u32 prog_id;
__u32 handle;
__u32 priority;
size_t :0;
};
#define bpf_tc_opts__last_field priority
LIBBPF_API int bpf_tc_hook_create(struct bpf_tc_hook *hook);
LIBBPF_API int bpf_tc_hook_destroy(struct bpf_tc_hook *hook);
LIBBPF_API int bpf_tc_attach(const struct bpf_tc_hook *hook,
struct bpf_tc_opts *opts);
LIBBPF_API int bpf_tc_detach(const struct bpf_tc_hook *hook,
const struct bpf_tc_opts *opts);
LIBBPF_API int bpf_tc_query(const struct bpf_tc_hook *hook,
struct bpf_tc_opts *opts);
/* Ring buffer APIs */ /* Ring buffer APIs */
struct ring_buffer; struct ring_buffer;
...@@ -756,6 +801,18 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s); ...@@ -756,6 +801,18 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s); LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s); LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
struct gen_loader_opts {
size_t sz; /* size of this struct, for forward/backward compatiblity */
const char *data;
const char *insns;
__u32 data_sz;
__u32 insns_sz;
};
#define gen_loader_opts__last_field insns_sz
LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
struct gen_loader_opts *opts);
enum libbpf_tristate { enum libbpf_tristate {
TRI_NO = 0, TRI_NO = 0,
TRI_YES = 1, TRI_YES = 1,
...@@ -768,10 +825,18 @@ struct bpf_linker_opts { ...@@ -768,10 +825,18 @@ struct bpf_linker_opts {
}; };
#define bpf_linker_opts__last_field sz #define bpf_linker_opts__last_field sz
struct bpf_linker_file_opts {
/* size of this struct, for forward/backward compatiblity */
size_t sz;
};
#define bpf_linker_file_opts__last_field sz
struct bpf_linker; struct bpf_linker;
LIBBPF_API struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts *opts); LIBBPF_API struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts *opts);
LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker, const char *filename); LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker,
const char *filename,
const struct bpf_linker_file_opts *opts);
LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker); LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker);
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker); LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);
......
...@@ -359,6 +359,13 @@ LIBBPF_0.4.0 { ...@@ -359,6 +359,13 @@ LIBBPF_0.4.0 {
bpf_linker__finalize; bpf_linker__finalize;
bpf_linker__free; bpf_linker__free;
bpf_linker__new; bpf_linker__new;
bpf_map__initial_value;
bpf_map__inner_map; bpf_map__inner_map;
bpf_object__gen_loader;
bpf_object__set_kversion; bpf_object__set_kversion;
bpf_tc_attach;
bpf_tc_detach;
bpf_tc_hook_create;
bpf_tc_hook_destroy;
bpf_tc_query;
} LIBBPF_0.3.0; } LIBBPF_0.3.0;
...@@ -258,6 +258,8 @@ int bpf_object__section_size(const struct bpf_object *obj, const char *name, ...@@ -258,6 +258,8 @@ int bpf_object__section_size(const struct bpf_object *obj, const char *name,
int bpf_object__variable_offset(const struct bpf_object *obj, const char *name, int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
__u32 *off); __u32 *off);
struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf); struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf);
void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
const char **prefix, int *kind);
struct btf_ext_info { struct btf_ext_info {
/* /*
......
...@@ -158,7 +158,9 @@ struct bpf_linker { ...@@ -158,7 +158,9 @@ struct bpf_linker {
static int init_output_elf(struct bpf_linker *linker, const char *file); static int init_output_elf(struct bpf_linker *linker, const char *file);
static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, struct src_obj *obj); static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts,
struct src_obj *obj);
static int linker_sanity_check_elf(struct src_obj *obj); static int linker_sanity_check_elf(struct src_obj *obj);
static int linker_sanity_check_elf_symtab(struct src_obj *obj, struct src_sec *sec); static int linker_sanity_check_elf_symtab(struct src_obj *obj, struct src_sec *sec);
static int linker_sanity_check_elf_relos(struct src_obj *obj, struct src_sec *sec); static int linker_sanity_check_elf_relos(struct src_obj *obj, struct src_sec *sec);
...@@ -435,15 +437,19 @@ static int init_output_elf(struct bpf_linker *linker, const char *file) ...@@ -435,15 +437,19 @@ static int init_output_elf(struct bpf_linker *linker, const char *file)
return 0; return 0;
} }
int bpf_linker__add_file(struct bpf_linker *linker, const char *filename) int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts)
{ {
struct src_obj obj = {}; struct src_obj obj = {};
int err = 0; int err = 0;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return -EINVAL;
if (!linker->elf) if (!linker->elf)
return -EINVAL; return -EINVAL;
err = err ?: linker_load_obj_file(linker, filename, &obj); err = err ?: linker_load_obj_file(linker, filename, opts, &obj);
err = err ?: linker_append_sec_data(linker, &obj); err = err ?: linker_append_sec_data(linker, &obj);
err = err ?: linker_append_elf_syms(linker, &obj); err = err ?: linker_append_elf_syms(linker, &obj);
err = err ?: linker_append_elf_relos(linker, &obj); err = err ?: linker_append_elf_relos(linker, &obj);
...@@ -529,7 +535,9 @@ static struct src_sec *add_src_sec(struct src_obj *obj, const char *sec_name) ...@@ -529,7 +535,9 @@ static struct src_sec *add_src_sec(struct src_obj *obj, const char *sec_name)
return sec; return sec;
} }
static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, struct src_obj *obj) static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts,
struct src_obj *obj)
{ {
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
const int host_endianness = ELFDATA2LSB; const int host_endianness = ELFDATA2LSB;
...@@ -1780,7 +1788,7 @@ static void sym_update_visibility(Elf64_Sym *sym, int sym_vis) ...@@ -1780,7 +1788,7 @@ static void sym_update_visibility(Elf64_Sym *sym, int sym_vis)
/* libelf doesn't provide setters for ST_VISIBILITY, /* libelf doesn't provide setters for ST_VISIBILITY,
* but it is stored in the lower 2 bits of st_other * but it is stored in the lower 2 bits of st_other
*/ */
sym->st_other &= 0x03; sym->st_other &= ~0x03;
sym->st_other |= sym_vis; sym->st_other |= sym_vis;
} }
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <memory.h> #include <memory.h>
#include <unistd.h> #include <unistd.h>
#include <arpa/inet.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/pkt_cls.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <errno.h> #include <errno.h>
...@@ -73,9 +76,20 @@ static int libbpf_netlink_open(__u32 *nl_pid) ...@@ -73,9 +76,20 @@ static int libbpf_netlink_open(__u32 *nl_pid)
return ret; return ret;
} }
static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, static void libbpf_netlink_close(int sock)
__dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, {
void *cookie) close(sock);
}
enum {
NL_CONT,
NL_NEXT,
NL_DONE,
};
static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
__dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
void *cookie)
{ {
bool multipart = true; bool multipart = true;
struct nlmsgerr *err; struct nlmsgerr *err;
...@@ -84,6 +98,7 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, ...@@ -84,6 +98,7 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
int len, ret; int len, ret;
while (multipart) { while (multipart) {
start:
multipart = false; multipart = false;
len = recv(sock, buf, sizeof(buf), 0); len = recv(sock, buf, sizeof(buf), 0);
if (len < 0) { if (len < 0) {
...@@ -121,8 +136,16 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, ...@@ -121,8 +136,16 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
} }
if (_fn) { if (_fn) {
ret = _fn(nh, fn, cookie); ret = _fn(nh, fn, cookie);
if (ret) switch (ret) {
case NL_CONT:
break;
case NL_NEXT:
goto start;
case NL_DONE:
return 0;
default:
return ret; return ret;
}
} }
} }
} }
...@@ -131,72 +154,72 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq, ...@@ -131,72 +154,72 @@ static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
return ret; return ret;
} }
static int libbpf_netlink_send_recv(struct nlmsghdr *nh,
__dump_nlmsg_t parse_msg,
libbpf_dump_nlmsg_t parse_attr,
void *cookie)
{
__u32 nl_pid = 0;
int sock, ret;
sock = libbpf_netlink_open(&nl_pid);
if (sock < 0)
return sock;
nh->nlmsg_pid = 0;
nh->nlmsg_seq = time(NULL);
if (send(sock, nh, nh->nlmsg_len, 0) < 0) {
ret = -errno;
goto out;
}
ret = libbpf_netlink_recv(sock, nl_pid, nh->nlmsg_seq,
parse_msg, parse_attr, cookie);
out:
libbpf_netlink_close(sock);
return ret;
}
static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd, static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
__u32 flags) __u32 flags)
{ {
int sock, seq = 0, ret; struct nlattr *nla;
struct nlattr *nla, *nla_xdp; int ret;
struct { struct {
struct nlmsghdr nh; struct nlmsghdr nh;
struct ifinfomsg ifinfo; struct ifinfomsg ifinfo;
char attrbuf[64]; char attrbuf[64];
} req; } req;
__u32 nl_pid = 0;
sock = libbpf_netlink_open(&nl_pid);
if (sock < 0)
return sock;
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.nh.nlmsg_type = RTM_SETLINK; req.nh.nlmsg_type = RTM_SETLINK;
req.nh.nlmsg_pid = 0;
req.nh.nlmsg_seq = ++seq;
req.ifinfo.ifi_family = AF_UNSPEC; req.ifinfo.ifi_family = AF_UNSPEC;
req.ifinfo.ifi_index = ifindex; req.ifinfo.ifi_index = ifindex;
/* started nested attribute for XDP */ nla = nlattr_begin_nested(&req.nh, sizeof(req), IFLA_XDP);
nla = (struct nlattr *)(((char *)&req) if (!nla)
+ NLMSG_ALIGN(req.nh.nlmsg_len)); return -EMSGSIZE;
nla->nla_type = NLA_F_NESTED | IFLA_XDP; ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FD, &fd, sizeof(fd));
nla->nla_len = NLA_HDRLEN; if (ret < 0)
return ret;
/* add XDP fd */
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
nla_xdp->nla_type = IFLA_XDP_FD;
nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
nla->nla_len += nla_xdp->nla_len;
/* if user passed in any flags, add those too */
if (flags) { if (flags) {
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FLAGS, &flags,
nla_xdp->nla_type = IFLA_XDP_FLAGS; sizeof(flags));
nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); if (ret < 0)
memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); return ret;
nla->nla_len += nla_xdp->nla_len;
} }
if (flags & XDP_FLAGS_REPLACE) { if (flags & XDP_FLAGS_REPLACE) {
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_EXPECTED_FD,
nla_xdp->nla_type = IFLA_XDP_EXPECTED_FD; &old_fd, sizeof(old_fd));
nla_xdp->nla_len = NLA_HDRLEN + sizeof(old_fd); if (ret < 0)
memcpy((char *)nla_xdp + NLA_HDRLEN, &old_fd, sizeof(old_fd)); return ret;
nla->nla_len += nla_xdp->nla_len;
} }
nlattr_end_nested(&req.nh, nla);
req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
ret = -errno;
goto cleanup;
}
ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
cleanup:
close(sock);
return ret;
} }
int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
...@@ -212,9 +235,7 @@ int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, ...@@ -212,9 +235,7 @@ int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
flags |= XDP_FLAGS_REPLACE; flags |= XDP_FLAGS_REPLACE;
} }
return __bpf_set_link_xdp_fd_replace(ifindex, fd, return __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags);
old_fd,
flags);
} }
int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
...@@ -231,6 +252,7 @@ static int __dump_link_nlmsg(struct nlmsghdr *nlh, ...@@ -231,6 +252,7 @@ static int __dump_link_nlmsg(struct nlmsghdr *nlh,
len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
return -LIBBPF_ERRNO__NLPARSE; return -LIBBPF_ERRNO__NLPARSE;
...@@ -282,16 +304,21 @@ static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) ...@@ -282,16 +304,21 @@ static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
return 0; return 0;
} }
static int libbpf_nl_get_link(int sock, unsigned int nl_pid,
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
size_t info_size, __u32 flags) size_t info_size, __u32 flags)
{ {
struct xdp_id_md xdp_id = {}; struct xdp_id_md xdp_id = {};
int sock, ret;
__u32 nl_pid = 0;
__u32 mask; __u32 mask;
int ret;
struct {
struct nlmsghdr nh;
struct ifinfomsg ifm;
} req = {
.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nh.nlmsg_type = RTM_GETLINK,
.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
.ifm.ifi_family = AF_PACKET,
};
if (flags & ~XDP_FLAGS_MASK || !info_size) if (flags & ~XDP_FLAGS_MASK || !info_size)
return -EINVAL; return -EINVAL;
...@@ -302,14 +329,11 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, ...@@ -302,14 +329,11 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
if (flags && flags & mask) if (flags && flags & mask)
return -EINVAL; return -EINVAL;
sock = libbpf_netlink_open(&nl_pid);
if (sock < 0)
return sock;
xdp_id.ifindex = ifindex; xdp_id.ifindex = ifindex;
xdp_id.flags = flags; xdp_id.flags = flags;
ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id); ret = libbpf_netlink_send_recv(&req.nh, __dump_link_nlmsg,
get_xdp_info, &xdp_id);
if (!ret) { if (!ret) {
size_t sz = min(info_size, sizeof(xdp_id.info)); size_t sz = min(info_size, sizeof(xdp_id.info));
...@@ -317,7 +341,6 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, ...@@ -317,7 +341,6 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
memset((void *) info + sz, 0, info_size - sz); memset((void *) info + sz, 0, info_size - sz);
} }
close(sock);
return ret; return ret;
} }
...@@ -349,24 +372,403 @@ int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) ...@@ -349,24 +372,403 @@ int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
return ret; return ret;
} }
int libbpf_nl_get_link(int sock, unsigned int nl_pid, typedef int (*qdisc_config_t)(struct nlmsghdr *nh, struct tcmsg *t,
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) size_t maxsz);
static int clsact_config(struct nlmsghdr *nh, struct tcmsg *t, size_t maxsz)
{ {
t->tcm_parent = TC_H_CLSACT;
t->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
return nlattr_add(nh, maxsz, TCA_KIND, "clsact", sizeof("clsact"));
}
static int attach_point_to_config(struct bpf_tc_hook *hook,
qdisc_config_t *config)
{
switch (OPTS_GET(hook, attach_point, 0)) {
case BPF_TC_INGRESS:
case BPF_TC_EGRESS:
case BPF_TC_INGRESS | BPF_TC_EGRESS:
if (OPTS_GET(hook, parent, 0))
return -EINVAL;
*config = &clsact_config;
return 0;
case BPF_TC_CUSTOM:
return -EOPNOTSUPP;
default:
return -EINVAL;
}
}
static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
__u32 *parent)
{
switch (attach_point) {
case BPF_TC_INGRESS:
case BPF_TC_EGRESS:
if (*parent)
return -EINVAL;
*parent = TC_H_MAKE(TC_H_CLSACT,
attach_point == BPF_TC_INGRESS ?
TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
break;
case BPF_TC_CUSTOM:
if (!*parent)
return -EINVAL;
break;
default:
return -EINVAL;
}
return 0;
}
static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
{
qdisc_config_t config;
int ret;
struct { struct {
struct nlmsghdr nlh; struct nlmsghdr nh;
struct ifinfomsg ifm; struct tcmsg tc;
} req = { char buf[256];
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), } req;
.nlh.nlmsg_type = RTM_GETLINK,
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, ret = attach_point_to_config(hook, &config);
.ifm.ifi_family = AF_PACKET, if (ret < 0)
}; return ret;
int seq = time(NULL);
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
req.nh.nlmsg_type = cmd;
req.tc.tcm_family = AF_UNSPEC;
req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
ret = config(&req.nh, &req.tc, sizeof(req));
if (ret < 0)
return ret;
return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
}
static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
{
return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE);
}
static int tc_qdisc_delete(struct bpf_tc_hook *hook)
{
return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
}
int bpf_tc_hook_create(struct bpf_tc_hook *hook)
{
if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
OPTS_GET(hook, ifindex, 0) <= 0)
return -EINVAL;
return tc_qdisc_create_excl(hook);
}
req.nlh.nlmsg_seq = seq; static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) const struct bpf_tc_opts *opts,
const bool flush);
int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
{
if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
OPTS_GET(hook, ifindex, 0) <= 0)
return -EINVAL;
switch (OPTS_GET(hook, attach_point, 0)) {
case BPF_TC_INGRESS:
case BPF_TC_EGRESS:
return __bpf_tc_detach(hook, NULL, true);
case BPF_TC_INGRESS | BPF_TC_EGRESS:
return tc_qdisc_delete(hook);
case BPF_TC_CUSTOM:
return -EOPNOTSUPP;
default:
return -EINVAL;
}
}
struct bpf_cb_ctx {
struct bpf_tc_opts *opts;
bool processed;
};
static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
bool unicast)
{
struct nlattr *tbb[TCA_BPF_MAX + 1];
struct bpf_cb_ctx *info = cookie;
if (!info || !info->opts)
return -EINVAL;
if (unicast && info->processed)
return -EINVAL;
if (!tb[TCA_OPTIONS])
return NL_CONT;
libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
if (!tbb[TCA_BPF_ID])
return -EINVAL;
OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
OPTS_SET(info->opts, handle, tc->tcm_handle);
OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
info->processed = true;
return unicast ? NL_NEXT : NL_DONE;
}
static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
void *cookie)
{
struct tcmsg *tc = NLMSG_DATA(nh);
struct nlattr *tb[TCA_MAX + 1];
libbpf_nla_parse(tb, TCA_MAX,
(struct nlattr *)((char *)tc + NLMSG_ALIGN(sizeof(*tc))),
NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
if (!tb[TCA_KIND])
return NL_CONT;
return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
}
static int tc_add_fd_and_name(struct nlmsghdr *nh, size_t maxsz, int fd)
{
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
char name[256];
int len, ret;
ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
if (ret < 0)
return ret;
ret = nlattr_add(nh, maxsz, TCA_BPF_FD, &fd, sizeof(fd));
if (ret < 0)
return ret;
len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
if (len < 0)
return -errno; return -errno;
if (len >= sizeof(name))
return -ENAMETOOLONG;
return nlattr_add(nh, maxsz, TCA_BPF_NAME, name, len + 1);
}
int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
{
__u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
int ret, ifindex, attach_point, prog_fd;
struct bpf_cb_ctx info = {};
struct nlattr *nla;
struct {
struct nlmsghdr nh;
struct tcmsg tc;
char buf[256];
} req;
if (!hook || !opts ||
!OPTS_VALID(hook, bpf_tc_hook) ||
!OPTS_VALID(opts, bpf_tc_opts))
return -EINVAL;
ifindex = OPTS_GET(hook, ifindex, 0);
parent = OPTS_GET(hook, parent, 0);
attach_point = OPTS_GET(hook, attach_point, 0);
handle = OPTS_GET(opts, handle, 0);
priority = OPTS_GET(opts, priority, 0);
prog_fd = OPTS_GET(opts, prog_fd, 0);
prog_id = OPTS_GET(opts, prog_id, 0);
flags = OPTS_GET(opts, flags, 0);
if (ifindex <= 0 || !prog_fd || prog_id)
return -EINVAL;
if (priority > UINT16_MAX)
return -EINVAL;
if (flags & ~BPF_TC_F_REPLACE)
return -EINVAL;
flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
protocol = ETH_P_ALL;
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
NLM_F_ECHO | flags;
req.nh.nlmsg_type = RTM_NEWTFILTER;
req.tc.tcm_family = AF_UNSPEC;
req.tc.tcm_ifindex = ifindex;
req.tc.tcm_handle = handle;
req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
ret = tc_get_tcm_parent(attach_point, &parent);
if (ret < 0)
return ret;
req.tc.tcm_parent = parent;
ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND, "bpf", sizeof("bpf"));
if (ret < 0)
return ret;
nla = nlattr_begin_nested(&req.nh, sizeof(req), TCA_OPTIONS);
if (!nla)
return -EMSGSIZE;
ret = tc_add_fd_and_name(&req.nh, sizeof(req), prog_fd);
if (ret < 0)
return ret;
bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
ret = nlattr_add(&req.nh, sizeof(req), TCA_BPF_FLAGS, &bpf_flags,
sizeof(bpf_flags));
if (ret < 0)
return ret;
nlattr_end_nested(&req.nh, nla);
info.opts = opts;
ret = libbpf_netlink_send_recv(&req.nh, get_tc_info, NULL, &info);
if (ret < 0)
return ret;
if (!info.processed)
return -ENOENT;
return ret;
}
static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
const struct bpf_tc_opts *opts,
const bool flush)
{
__u32 protocol = 0, handle, priority, parent, prog_id, flags;
int ret, ifindex, attach_point, prog_fd;
struct {
struct nlmsghdr nh;
struct tcmsg tc;
char buf[256];
} req;
return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, if (!hook ||
dump_link_nlmsg, cookie); !OPTS_VALID(hook, bpf_tc_hook) ||
!OPTS_VALID(opts, bpf_tc_opts))
return -EINVAL;
ifindex = OPTS_GET(hook, ifindex, 0);
parent = OPTS_GET(hook, parent, 0);
attach_point = OPTS_GET(hook, attach_point, 0);
handle = OPTS_GET(opts, handle, 0);
priority = OPTS_GET(opts, priority, 0);
prog_fd = OPTS_GET(opts, prog_fd, 0);
prog_id = OPTS_GET(opts, prog_id, 0);
flags = OPTS_GET(opts, flags, 0);
if (ifindex <= 0 || flags || prog_fd || prog_id)
return -EINVAL;
if (priority > UINT16_MAX)
return -EINVAL;
if (flags & ~BPF_TC_F_REPLACE)
return -EINVAL;
if (!flush) {
if (!handle || !priority)
return -EINVAL;
protocol = ETH_P_ALL;
} else {
if (handle || priority)
return -EINVAL;
}
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.nh.nlmsg_type = RTM_DELTFILTER;
req.tc.tcm_family = AF_UNSPEC;
req.tc.tcm_ifindex = ifindex;
if (!flush) {
req.tc.tcm_handle = handle;
req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
}
ret = tc_get_tcm_parent(attach_point, &parent);
if (ret < 0)
return ret;
req.tc.tcm_parent = parent;
if (!flush) {
ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND,
"bpf", sizeof("bpf"));
if (ret < 0)
return ret;
}
return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
}
int bpf_tc_detach(const struct bpf_tc_hook *hook,
const struct bpf_tc_opts *opts)
{
return !opts ? -EINVAL : __bpf_tc_detach(hook, opts, false);
}
int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
{
__u32 protocol, handle, priority, parent, prog_id, flags;
int ret, ifindex, attach_point, prog_fd;
struct bpf_cb_ctx info = {};
struct {
struct nlmsghdr nh;
struct tcmsg tc;
char buf[256];
} req;
if (!hook || !opts ||
!OPTS_VALID(hook, bpf_tc_hook) ||
!OPTS_VALID(opts, bpf_tc_opts))
return -EINVAL;
ifindex = OPTS_GET(hook, ifindex, 0);
parent = OPTS_GET(hook, parent, 0);
attach_point = OPTS_GET(hook, attach_point, 0);
handle = OPTS_GET(opts, handle, 0);
priority = OPTS_GET(opts, priority, 0);
prog_fd = OPTS_GET(opts, prog_fd, 0);
prog_id = OPTS_GET(opts, prog_id, 0);
flags = OPTS_GET(opts, flags, 0);
if (ifindex <= 0 || flags || prog_fd || prog_id ||
!handle || !priority)
return -EINVAL;
if (priority > UINT16_MAX)
return -EINVAL;
protocol = ETH_P_ALL;
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req.nh.nlmsg_flags = NLM_F_REQUEST;
req.nh.nlmsg_type = RTM_GETTFILTER;
req.tc.tcm_family = AF_UNSPEC;
req.tc.tcm_ifindex = ifindex;
req.tc.tcm_handle = handle;
req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
ret = tc_get_tcm_parent(attach_point, &parent);
if (ret < 0)
return ret;
req.tc.tcm_parent = parent;
ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND, "bpf", sizeof("bpf"));
if (ret < 0)
return ret;
info.opts = opts;
ret = libbpf_netlink_send_recv(&req.nh, get_tc_info, NULL, &info);
if (ret < 0)
return ret;
if (!info.processed)
return -ENOENT;
return ret;
} }
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
#define __LIBBPF_NLATTR_H #define __LIBBPF_NLATTR_H
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <errno.h>
#include <linux/netlink.h> #include <linux/netlink.h>
/* avoid multiple definition of netlink features */ /* avoid multiple definition of netlink features */
#define __LINUX_NETLINK_H #define __LINUX_NETLINK_H
...@@ -103,4 +106,49 @@ int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype, ...@@ -103,4 +106,49 @@ int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype,
int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh); int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh);
static inline struct nlattr *nla_data(struct nlattr *nla)
{
return (struct nlattr *)((char *)nla + NLA_HDRLEN);
}
static inline struct nlattr *nh_tail(struct nlmsghdr *nh)
{
return (struct nlattr *)((char *)nh + NLMSG_ALIGN(nh->nlmsg_len));
}
static inline int nlattr_add(struct nlmsghdr *nh, size_t maxsz, int type,
const void *data, int len)
{
struct nlattr *nla;
if (NLMSG_ALIGN(nh->nlmsg_len) + NLA_ALIGN(NLA_HDRLEN + len) > maxsz)
return -EMSGSIZE;
if (!!data != !!len)
return -EINVAL;
nla = nh_tail(nh);
nla->nla_type = type;
nla->nla_len = NLA_HDRLEN + len;
if (data)
memcpy(nla_data(nla), data, len);
nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + NLA_ALIGN(nla->nla_len);
return 0;
}
static inline struct nlattr *nlattr_begin_nested(struct nlmsghdr *nh,
size_t maxsz, int type)
{
struct nlattr *tail;
tail = nh_tail(nh);
if (nlattr_add(nh, maxsz, type | NLA_F_NESTED, NULL, 0))
return NULL;
return tail;
}
static inline void nlattr_end_nested(struct nlmsghdr *nh, struct nlattr *tail)
{
tail->nla_len = (char *)nh_tail(nh) - (char *)tail;
}
#endif /* __LIBBPF_NLATTR_H */ #endif /* __LIBBPF_NLATTR_H */
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* Copyright (c) 2021 Facebook */
#ifndef __SKEL_INTERNAL_H
#define __SKEL_INTERNAL_H
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/mman.h>
/* This file is a base header for auto-generated *.lskel.h files.
* Its contents will change and may become part of auto-generation in the future.
*
* The layout of bpf_[map|prog]_desc and bpf_loader_ctx is feature dependent
* and will change from one version of libbpf to another and features
* requested during loader program generation.
*/
struct bpf_map_desc {
union {
/* input for the loader prog */
struct {
__aligned_u64 initial_value;
__u32 max_entries;
};
/* output of the loader prog */
struct {
int map_fd;
};
};
};
struct bpf_prog_desc {
int prog_fd;
};
struct bpf_loader_ctx {
size_t sz;
__u32 log_level;
__u32 log_size;
__u64 log_buf;
};
struct bpf_load_and_run_opts {
struct bpf_loader_ctx *ctx;
const void *data;
const void *insns;
__u32 data_sz;
__u32 insns_sz;
const char *errstr;
};
static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
unsigned int size)
{
return syscall(__NR_bpf, cmd, attr, size);
}
static inline int skel_closenz(int fd)
{
if (fd > 0)
return close(fd);
return -EINVAL;
}
static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
{
int map_fd = -1, prog_fd = -1, key = 0, err;
union bpf_attr attr;
map_fd = bpf_create_map_name(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
opts->data_sz, 1, 0);
if (map_fd < 0) {
opts->errstr = "failed to create loader map";
err = -errno;
goto out;
}
err = bpf_map_update_elem(map_fd, &key, opts->data, 0);
if (err < 0) {
opts->errstr = "failed to update loader map";
err = -errno;
goto out;
}
memset(&attr, 0, sizeof(attr));
attr.prog_type = BPF_PROG_TYPE_SYSCALL;
attr.insns = (long) opts->insns;
attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn);
attr.license = (long) "Dual BSD/GPL";
memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
attr.fd_array = (long) &map_fd;
attr.log_level = opts->ctx->log_level;
attr.log_size = opts->ctx->log_size;
attr.log_buf = opts->ctx->log_buf;
attr.prog_flags = BPF_F_SLEEPABLE;
prog_fd = skel_sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (prog_fd < 0) {
opts->errstr = "failed to load loader prog";
err = -errno;
goto out;
}
memset(&attr, 0, sizeof(attr));
attr.test.prog_fd = prog_fd;
attr.test.ctx_in = (long) opts->ctx;
attr.test.ctx_size_in = opts->ctx->sz;
err = skel_sys_bpf(BPF_PROG_RUN, &attr, sizeof(attr));
if (err < 0 || (int)attr.test.retval < 0) {
opts->errstr = "failed to execute loader prog";
if (err < 0)
err = -errno;
else
err = (int)attr.test.retval;
goto out;
}
err = 0;
out:
if (map_fd >= 0)
close(map_fd);
if (prog_fd >= 0)
close(prog_fd);
return err;
}
#endif
...@@ -30,6 +30,7 @@ test_sysctl ...@@ -30,6 +30,7 @@ test_sysctl
xdping xdping
test_cpp test_cpp
*.skel.h *.skel.h
*.lskel.h
/no_alu32 /no_alu32
/bpf_gcc /bpf_gcc
/tools /tools
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Facebook */ /* Copyright (c) 2019 Facebook */
#include <test_progs.h> #include <test_progs.h>
#include "fentry_test.skel.h" #include "fentry_test.lskel.h"
#include "fexit_test.skel.h" #include "fexit_test.lskel.h"
void test_fentry_fexit(void) void test_fentry_fexit(void)
{ {
...@@ -26,7 +26,7 @@ void test_fentry_fexit(void) ...@@ -26,7 +26,7 @@ void test_fentry_fexit(void)
if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err)) if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err))
goto close_prog; goto close_prog;
prog_fd = bpf_program__fd(fexit_skel->progs.test1); prog_fd = fexit_skel->progs.test1.prog_fd;
err = bpf_prog_test_run(prog_fd, 1, NULL, 0, err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
NULL, NULL, &retval, &duration); NULL, NULL, &retval, &duration);
CHECK(err || retval, "ipv6", CHECK(err || retval, "ipv6",
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#include <test_progs.h> #include <test_progs.h>
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include <bpf/btf.h> #include <bpf/btf.h>
#include "test_ksyms_module.skel.h" #include "test_ksyms_module.lskel.h"
static int duration; static int duration;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册