Commit 1b2fd38d authored by Daniel Borkmann's avatar Daniel Borkmann

Merge branch 'bpf-dynamic-relinking'

Alexei Starovoitov says:

====================
The last few month BPF community has been discussing an approach to call
chaining, since exiting bpt_tail_call() mechanism used in production XDP
programs has plenty of downsides. The outcome of these discussion was a
conclusion to implement dynamic re-linking of BPF programs. Where rootlet XDP
program attached to a netdevice can programmatically define a policy of
execution of other XDP programs. Such rootlet would be compiled as normal XDP
program and provide a number of placeholder global functions which later can be
replaced with future XDP programs. BPF trampoline, function by function
verification were building blocks towards that goal. The patch 1 is a final
building block. It introduces dynamic program extensions. A number of
improvements like more flexible function by function verification and better
libbpf api will be implemented in future patches.

v1->v2:
- addressed Andrii's comments
- rebase
====================
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents 2a67a6cc 7805fe84
...@@ -465,7 +465,8 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start); ...@@ -465,7 +465,8 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start);
enum bpf_tramp_prog_type { enum bpf_tramp_prog_type {
BPF_TRAMP_FENTRY, BPF_TRAMP_FENTRY,
BPF_TRAMP_FEXIT, BPF_TRAMP_FEXIT,
BPF_TRAMP_MAX BPF_TRAMP_MAX,
BPF_TRAMP_REPLACE, /* more than MAX */
}; };
struct bpf_trampoline { struct bpf_trampoline {
...@@ -480,6 +481,11 @@ struct bpf_trampoline { ...@@ -480,6 +481,11 @@ struct bpf_trampoline {
void *addr; void *addr;
bool ftrace_managed; bool ftrace_managed;
} func; } func;
/* if !NULL this is BPF_PROG_TYPE_EXT program that extends another BPF
* program by replacing one of its functions. func.addr is the address
* of the function it replaced.
*/
struct bpf_prog *extension_prog;
/* list of BPF programs using this trampoline */ /* list of BPF programs using this trampoline */
struct hlist_head progs_hlist[BPF_TRAMP_MAX]; struct hlist_head progs_hlist[BPF_TRAMP_MAX];
/* Number of attached programs. A counter per kind. */ /* Number of attached programs. A counter per kind. */
...@@ -1107,6 +1113,8 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, ...@@ -1107,6 +1113,8 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
struct bpf_reg_state *regs); struct bpf_reg_state *regs);
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog, int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
struct bpf_reg_state *reg); struct bpf_reg_state *reg);
int btf_check_type_match(struct bpf_verifier_env *env, struct bpf_prog *prog,
struct btf *btf, const struct btf_type *t);
struct bpf_prog *bpf_prog_by_id(u32 id); struct bpf_prog *bpf_prog_by_id(u32 id);
......
...@@ -68,6 +68,8 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_SK_REUSEPORT, sk_reuseport, ...@@ -68,6 +68,8 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_SK_REUSEPORT, sk_reuseport,
#if defined(CONFIG_BPF_JIT) #if defined(CONFIG_BPF_JIT)
BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops, BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops,
void *, void *) void *, void *)
BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
void *, void *)
#endif #endif
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
......
...@@ -107,6 +107,11 @@ static inline u16 btf_type_vlen(const struct btf_type *t) ...@@ -107,6 +107,11 @@ static inline u16 btf_type_vlen(const struct btf_type *t)
return BTF_INFO_VLEN(t->info); return BTF_INFO_VLEN(t->info);
} }
static inline u16 btf_func_linkage(const struct btf_type *t)
{
return BTF_INFO_VLEN(t->info);
}
static inline bool btf_type_kflag(const struct btf_type *t) static inline bool btf_type_kflag(const struct btf_type *t)
{ {
return BTF_INFO_KFLAG(t->info); return BTF_INFO_KFLAG(t->info);
......
...@@ -180,6 +180,7 @@ enum bpf_prog_type { ...@@ -180,6 +180,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_PROG_TYPE_CGROUP_SOCKOPT,
BPF_PROG_TYPE_TRACING, BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS, BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT,
}; };
enum bpf_attach_type { enum bpf_attach_type {
......
...@@ -276,6 +276,11 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = { ...@@ -276,6 +276,11 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DATASEC] = "DATASEC", [BTF_KIND_DATASEC] = "DATASEC",
}; };
static const char *btf_type_str(const struct btf_type *t)
{
return btf_kind_str[BTF_INFO_KIND(t->info)];
}
struct btf_kind_operations { struct btf_kind_operations {
s32 (*check_meta)(struct btf_verifier_env *env, s32 (*check_meta)(struct btf_verifier_env *env,
const struct btf_type *t, const struct btf_type *t,
...@@ -4115,6 +4120,148 @@ int btf_distill_func_proto(struct bpf_verifier_log *log, ...@@ -4115,6 +4120,148 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
return 0; return 0;
} }
/* Compare BTFs of two functions assuming only scalars and pointers to context.
* t1 points to BTF_KIND_FUNC in btf1
* t2 points to BTF_KIND_FUNC in btf2
* Returns:
* EINVAL - function prototype mismatch
* EFAULT - verifier bug
* 0 - 99% match. The last 1% is validated by the verifier.
*/
int btf_check_func_type_match(struct bpf_verifier_log *log,
struct btf *btf1, const struct btf_type *t1,
struct btf *btf2, const struct btf_type *t2)
{
const struct btf_param *args1, *args2;
const char *fn1, *fn2, *s1, *s2;
u32 nargs1, nargs2, i;
fn1 = btf_name_by_offset(btf1, t1->name_off);
fn2 = btf_name_by_offset(btf2, t2->name_off);
if (btf_func_linkage(t1) != BTF_FUNC_GLOBAL) {
bpf_log(log, "%s() is not a global function\n", fn1);
return -EINVAL;
}
if (btf_func_linkage(t2) != BTF_FUNC_GLOBAL) {
bpf_log(log, "%s() is not a global function\n", fn2);
return -EINVAL;
}
t1 = btf_type_by_id(btf1, t1->type);
if (!t1 || !btf_type_is_func_proto(t1))
return -EFAULT;
t2 = btf_type_by_id(btf2, t2->type);
if (!t2 || !btf_type_is_func_proto(t2))
return -EFAULT;
args1 = (const struct btf_param *)(t1 + 1);
nargs1 = btf_type_vlen(t1);
args2 = (const struct btf_param *)(t2 + 1);
nargs2 = btf_type_vlen(t2);
if (nargs1 != nargs2) {
bpf_log(log, "%s() has %d args while %s() has %d args\n",
fn1, nargs1, fn2, nargs2);
return -EINVAL;
}
t1 = btf_type_skip_modifiers(btf1, t1->type, NULL);
t2 = btf_type_skip_modifiers(btf2, t2->type, NULL);
if (t1->info != t2->info) {
bpf_log(log,
"Return type %s of %s() doesn't match type %s of %s()\n",
btf_type_str(t1), fn1,
btf_type_str(t2), fn2);
return -EINVAL;
}
for (i = 0; i < nargs1; i++) {
t1 = btf_type_skip_modifiers(btf1, args1[i].type, NULL);
t2 = btf_type_skip_modifiers(btf2, args2[i].type, NULL);
if (t1->info != t2->info) {
bpf_log(log, "arg%d in %s() is %s while %s() has %s\n",
i, fn1, btf_type_str(t1),
fn2, btf_type_str(t2));
return -EINVAL;
}
if (btf_type_has_size(t1) && t1->size != t2->size) {
bpf_log(log,
"arg%d in %s() has size %d while %s() has %d\n",
i, fn1, t1->size,
fn2, t2->size);
return -EINVAL;
}
/* global functions are validated with scalars and pointers
* to context only. And only global functions can be replaced.
* Hence type check only those types.
*/
if (btf_type_is_int(t1) || btf_type_is_enum(t1))
continue;
if (!btf_type_is_ptr(t1)) {
bpf_log(log,
"arg%d in %s() has unrecognized type\n",
i, fn1);
return -EINVAL;
}
t1 = btf_type_skip_modifiers(btf1, t1->type, NULL);
t2 = btf_type_skip_modifiers(btf2, t2->type, NULL);
if (!btf_type_is_struct(t1)) {
bpf_log(log,
"arg%d in %s() is not a pointer to context\n",
i, fn1);
return -EINVAL;
}
if (!btf_type_is_struct(t2)) {
bpf_log(log,
"arg%d in %s() is not a pointer to context\n",
i, fn2);
return -EINVAL;
}
/* This is an optional check to make program writing easier.
* Compare names of structs and report an error to the user.
* btf_prepare_func_args() already checked that t2 struct
* is a context type. btf_prepare_func_args() will check
* later that t1 struct is a context type as well.
*/
s1 = btf_name_by_offset(btf1, t1->name_off);
s2 = btf_name_by_offset(btf2, t2->name_off);
if (strcmp(s1, s2)) {
bpf_log(log,
"arg%d %s(struct %s *) doesn't match %s(struct %s *)\n",
i, fn1, s1, fn2, s2);
return -EINVAL;
}
}
return 0;
}
/* Compare BTFs of given program with BTF of target program */
int btf_check_type_match(struct bpf_verifier_env *env, struct bpf_prog *prog,
struct btf *btf2, const struct btf_type *t2)
{
struct btf *btf1 = prog->aux->btf;
const struct btf_type *t1;
u32 btf_id = 0;
if (!prog->aux->func_info) {
bpf_log(&env->log, "Program extension requires BTF\n");
return -EINVAL;
}
btf_id = prog->aux->func_info[0].type_id;
if (!btf_id)
return -EFAULT;
t1 = btf_type_by_id(btf1, btf_id);
if (!t1 || !btf_type_is_func(t1))
return -EFAULT;
return btf_check_func_type_match(&env->log, btf1, t1, btf2, t2);
}
/* Compare BTF of a function with given bpf_reg_state. /* Compare BTF of a function with given bpf_reg_state.
* Returns: * Returns:
* EFAULT - there is a verifier bug. Abort verification. * EFAULT - there is a verifier bug. Abort verification.
...@@ -4224,6 +4371,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog, ...@@ -4224,6 +4371,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
{ {
struct bpf_verifier_log *log = &env->log; struct bpf_verifier_log *log = &env->log;
struct bpf_prog *prog = env->prog; struct bpf_prog *prog = env->prog;
enum bpf_prog_type prog_type = prog->type;
struct btf *btf = prog->aux->btf; struct btf *btf = prog->aux->btf;
const struct btf_param *args; const struct btf_param *args;
const struct btf_type *t; const struct btf_type *t;
...@@ -4261,6 +4409,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog, ...@@ -4261,6 +4409,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
bpf_log(log, "Verifier bug in function %s()\n", tname); bpf_log(log, "Verifier bug in function %s()\n", tname);
return -EFAULT; return -EFAULT;
} }
if (prog_type == BPF_PROG_TYPE_EXT)
prog_type = prog->aux->linked_prog->type;
t = btf_type_by_id(btf, t->type); t = btf_type_by_id(btf, t->type);
if (!t || !btf_type_is_func_proto(t)) { if (!t || !btf_type_is_func_proto(t)) {
...@@ -4296,7 +4446,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog, ...@@ -4296,7 +4446,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
continue; continue;
} }
if (btf_type_is_ptr(t) && if (btf_type_is_ptr(t) &&
btf_get_prog_ctx_type(log, btf, t, prog->type, i)) { btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
reg[i + 1].type = PTR_TO_CTX; reg[i + 1].type = PTR_TO_CTX;
continue; continue;
} }
......
...@@ -1932,13 +1932,15 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type, ...@@ -1932,13 +1932,15 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
switch (prog_type) { switch (prog_type) {
case BPF_PROG_TYPE_TRACING: case BPF_PROG_TYPE_TRACING:
case BPF_PROG_TYPE_STRUCT_OPS: case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT:
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
} }
if (prog_fd && prog_type != BPF_PROG_TYPE_TRACING) if (prog_fd && prog_type != BPF_PROG_TYPE_TRACING &&
prog_type != BPF_PROG_TYPE_EXT)
return -EINVAL; return -EINVAL;
switch (prog_type) { switch (prog_type) {
...@@ -1981,6 +1983,10 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type, ...@@ -1981,6 +1983,10 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
default: default:
return -EINVAL; return -EINVAL;
} }
case BPF_PROG_TYPE_EXT:
if (expected_attach_type)
return -EINVAL;
/* fallthrough */
default: default:
return 0; return 0;
} }
...@@ -2183,7 +2189,8 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) ...@@ -2183,7 +2189,8 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
int tr_fd, err; int tr_fd, err;
if (prog->expected_attach_type != BPF_TRACE_FENTRY && if (prog->expected_attach_type != BPF_TRACE_FENTRY &&
prog->expected_attach_type != BPF_TRACE_FEXIT) { prog->expected_attach_type != BPF_TRACE_FEXIT &&
prog->type != BPF_PROG_TYPE_EXT) {
err = -EINVAL; err = -EINVAL;
goto out_put_prog; goto out_put_prog;
} }
...@@ -2250,12 +2257,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) ...@@ -2250,12 +2257,14 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT && if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
prog->type != BPF_PROG_TYPE_TRACING && prog->type != BPF_PROG_TYPE_TRACING &&
prog->type != BPF_PROG_TYPE_EXT &&
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) { prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
err = -EINVAL; err = -EINVAL;
goto out_put_prog; goto out_put_prog;
} }
if (prog->type == BPF_PROG_TYPE_TRACING) { if (prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_EXT) {
if (attr->raw_tracepoint.name) { if (attr->raw_tracepoint.name) {
/* The attach point for this category of programs /* The attach point for this category of programs
* should be specified via btf_id during program load. * should be specified via btf_id during program load.
......
...@@ -5,6 +5,12 @@ ...@@ -5,6 +5,12 @@
#include <linux/filter.h> #include <linux/filter.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
/* dummy _ops. The verifier will operate on target program's ops. */
const struct bpf_verifier_ops bpf_extension_verifier_ops = {
};
const struct bpf_prog_ops bpf_extension_prog_ops = {
};
/* btf_vmlinux has ~22k attachable functions. 1k htab is enough. */ /* btf_vmlinux has ~22k attachable functions. 1k htab is enough. */
#define TRAMPOLINE_HASH_BITS 10 #define TRAMPOLINE_HASH_BITS 10
#define TRAMPOLINE_TABLE_SIZE (1 << TRAMPOLINE_HASH_BITS) #define TRAMPOLINE_TABLE_SIZE (1 << TRAMPOLINE_HASH_BITS)
...@@ -194,8 +200,10 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t) ...@@ -194,8 +200,10 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t)
switch (t) { switch (t) {
case BPF_TRACE_FENTRY: case BPF_TRACE_FENTRY:
return BPF_TRAMP_FENTRY; return BPF_TRAMP_FENTRY;
default: case BPF_TRACE_FEXIT:
return BPF_TRAMP_FEXIT; return BPF_TRAMP_FEXIT;
default:
return BPF_TRAMP_REPLACE;
} }
} }
...@@ -204,12 +212,31 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog) ...@@ -204,12 +212,31 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
enum bpf_tramp_prog_type kind; enum bpf_tramp_prog_type kind;
struct bpf_trampoline *tr; struct bpf_trampoline *tr;
int err = 0; int err = 0;
int cnt;
tr = prog->aux->trampoline; tr = prog->aux->trampoline;
kind = bpf_attach_type_to_tramp(prog->expected_attach_type); kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
mutex_lock(&tr->mutex); mutex_lock(&tr->mutex);
if (tr->progs_cnt[BPF_TRAMP_FENTRY] + tr->progs_cnt[BPF_TRAMP_FEXIT] if (tr->extension_prog) {
>= BPF_MAX_TRAMP_PROGS) { /* cannot attach fentry/fexit if extension prog is attached.
* cannot overwrite extension prog either.
*/
err = -EBUSY;
goto out;
}
cnt = tr->progs_cnt[BPF_TRAMP_FENTRY] + tr->progs_cnt[BPF_TRAMP_FEXIT];
if (kind == BPF_TRAMP_REPLACE) {
/* Cannot attach extension if fentry/fexit are in use. */
if (cnt) {
err = -EBUSY;
goto out;
}
tr->extension_prog = prog;
err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP, NULL,
prog->bpf_func);
goto out;
}
if (cnt >= BPF_MAX_TRAMP_PROGS) {
err = -E2BIG; err = -E2BIG;
goto out; goto out;
} }
...@@ -240,9 +267,17 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog) ...@@ -240,9 +267,17 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
tr = prog->aux->trampoline; tr = prog->aux->trampoline;
kind = bpf_attach_type_to_tramp(prog->expected_attach_type); kind = bpf_attach_type_to_tramp(prog->expected_attach_type);
mutex_lock(&tr->mutex); mutex_lock(&tr->mutex);
if (kind == BPF_TRAMP_REPLACE) {
WARN_ON_ONCE(!tr->extension_prog);
err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP,
tr->extension_prog->bpf_func, NULL);
tr->extension_prog = NULL;
goto out;
}
hlist_del(&prog->aux->tramp_hlist); hlist_del(&prog->aux->tramp_hlist);
tr->progs_cnt[kind]--; tr->progs_cnt[kind]--;
err = bpf_trampoline_update(prog->aux->trampoline); err = bpf_trampoline_update(prog->aux->trampoline);
out:
mutex_unlock(&tr->mutex); mutex_unlock(&tr->mutex);
return err; return err;
} }
......
...@@ -9564,7 +9564,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog) ...@@ -9564,7 +9564,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
subprog); subprog);
regs = state->frame[state->curframe]->regs; regs = state->frame[state->curframe]->regs;
if (subprog) { if (subprog || env->prog->type == BPF_PROG_TYPE_EXT) {
ret = btf_prepare_func_args(env, subprog, regs); ret = btf_prepare_func_args(env, subprog, regs);
if (ret) if (ret)
goto out; goto out;
...@@ -9742,6 +9742,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env) ...@@ -9742,6 +9742,7 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
static int check_attach_btf_id(struct bpf_verifier_env *env) static int check_attach_btf_id(struct bpf_verifier_env *env)
{ {
struct bpf_prog *prog = env->prog; struct bpf_prog *prog = env->prog;
bool prog_extension = prog->type == BPF_PROG_TYPE_EXT;
struct bpf_prog *tgt_prog = prog->aux->linked_prog; struct bpf_prog *tgt_prog = prog->aux->linked_prog;
u32 btf_id = prog->aux->attach_btf_id; u32 btf_id = prog->aux->attach_btf_id;
const char prefix[] = "btf_trace_"; const char prefix[] = "btf_trace_";
...@@ -9757,7 +9758,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -9757,7 +9758,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
return check_struct_ops_btf_id(env); return check_struct_ops_btf_id(env);
if (prog->type != BPF_PROG_TYPE_TRACING) if (prog->type != BPF_PROG_TYPE_TRACING && !prog_extension)
return 0; return 0;
if (!btf_id) { if (!btf_id) {
...@@ -9793,8 +9794,59 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -9793,8 +9794,59 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
return -EINVAL; return -EINVAL;
} }
conservative = aux->func_info_aux[subprog].unreliable; conservative = aux->func_info_aux[subprog].unreliable;
if (prog_extension) {
if (conservative) {
verbose(env,
"Cannot replace static functions\n");
return -EINVAL;
}
if (!prog->jit_requested) {
verbose(env,
"Extension programs should be JITed\n");
return -EINVAL;
}
env->ops = bpf_verifier_ops[tgt_prog->type];
}
if (!tgt_prog->jited) {
verbose(env, "Can attach to only JITed progs\n");
return -EINVAL;
}
if (tgt_prog->type == prog->type) {
/* Cannot fentry/fexit another fentry/fexit program.
* Cannot attach program extension to another extension.
* It's ok to attach fentry/fexit to extension program.
*/
verbose(env, "Cannot recursively attach\n");
return -EINVAL;
}
if (tgt_prog->type == BPF_PROG_TYPE_TRACING &&
prog_extension &&
(tgt_prog->expected_attach_type == BPF_TRACE_FENTRY ||
tgt_prog->expected_attach_type == BPF_TRACE_FEXIT)) {
/* Program extensions can extend all program types
* except fentry/fexit. The reason is the following.
* The fentry/fexit programs are used for performance
* analysis, stats and can be attached to any program
* type except themselves. When extension program is
* replacing XDP function it is necessary to allow
* performance analysis of all functions. Both original
* XDP program and its program extension. Hence
* attaching fentry/fexit to BPF_PROG_TYPE_EXT is
* allowed. If extending of fentry/fexit was allowed it
* would be possible to create long call chain
* fentry->extension->fentry->extension beyond
* reasonable stack size. Hence extending fentry is not
* allowed.
*/
verbose(env, "Cannot extend fentry/fexit\n");
return -EINVAL;
}
key = ((u64)aux->id) << 32 | btf_id; key = ((u64)aux->id) << 32 | btf_id;
} else { } else {
if (prog_extension) {
verbose(env, "Cannot replace kernel functions\n");
return -EINVAL;
}
key = btf_id; key = btf_id;
} }
...@@ -9832,6 +9884,10 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -9832,6 +9884,10 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
prog->aux->attach_func_proto = t; prog->aux->attach_func_proto = t;
prog->aux->attach_btf_trace = true; prog->aux->attach_btf_trace = true;
return 0; return 0;
default:
if (!prog_extension)
return -EINVAL;
/* fallthrough */
case BPF_TRACE_FENTRY: case BPF_TRACE_FENTRY:
case BPF_TRACE_FEXIT: case BPF_TRACE_FEXIT:
if (!btf_type_is_func(t)) { if (!btf_type_is_func(t)) {
...@@ -9839,6 +9895,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -9839,6 +9895,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
btf_id); btf_id);
return -EINVAL; return -EINVAL;
} }
if (prog_extension &&
btf_check_type_match(env, prog, btf, t))
return -EINVAL;
t = btf_type_by_id(btf, t->type); t = btf_type_by_id(btf, t->type);
if (!btf_type_is_func_proto(t)) if (!btf_type_is_func_proto(t))
return -EINVAL; return -EINVAL;
...@@ -9862,18 +9921,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -9862,18 +9921,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
if (ret < 0) if (ret < 0)
goto out; goto out;
if (tgt_prog) { if (tgt_prog) {
if (!tgt_prog->jited) {
/* for now */
verbose(env, "Can trace only JITed BPF progs\n");
ret = -EINVAL;
goto out;
}
if (tgt_prog->type == BPF_PROG_TYPE_TRACING) {
/* prevent cycles */
verbose(env, "Cannot recursively attach\n");
ret = -EINVAL;
goto out;
}
if (subprog == 0) if (subprog == 0)
addr = (long) tgt_prog->bpf_func; addr = (long) tgt_prog->bpf_func;
else else
...@@ -9895,8 +9942,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) ...@@ -9895,8 +9942,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
if (ret) if (ret)
bpf_trampoline_put(tr); bpf_trampoline_put(tr);
return ret; return ret;
default:
return -EINVAL;
} }
} }
...@@ -9966,10 +10011,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, ...@@ -9966,10 +10011,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
goto skip_full_check; goto skip_full_check;
} }
ret = check_attach_btf_id(env);
if (ret)
goto skip_full_check;
env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT); env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT);
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
env->strict_alignment = true; env->strict_alignment = true;
...@@ -10006,6 +10047,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, ...@@ -10006,6 +10047,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
if (ret < 0) if (ret < 0)
goto skip_full_check; goto skip_full_check;
ret = check_attach_btf_id(env);
if (ret)
goto skip_full_check;
ret = check_cfg(env); ret = check_cfg(env);
if (ret < 0) if (ret < 0)
goto skip_full_check; goto skip_full_check;
......
...@@ -180,6 +180,7 @@ enum bpf_prog_type { ...@@ -180,6 +180,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_PROG_TYPE_CGROUP_SOCKOPT,
BPF_PROG_TYPE_TRACING, BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS, BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT,
}; };
enum bpf_attach_type { enum bpf_attach_type {
......
...@@ -237,7 +237,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr, ...@@ -237,7 +237,8 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
attr.expected_attach_type = load_attr->expected_attach_type; attr.expected_attach_type = load_attr->expected_attach_type;
if (attr.prog_type == BPF_PROG_TYPE_STRUCT_OPS) { if (attr.prog_type == BPF_PROG_TYPE_STRUCT_OPS) {
attr.attach_btf_id = load_attr->attach_btf_id; attr.attach_btf_id = load_attr->attach_btf_id;
} else if (attr.prog_type == BPF_PROG_TYPE_TRACING) { } else if (attr.prog_type == BPF_PROG_TYPE_TRACING ||
attr.prog_type == BPF_PROG_TYPE_EXT) {
attr.attach_btf_id = load_attr->attach_btf_id; attr.attach_btf_id = load_attr->attach_btf_id;
attr.attach_prog_fd = load_attr->attach_prog_fd; attr.attach_prog_fd = load_attr->attach_prog_fd;
} else { } else {
......
...@@ -4837,7 +4837,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, ...@@ -4837,7 +4837,8 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.license = license; load_attr.license = license;
if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) { if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) {
load_attr.attach_btf_id = prog->attach_btf_id; load_attr.attach_btf_id = prog->attach_btf_id;
} else if (prog->type == BPF_PROG_TYPE_TRACING) { } else if (prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_EXT) {
load_attr.attach_prog_fd = prog->attach_prog_fd; load_attr.attach_prog_fd = prog->attach_prog_fd;
load_attr.attach_btf_id = prog->attach_btf_id; load_attr.attach_btf_id = prog->attach_btf_id;
} else { } else {
...@@ -4918,7 +4919,8 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver) ...@@ -4918,7 +4919,8 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
{ {
int err = 0, fd, i, btf_id; int err = 0, fd, i, btf_id;
if (prog->type == BPF_PROG_TYPE_TRACING) { if (prog->type == BPF_PROG_TYPE_TRACING ||
prog->type == BPF_PROG_TYPE_EXT) {
btf_id = libbpf_find_attach_btf_id(prog); btf_id = libbpf_find_attach_btf_id(prog);
if (btf_id <= 0) if (btf_id <= 0)
return btf_id; return btf_id;
...@@ -5092,7 +5094,8 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, ...@@ -5092,7 +5094,8 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
bpf_program__set_type(prog, prog_type); bpf_program__set_type(prog, prog_type);
bpf_program__set_expected_attach_type(prog, attach_type); bpf_program__set_expected_attach_type(prog, attach_type);
if (prog_type == BPF_PROG_TYPE_TRACING) if (prog_type == BPF_PROG_TYPE_TRACING ||
prog_type == BPF_PROG_TYPE_EXT)
prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0); prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);
} }
...@@ -6166,6 +6169,7 @@ BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP); ...@@ -6166,6 +6169,7 @@ BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT); BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
BPF_PROG_TYPE_FNS(tracing, BPF_PROG_TYPE_TRACING); BPF_PROG_TYPE_FNS(tracing, BPF_PROG_TYPE_TRACING);
BPF_PROG_TYPE_FNS(struct_ops, BPF_PROG_TYPE_STRUCT_OPS); BPF_PROG_TYPE_FNS(struct_ops, BPF_PROG_TYPE_STRUCT_OPS);
BPF_PROG_TYPE_FNS(extension, BPF_PROG_TYPE_EXT);
enum bpf_attach_type enum bpf_attach_type
bpf_program__get_expected_attach_type(struct bpf_program *prog) bpf_program__get_expected_attach_type(struct bpf_program *prog)
...@@ -6265,6 +6269,9 @@ static const struct bpf_sec_def section_defs[] = { ...@@ -6265,6 +6269,9 @@ static const struct bpf_sec_def section_defs[] = {
.expected_attach_type = BPF_TRACE_FEXIT, .expected_attach_type = BPF_TRACE_FEXIT,
.is_attach_btf = true, .is_attach_btf = true,
.attach_fn = attach_trace), .attach_fn = attach_trace),
SEC_DEF("freplace/", EXT,
.is_attach_btf = true,
.attach_fn = attach_trace),
BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP), BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP),
BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT), BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN), BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
......
...@@ -318,6 +318,7 @@ LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog); ...@@ -318,6 +318,7 @@ LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_perf_event(struct bpf_program *prog); LIBBPF_API int bpf_program__set_perf_event(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_tracing(struct bpf_program *prog); LIBBPF_API int bpf_program__set_tracing(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_struct_ops(struct bpf_program *prog); LIBBPF_API int bpf_program__set_struct_ops(struct bpf_program *prog);
LIBBPF_API int bpf_program__set_extension(struct bpf_program *prog);
LIBBPF_API enum bpf_prog_type bpf_program__get_type(struct bpf_program *prog); LIBBPF_API enum bpf_prog_type bpf_program__get_type(struct bpf_program *prog);
LIBBPF_API void bpf_program__set_type(struct bpf_program *prog, LIBBPF_API void bpf_program__set_type(struct bpf_program *prog,
...@@ -339,6 +340,7 @@ LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog); ...@@ -339,6 +340,7 @@ LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_tracing(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_tracing(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_struct_ops(const struct bpf_program *prog); LIBBPF_API bool bpf_program__is_struct_ops(const struct bpf_program *prog);
LIBBPF_API bool bpf_program__is_extension(const struct bpf_program *prog);
/* /*
* No need for __attribute__((packed)), all members of 'bpf_map_def' * No need for __attribute__((packed)), all members of 'bpf_map_def'
......
...@@ -228,7 +228,9 @@ LIBBPF_0.0.7 { ...@@ -228,7 +228,9 @@ LIBBPF_0.0.7 {
bpf_prog_attach_xattr; bpf_prog_attach_xattr;
bpf_program__attach; bpf_program__attach;
bpf_program__name; bpf_program__name;
bpf_program__is_extension;
bpf_program__is_struct_ops; bpf_program__is_struct_ops;
bpf_program__set_extension;
bpf_program__set_struct_ops; bpf_program__set_struct_ops;
btf__align_of; btf__align_of;
libbpf_find_kernel_btf; libbpf_find_kernel_btf;
......
...@@ -107,6 +107,7 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, ...@@ -107,6 +107,7 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
case BPF_PROG_TYPE_CGROUP_SOCKOPT: case BPF_PROG_TYPE_CGROUP_SOCKOPT:
case BPF_PROG_TYPE_TRACING: case BPF_PROG_TYPE_TRACING:
case BPF_PROG_TYPE_STRUCT_OPS: case BPF_PROG_TYPE_STRUCT_OPS:
case BPF_PROG_TYPE_EXT:
default: default:
break; break;
} }
......
...@@ -26,7 +26,7 @@ static void test_fexit_bpf2bpf_common(const char *obj_file, ...@@ -26,7 +26,7 @@ static void test_fexit_bpf2bpf_common(const char *obj_file,
link = calloc(sizeof(struct bpf_link *), prog_cnt); link = calloc(sizeof(struct bpf_link *), prog_cnt);
prog = calloc(sizeof(struct bpf_program *), prog_cnt); prog = calloc(sizeof(struct bpf_program *), prog_cnt);
result = malloc(prog_cnt * sizeof(u64)); result = malloc((prog_cnt + 32 /* spare */) * sizeof(u64));
if (CHECK(!link || !prog || !result, "alloc_memory", if (CHECK(!link || !prog || !result, "alloc_memory",
"failed to alloc memory")) "failed to alloc memory"))
goto close_prog; goto close_prog;
...@@ -106,8 +106,26 @@ static void test_target_yes_callees(void) ...@@ -106,8 +106,26 @@ static void test_target_yes_callees(void)
prog_name); prog_name);
} }
static void test_func_replace(void)
{
const char *prog_name[] = {
"fexit/test_pkt_access",
"fexit/test_pkt_access_subprog1",
"fexit/test_pkt_access_subprog2",
"fexit/test_pkt_access_subprog3",
"freplace/get_skb_len",
"freplace/get_skb_ifindex",
"freplace/get_constant",
};
test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
"./test_pkt_access.o",
ARRAY_SIZE(prog_name),
prog_name);
}
void test_fexit_bpf2bpf(void) void test_fexit_bpf2bpf(void)
{ {
test_target_no_callees(); test_target_no_callees();
test_target_yes_callees(); test_target_yes_callees();
test_func_replace();
} }
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Facebook */ /* Copyright (c) 2019 Facebook */
#include <linux/stddef.h>
#include <linux/ipv6.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <bpf/bpf_helpers.h> #include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include "bpf_trace_helpers.h" #include "bpf_trace_helpers.h"
struct sk_buff { struct sk_buff {
...@@ -94,4 +97,58 @@ int BPF_PROG(test_subprog3, int val, struct sk_buff *skb, int ret) ...@@ -94,4 +97,58 @@ int BPF_PROG(test_subprog3, int val, struct sk_buff *skb, int ret)
test_result_subprog3 = 1; test_result_subprog3 = 1;
return 0; return 0;
} }
__u64 test_get_skb_len = 0;
SEC("freplace/get_skb_len")
int new_get_skb_len(struct __sk_buff *skb)
{
int len = skb->len;
if (len != 74)
return 0;
test_get_skb_len = 1;
return 74; /* original get_skb_len() returns skb->len */
}
__u64 test_get_skb_ifindex = 0;
SEC("freplace/get_skb_ifindex")
int new_get_skb_ifindex(int val, struct __sk_buff *skb, int var)
{
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
struct ipv6hdr ip6, *ip6p;
int ifindex = skb->ifindex;
__u32 eth_proto;
__u32 nh_off;
/* check that BPF extension can read packet via direct packet access */
if (data + 14 + sizeof(ip6) > data_end)
return 0;
ip6p = data + 14;
if (ip6p->nexthdr != 6 || ip6p->payload_len != __bpf_constant_htons(123))
return 0;
/* check that legacy packet access helper works too */
if (bpf_skb_load_bytes(skb, 14, &ip6, sizeof(ip6)) < 0)
return 0;
ip6p = &ip6;
if (ip6p->nexthdr != 6 || ip6p->payload_len != __bpf_constant_htons(123))
return 0;
if (ifindex != 1 || val != 3 || var != 1)
return 0;
test_get_skb_ifindex = 1;
return 3; /* original get_skb_ifindex() returns val * ifindex * var */
}
volatile __u64 test_get_constant = 0;
SEC("freplace/get_constant")
int new_get_constant(long val)
{
if (val != 123)
return 0;
test_get_constant = 1;
return test_get_constant; /* original get_constant() returns val - 122 */
}
char _license[] SEC("license") = "GPL"; char _license[] SEC("license") = "GPL";
...@@ -57,12 +57,18 @@ int get_skb_len(struct __sk_buff *skb) ...@@ -57,12 +57,18 @@ int get_skb_len(struct __sk_buff *skb)
return skb->len; return skb->len;
} }
__attribute__ ((noinline))
int get_constant(long val)
{
return val - 122;
}
int get_skb_ifindex(int, struct __sk_buff *skb, int); int get_skb_ifindex(int, struct __sk_buff *skb, int);
__attribute__ ((noinline)) __attribute__ ((noinline))
int test_pkt_access_subprog3(int val, struct __sk_buff *skb) int test_pkt_access_subprog3(int val, struct __sk_buff *skb)
{ {
return get_skb_len(skb) * get_skb_ifindex(val, skb, 1); return get_skb_len(skb) * get_skb_ifindex(val, skb, get_constant(123));
} }
__attribute__ ((noinline)) __attribute__ ((noinline))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment