This series adds support attaching freplace BPF programs to multiple targets.
This is needed to support incremental attachment of multiple XDP programs using
the libxdp dispatcher model.
The first three patches are refactoring patches: The first one is a trivial
change to the logging in the verifier, split out to make the subsequent refactor
easier to read. Patch 2 refactors check_attach_btf_id() so that the checks on
program and target compatibility can be reused when attaching to a secondary
location.
Patch 3 changes prog_aux->linked_prog to be an embedded bpf_tracing_link that is
initialised at program load time. This nicely encapsulates both the trampoline
and the prog reference, and moves the release of these references into bpf_link
teardown. At raw_tracepoint_open() time (i.e., when the link is attached), it
will be removed from the extension prog, and primed as a regular bpf_link.
Based on these refactorings, it becomes pretty straight-forward to support
multiple-attach for freplace programs (patch 4). This is simply a matter of
creating a second bpf_tracing_link if a target is supplied to
raw_tracepoint_open().
Patch 5 is a port of Jiri Olsa's patch to support fentry/fexit on freplace
programs. His approach of getting the target type from the target program
reference no longer works after we've gotten rid of linked_prog (because the
bpf_tracing_link reference disappears on attach). Instead, we used the saved
reference to the target prog type that is also used to verify compatibility on
secondary freplace attachment.
Patches 6-7 are tools and libbpf updates, and patches 8-9 are selftests, the
first one for the multi-freplace functionality itself, and the second one is
Jiri's previous selftest for the fentry-to-freplace fix.
With this series, libxdp and xdp-tools can successfully attach multiple programs
one at a time. To play with this, use the 'freplace-multi-attach' branch of
xdp-tools:
$ git clone --recurse-submodules --branch freplace-multi-attach https://github.com/xdp-project/xdp-tools
$ cd xdp-tools
$ make
$ sudo ./xdp-loader/xdp-loader load veth0 lib/testing/xdp_drop.o
$ sudo ./xdp-loader/xdp-loader load veth0 lib/testing/xdp_pass.o
$ sudo ./xdp-loader/xdp-loader status
The series is also available here:
https://git.kernel.org/pub/scm/linux/kernel/git/toke/linux.git/log/?h=bpf-freplace-multi-attach-alt-03
Changelog:
v3:
- Get rid of prog_aux->linked_prog entirely in favour of a bpf_tracing_link
- Incorporate Jiri's fix for attaching fentry to freplace programs
v2:
- Drop the log arguments from bpf_raw_tracepoint_open
- Fix kbot errors
- Rebase to latest bpf-next
---
Jiri Olsa (1):
selftests/bpf: Adding test for arg dereference in extension trace
Toke Høiland-Jørgensen (8):
bpf: change logging calls from verbose() to bpf_log() and use log pointer
bpf: verifier: refactor check_attach_btf_id()
bpf: wrap prog->aux->linked_prog in a bpf_tracing_link
bpf: support attaching freplace programs to multiple attach points
bpf: Fix context type resolving for extension programs
tools: add new members to bpf_attr.raw_tracepoint in bpf.h
libbpf: add support for supplying target to bpf_raw_tracepoint_open()
selftests: add test for multiple attachments of freplace program
include/linux/bpf.h | 33 ++-
include/linux/bpf_verifier.h | 9 +
include/uapi/linux/bpf.h | 6 +-
kernel/bpf/btf.c | 22 +-
kernel/bpf/core.c | 5 +-
kernel/bpf/syscall.c | 161 +++++++++--
kernel/bpf/trampoline.c | 34 ++-
kernel/bpf/verifier.c | 251 ++++++++++--------
tools/include/uapi/linux/bpf.h | 6 +-
tools/lib/bpf/bpf.c | 13 +-
tools/lib/bpf/bpf.h | 9 +
tools/lib/bpf/libbpf.map | 1 +
.../selftests/bpf/prog_tests/fexit_bpf2bpf.c | 171 +++++++++---
.../selftests/bpf/prog_tests/trace_ext.c | 93 +++++++
.../bpf/progs/freplace_get_constant.c | 15 ++
.../selftests/bpf/progs/test_trace_ext.c | 18 ++
.../bpf/progs/test_trace_ext_tracing.c | 25 ++
17 files changed, 683 insertions(+), 189 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/trace_ext.c
create mode 100644 tools/testing/selftests/bpf/progs/freplace_get_constant.c
create mode 100644 tools/testing/selftests/bpf/progs/test_trace_ext.c
create mode 100644 tools/testing/selftests/bpf/progs/test_trace_ext_tracing.c
From: Toke Høiland-Jørgensen <redacted>
In preparation for moving code around, change a bunch of references to
env->log (and the verbose() logging helper) to use bpf_log() and a direct
pointer to struct bpf_verifier_log. While we're touching the function
signature, mark the 'prog' argument to bpf_check_type_match() as const.
Also enhance the bpf_verifier_log_needed() check to handle NULL pointers
for the log struct so we can re-use the code with logging disabled.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
include/linux/bpf.h | 2 +-
include/linux/bpf_verifier.h | 5 +++-
kernel/bpf/btf.c | 6 +++--
kernel/bpf/verifier.c | 48 +++++++++++++++++++++---------------------
4 files changed, 31 insertions(+), 30 deletions(-)
@@ -1394,7 +1394,7 @@ int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,structbpf_reg_state*regs);intbtf_prepare_func_args(structbpf_verifier_env*env,intsubprog,structbpf_reg_state*reg);-intbtf_check_type_match(structbpf_verifier_env*env,structbpf_prog*prog,+intbtf_check_type_match(structbpf_verifier_log*log,conststructbpf_prog*prog,structbtf*btf,conststructbtf_type*t);structbpf_prog*bpf_prog_by_id(u32id);
@@ -4401,7 +4401,7 @@ static int btf_check_func_type_match(struct bpf_verifier_log *log,}/* Compare BTFs of given program with BTF of target program */-intbtf_check_type_match(structbpf_verifier_env*env,structbpf_prog*prog,+intbtf_check_type_match(structbpf_verifier_log*log,conststructbpf_prog*prog,structbtf*btf2,conststructbtf_type*t2){structbtf*btf1=prog->aux->btf;
@@ -4421,7 +4421,7 @@ int btf_check_type_match(struct bpf_verifier_env *env, struct bpf_prog *prog,if(!t1||!btf_type_is_func(t1))return-EFAULT;-returnbtf_check_func_type_match(&env->log,btf1,t1,btf2,t2);+returnbtf_check_func_type_match(log,btf1,t1,btf2,t2);}/* Compare BTF of a function with given bpf_reg_state.
@@ -11043,6 +11043,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)structbpf_prog*prog=env->prog;boolprog_extension=prog->type==BPF_PROG_TYPE_EXT;structbpf_prog*tgt_prog=prog->aux->linked_prog;+structbpf_verifier_log*log=&env->log;u32btf_id=prog->aux->attach_btf_id;constcharprefix[]="btf_trace_";structbtf_func_modelfmodel;
@@ -11070,23 +11071,23 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)return0;if(!btf_id){-verbose(env,"Tracing programs must provide btf_id\n");+bpf_log(log,"Tracing programs must provide btf_id\n");return-EINVAL;}btf=bpf_prog_get_target_btf(prog);if(!btf){-verbose(env,+bpf_log(log,"FENTRY/FEXIT program can only be attached to another program annotated with BTF\n");return-EINVAL;}t=btf_type_by_id(btf,btf_id);if(!t){-verbose(env,"attach_btf_id %u is invalid\n",btf_id);+bpf_log(log,"attach_btf_id %u is invalid\n",btf_id);return-EINVAL;}tname=btf_name_by_offset(btf,t->name_off);if(!tname){-verbose(env,"attach_btf_id %u doesn't have a name\n",btf_id);+bpf_log(log,"attach_btf_id %u doesn't have a name\n",btf_id);return-EINVAL;}if(tgt_prog){
@@ -11098,18 +11099,18 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)break;}if(subprog==-1){-verbose(env,"Subprog %s doesn't exist\n",tname);+bpf_log(log,"Subprog %s doesn't exist\n",tname);return-EINVAL;}conservative=aux->func_info_aux[subprog].unreliable;if(prog_extension){if(conservative){-verbose(env,+bpf_log(log,"Cannot replace static functions\n");return-EINVAL;}if(!prog->jit_requested){-verbose(env,+bpf_log(log,"Extension programs should be JITed\n");return-EINVAL;}
@@ -11117,7 +11118,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)prog->expected_attach_type=tgt_prog->expected_attach_type;}if(!tgt_prog->jited){-verbose(env,"Can attach to only JITed progs\n");+bpf_log(log,"Can attach to only JITed progs\n");return-EINVAL;}if(tgt_prog->type==prog->type){
@@ -11162,17 +11163,17 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)switch(prog->expected_attach_type){caseBPF_TRACE_RAW_TP:if(tgt_prog){-verbose(env,+bpf_log(log,"Only FENTRY/FEXIT progs are attachable to another BPF prog\n");return-EINVAL;}if(!btf_type_is_typedef(t)){-verbose(env,"attach_btf_id %u is not a typedef\n",+bpf_log(log,"attach_btf_id %u is not a typedef\n",btf_id);return-EINVAL;}if(strncmp(prefix,tname,sizeof(prefix)-1)){-verbose(env,"attach_btf_id %u points to wrong type name %s\n",+bpf_log(log,"attach_btf_id %u points to wrong type name %s\n",btf_id,tname);return-EINVAL;}
@@ -11195,7 +11196,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)return0;caseBPF_TRACE_ITER:if(!btf_type_is_func(t)){-verbose(env,"attach_btf_id %u is not a function\n",+bpf_log(log,"attach_btf_id %u is not a function\n",btf_id);return-EINVAL;}
@@ -11206,8 +11207,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)prog->aux->attach_func_proto=t;if(!bpf_iter_prog_supported(prog))return-EINVAL;-ret=btf_distill_func_proto(&env->log,btf,t,-tname,&fmodel);+ret=btf_distill_func_proto(log,btf,t,tname,&fmodel);returnret;default:if(!prog_extension)
@@ -11219,18 +11219,18 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)caseBPF_TRACE_FEXIT:prog->aux->attach_func_name=tname;if(prog->type==BPF_PROG_TYPE_LSM){-ret=bpf_lsm_verify_prog(&env->log,prog);+ret=bpf_lsm_verify_prog(log,prog);if(ret<0)returnret;}if(!btf_type_is_func(t)){-verbose(env,"attach_btf_id %u is not a function\n",+bpf_log(log,"attach_btf_id %u is not a function\n",btf_id);return-EINVAL;}if(prog_extension&&-btf_check_type_match(env,prog,btf,t))+btf_check_type_match(log,prog,btf,t))return-EINVAL;t=btf_type_by_id(btf,t->type);if(!btf_type_is_func_proto(t))
@@ -11249,7 +11249,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)prog->aux->attach_func_proto=NULL;t=NULL;}-ret=btf_distill_func_proto(&env->log,btf,t,+ret=btf_distill_func_proto(log,btf,t,tname,&tr->func.model);if(ret<0)gotoout;
@@ -11261,7 +11261,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)}else{addr=kallsyms_lookup_name(tname);if(!addr){-verbose(env,+bpf_log(log,"The address of function %s cannot be found\n",tname);ret=-ENOENT;
@@ -11291,12 +11291,12 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)break;}if(ret)-verbose(env,"%s is not sleepable\n",+bpf_log(log,"%s is not sleepable\n",prog->aux->attach_func_name);}elseif(prog->expected_attach_type==BPF_MODIFY_RETURN){ret=check_attach_modify_return(prog,addr);if(ret)-verbose(env,"%s() is not modifiable\n",+bpf_log(log,"%s() is not modifiable\n",prog->aux->attach_func_name);}if(ret)
From: Toke Høiland-Jørgensen <redacted>
The bpf_tracing_link structure is a convenient data structure to contain
the reference to a linked program; in preparation for supporting multiple
attachments for the same freplace program, move the linked_prog in
prog->aux into a bpf_tracing_link wrapper.
With this change, it is no longer possible to attach the same tracing
program multiple times (detaching in-between), since the reference from the
tracing program to the target disappears on the first attach. However,
since the next patch will let the caller supply an attach target, that will
also make it possible to attach to the same place multiple times.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
include/linux/bpf.h | 21 +++++++++---
kernel/bpf/btf.c | 13 +++++---
kernel/bpf/core.c | 5 +--
kernel/bpf/syscall.c | 81 +++++++++++++++++++++++++++++++++++++----------
kernel/bpf/trampoline.c | 12 ++-----
kernel/bpf/verifier.c | 13 +++++---
6 files changed, 102 insertions(+), 43 deletions(-)
@@ -740,14 +743,13 @@ struct bpf_prog_aux {u32max_rdonly_access;u32max_rdwr_access;conststructbpf_ctx_arg_aux*ctx_arg_info;-structbpf_prog*linked_prog;+structbpf_tracing_link*tgt_link;boolverifier_zext;/* Zero extensions has been inserted by verifier. */booloffload_requested;boolattach_btf_trace;/* true if attaching to BTF-enabled raw tp */boolfunc_proto_unreliable;boolsleepable;enumbpf_tramp_prog_typetrampoline_prog_type;-structbpf_trampoline*trampoline;structhlist_nodetramp_hlist;/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */conststructbtf_type*attach_func_proto;
@@ -3733,14 +3733,17 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,structbpf_insn_access_aux*info){conststructbtf_type*t=prog->aux->attach_func_proto;-structbpf_prog*tgt_prog=prog->aux->linked_prog;structbtf*btf=bpf_prog_get_target_btf(prog);constchar*tname=prog->aux->attach_func_name;structbpf_verifier_log*log=info->log;+structbpf_prog*tgt_prog=NULL;conststructbtf_param*args;u32nr_args,arg;inti,ret;+if(prog->aux->tgt_link)+tgt_prog=prog->aux->tgt_link->tgt_prog;+if(off%8){bpf_log(log,"func '%s' offset %d is not multiple of 8\n",tname,off);
@@ -4572,7 +4575,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,return-EFAULT;}if(prog_type==BPF_PROG_TYPE_EXT)-prog_type=prog->aux->linked_prog->type;+prog_type=prog->aux->tgt_link->tgt_prog->type;t=btf_type_by_id(btf,t->type);if(!t||!btf_type_is_func_proto(t)){
@@ -2095,10 +2095,13 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)/* last field in 'union bpf_attr' used by this command */#define BPF_PROG_LOAD_LAST_FIELD attach_prog_fd+staticstructbpf_tracing_link*bpf_tracing_link_create(structbpf_prog*prog,+structbpf_prog*tgt_prog);+staticintbpf_prog_load(unionbpf_attr*attr,unionbpf_attr__user*uattr){enumbpf_prog_typetype=attr->prog_type;-structbpf_prog*prog;+structbpf_prog*prog,*tgt_prog=NULL;interr;charlicense[128];boolis_gpl;
@@ -2154,14 +2157,27 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)prog->expected_attach_type=attr->expected_attach_type;prog->aux->attach_btf_id=attr->attach_btf_id;if(attr->attach_prog_fd){-structbpf_prog*tgt_prog;-tgt_prog=bpf_prog_get(attr->attach_prog_fd);if(IS_ERR(tgt_prog)){err=PTR_ERR(tgt_prog);gotofree_prog_nouncharge;}-prog->aux->linked_prog=tgt_prog;+}++if(tgt_prog||prog->aux->attach_btf_id){+structbpf_tracing_link*link;++link=bpf_tracing_link_create(prog,tgt_prog);+if(IS_ERR(link)){+err=PTR_ERR(link);+if(tgt_prog)+bpf_prog_put(tgt_prog);+gotofree_prog_nouncharge;+}+prog->aux->tgt_link=link;++/* avoid circular ref - will be set on link activation */+link->link.prog=NULL;}prog->aux->offload_requested=!!attr->prog_ifindex;
@@ -2574,14 +2614,16 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)gotoout_put_prog;}-link=kzalloc(sizeof(*link),GFP_USER);+link=READ_ONCE(prog->aux->tgt_link);if(!link){-err=-ENOMEM;+err=-ENOENT;+gotoout_put_prog;+}+olink=cmpxchg(&prog->aux->tgt_link,link,NULL);+if(olink!=link){+err=-ENOENT;gotoout_put_prog;}-bpf_link_init(&link->link,BPF_LINK_TYPE_TRACING,-&bpf_tracing_link_lops,prog);-link->attach_type=prog->expected_attach_type;err=bpf_link_prime(&link->link,&link_primer);if(err){
@@ -2589,12 +2631,17 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)gotoout_put_prog;}-err=bpf_trampoline_link_prog(prog);+err=bpf_trampoline_link_prog(prog,link->trampoline);if(err){bpf_link_cleanup(&link_primer);gotoout_put_prog;}+/* at this point the link is no longer referenced from struct bpf_prog,+*sowecanpopulatethiswithoutintroducingacircularreference.+*/+link->link.prog=prog;+returnbpf_link_settle(&link_primer);out_put_prog:bpf_prog_put(prog);
@@ -301,7 +299,7 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)}hlist_add_head(&prog->aux->tramp_hlist,&tr->progs_hlist[kind]);tr->progs_cnt[kind]++;-err=bpf_trampoline_update(prog->aux->trampoline);+err=bpf_trampoline_update(tr);if(err){hlist_del(&prog->aux->tramp_hlist);tr->progs_cnt[kind]--;
@@ -312,13 +310,11 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)}/* bpf_trampoline_unlink_prog() should never fail. */-intbpf_trampoline_unlink_prog(structbpf_prog*prog)+intbpf_trampoline_unlink_prog(structbpf_prog*prog,structbpf_trampoline*tr){enumbpf_tramp_prog_typekind;-structbpf_trampoline*tr;interr;-tr=prog->aux->trampoline;kind=bpf_attach_type_to_tramp(prog);mutex_lock(&tr->mutex);if(kind==BPF_TRAMP_REPLACE){
@@ -330,7 +326,7 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)}hlist_del(&prog->aux->tramp_hlist);tr->progs_cnt[kind]--;-err=bpf_trampoline_update(prog->aux->trampoline);+err=bpf_trampoline_update(tr);out:mutex_unlock(&tr->mutex);returnerr;
@@ -11266,8 +11268,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,staticintcheck_attach_btf_id(structbpf_verifier_env*env){structbpf_prog*prog=env->prog;-structbpf_prog*tgt_prog=prog->aux->linked_prog;u32btf_id=prog->aux->attach_btf_id;+structbpf_prog*tgt_prog=NULL;structbtf_func_modelfmodel;conststructbtf_type*t;constchar*tname;
@@ -11275,6 +11277,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)intret;u64key;+if(prog->aux->tgt_link)+tgt_prog=prog->aux->tgt_link->tgt_prog;+if(prog->aux->sleepable&&prog->type!=BPF_PROG_TYPE_TRACING&&prog->type!=BPF_PROG_TYPE_LSM){verbose(env,"Only fentry/fexit/fmod_ret and lsm programs can be sleepable\n");
@@ -11334,7 +11339,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)returnret;}returnbpf_trampoline_get(key,(void*)addr,&fmodel,-&prog->aux->trampoline);+&prog->aux->tgt_link->trampoline);}}
On Fri, Sep 11, 2020 at 3:00 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted hunk
From: Toke Høiland-Jørgensen <redacted>
The bpf_tracing_link structure is a convenient data structure to contain
the reference to a linked program; in preparation for supporting multiple
attachments for the same freplace program, move the linked_prog in
prog->aux into a bpf_tracing_link wrapper.
With this change, it is no longer possible to attach the same tracing
program multiple times (detaching in-between), since the reference from the
tracing program to the target disappears on the first attach. However,
since the next patch will let the caller supply an attach target, that will
also make it possible to attach to the same place multiple times.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
include/linux/bpf.h | 21 +++++++++---
kernel/bpf/btf.c | 13 +++++---
kernel/bpf/core.c | 5 +--
kernel/bpf/syscall.c | 81 +++++++++++++++++++++++++++++++++++++----------
kernel/bpf/trampoline.c | 12 ++-----
kernel/bpf/verifier.c | 13 +++++---
6 files changed, 102 insertions(+), 43 deletions(-)
@@ -740,14 +743,13 @@ struct bpf_prog_aux {u32max_rdonly_access;u32max_rdwr_access;conststructbpf_ctx_arg_aux*ctx_arg_info;-structbpf_prog*linked_prog;+structbpf_tracing_link*tgt_link;boolverifier_zext;/* Zero extensions has been inserted by verifier. */booloffload_requested;boolattach_btf_trace;/* true if attaching to BTF-enabled raw tp */boolfunc_proto_unreliable;boolsleepable;enumbpf_tramp_prog_typetrampoline_prog_type;-structbpf_trampoline*trampoline;structhlist_nodetramp_hlist;/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */conststructbtf_type*attach_func_proto;
@@ -3733,14 +3733,17 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,structbpf_insn_access_aux*info){conststructbtf_type*t=prog->aux->attach_func_proto;-structbpf_prog*tgt_prog=prog->aux->linked_prog;structbtf*btf=bpf_prog_get_target_btf(prog);constchar*tname=prog->aux->attach_func_name;structbpf_verifier_log*log=info->log;+structbpf_prog*tgt_prog=NULL;conststructbtf_param*args;u32nr_args,arg;inti,ret;+if(prog->aux->tgt_link)+tgt_prog=prog->aux->tgt_link->tgt_prog;+if(off%8){bpf_log(log,"func '%s' offset %d is not multiple of 8\n",tname,off);
@@ -4572,7 +4575,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,return-EFAULT;}if(prog_type==BPF_PROG_TYPE_EXT)-prog_type=prog->aux->linked_prog->type;+prog_type=prog->aux->tgt_link->tgt_prog->type;t=btf_type_by_id(btf,t->type);if(!t||!btf_type_is_func_proto(t)){
Until the link is primed, you shouldn't bpf_link_put() it. At this
stage the link itself is just a piece of memory that needs to be
kfree()'d. And your circular dependency problem doesn't exist anymore.
You'll have to put a trampoline and target prog manually here, though
(but you have a similar problem below as well, so might just have a
small helper to do this). But I think it's simpler that relying on an
artificial "defunct" state of not-yet-activated bpf_link, which you do
with the dance around link->prog = NULL.
@@ -2095,10 +2095,13 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)/* last field in 'union bpf_attr' used by this command */#define BPF_PROG_LOAD_LAST_FIELD attach_prog_fd+staticstructbpf_tracing_link*bpf_tracing_link_create(structbpf_prog*prog,+structbpf_prog*tgt_prog);+staticintbpf_prog_load(unionbpf_attr*attr,unionbpf_attr__user*uattr){enumbpf_prog_typetype=attr->prog_type;-structbpf_prog*prog;+structbpf_prog*prog,*tgt_prog=NULL;interr;charlicense[128];boolis_gpl;
@@ -2154,14 +2157,27 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)prog->expected_attach_type=attr->expected_attach_type;prog->aux->attach_btf_id=attr->attach_btf_id;if(attr->attach_prog_fd){-structbpf_prog*tgt_prog;-tgt_prog=bpf_prog_get(attr->attach_prog_fd);if(IS_ERR(tgt_prog)){err=PTR_ERR(tgt_prog);gotofree_prog_nouncharge;}-prog->aux->linked_prog=tgt_prog;+}++if(tgt_prog||prog->aux->attach_btf_id){+structbpf_tracing_link*link;++link=bpf_tracing_link_create(prog,tgt_prog);+if(IS_ERR(link)){+err=PTR_ERR(link);+if(tgt_prog)+bpf_prog_put(tgt_prog);+gotofree_prog_nouncharge;+}+prog->aux->tgt_link=link;++/* avoid circular ref - will be set on link activation */+link->link.prog=NULL;
See above. I think this might work, but it's sort of an entire new
pattern for links and I'd like to avoid having to think about defunct
-> active -> defunct bpf_link state transitions. All you have to do is
clean up the not-yet-activated link appropriately, but otherwise no
unnecessary link internals manipulations are necessary.
if priming errors out, you need to put target prog and trampoline,
kfree(link) won't do it (and calling bpf_link_cleanup() is not correct
before priming). See above as well.
BTW, one interesting side effect of all this is that if your initial
attach failed, you won't be able to try again, because
prog->aux->tgt_link is gone. If that's the problem, we'll need to
introduce locking and copy that link, try to attach, then clear out
prog->aug->tgt_link only if we succeeded. Just bringing this up, as it
might not be obvious (or I might be wrong :).
quoted hunk
@@ -2589,12 +2631,17 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) goto out_put_prog; }- err = bpf_trampoline_link_prog(prog);+ err = bpf_trampoline_link_prog(prog, link->trampoline); if (err) { bpf_link_cleanup(&link_primer); goto out_put_prog; }+ /* at this point the link is no longer referenced from struct bpf_prog,+ * so we can populate this without introducing a circular reference.+ */+ link->link.prog = prog;
this won't be necessary either, as per above discussion on circular dependencies
On Fri, Sep 11, 2020 at 3:00 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted
From: Toke Høiland-Jørgensen <redacted>
The bpf_tracing_link structure is a convenient data structure to contain
the reference to a linked program; in preparation for supporting multiple
attachments for the same freplace program, move the linked_prog in
prog->aux into a bpf_tracing_link wrapper.
With this change, it is no longer possible to attach the same tracing
program multiple times (detaching in-between), since the reference from the
tracing program to the target disappears on the first attach. However,
since the next patch will let the caller supply an attach target, that will
also make it possible to attach to the same place multiple times.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
include/linux/bpf.h | 21 +++++++++---
kernel/bpf/btf.c | 13 +++++---
kernel/bpf/core.c | 5 +--
kernel/bpf/syscall.c | 81 +++++++++++++++++++++++++++++++++++++----------
kernel/bpf/trampoline.c | 12 ++-----
kernel/bpf/verifier.c | 13 +++++---
6 files changed, 102 insertions(+), 43 deletions(-)
@@ -740,14 +743,13 @@ struct bpf_prog_aux {u32max_rdonly_access;u32max_rdwr_access;conststructbpf_ctx_arg_aux*ctx_arg_info;-structbpf_prog*linked_prog;+structbpf_tracing_link*tgt_link;boolverifier_zext;/* Zero extensions has been inserted by verifier. */booloffload_requested;boolattach_btf_trace;/* true if attaching to BTF-enabled raw tp */boolfunc_proto_unreliable;boolsleepable;enumbpf_tramp_prog_typetrampoline_prog_type;-structbpf_trampoline*trampoline;structhlist_nodetramp_hlist;/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */conststructbtf_type*attach_func_proto;
@@ -3733,14 +3733,17 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,structbpf_insn_access_aux*info){conststructbtf_type*t=prog->aux->attach_func_proto;-structbpf_prog*tgt_prog=prog->aux->linked_prog;structbtf*btf=bpf_prog_get_target_btf(prog);constchar*tname=prog->aux->attach_func_name;structbpf_verifier_log*log=info->log;+structbpf_prog*tgt_prog=NULL;conststructbtf_param*args;u32nr_args,arg;inti,ret;+if(prog->aux->tgt_link)+tgt_prog=prog->aux->tgt_link->tgt_prog;+if(off%8){bpf_log(log,"func '%s' offset %d is not multiple of 8\n",tname,off);
@@ -4572,7 +4575,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,return-EFAULT;}if(prog_type==BPF_PROG_TYPE_EXT)-prog_type=prog->aux->linked_prog->type;+prog_type=prog->aux->tgt_link->tgt_prog->type;t=btf_type_by_id(btf,t->type);if(!t||!btf_type_is_func_proto(t)){
Until the link is primed, you shouldn't bpf_link_put() it. At this
stage the link itself is just a piece of memory that needs to be
kfree()'d. And your circular dependency problem doesn't exist anymore.
You'll have to put a trampoline and target prog manually here, though
(but you have a similar problem below as well, so might just have a
small helper to do this). But I think it's simpler that relying on an
artificial "defunct" state of not-yet-activated bpf_link, which you do
with the dance around link->prog = NULL.
Yeah, makes sense. I initially figured that would be 'breaking the
abstraction' of bpf_link, but I ended up having to do that anyway, so
you're right I might as well treat it as a piece of memory here.
[...]
quoted
@@ -2574,14 +2614,16 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) goto out_put_prog; }- link = kzalloc(sizeof(*link), GFP_USER);+ link = READ_ONCE(prog->aux->tgt_link); if (!link) {- err = -ENOMEM;+ err = -ENOENT;+ goto out_put_prog;+ }+ olink = cmpxchg(&prog->aux->tgt_link, link, NULL);+ if (olink != link) {+ err = -ENOENT; goto out_put_prog; }
Wouldn't single xchg to NULL be sufficient to achieve the same?
READ_ONCE + cmpxchg seems unnecessary to me.
It would, but in the next patch I'm introducing a check on the contents
of the link before cmpxchg'ing it, so figured it was easier to just use
the same pattern here.
if priming errors out, you need to put target prog and trampoline,
kfree(link) won't do it (and calling bpf_link_cleanup() is not correct
before priming). See above as well.
Ah yes, good catch!
BTW, one interesting side effect of all this is that if your initial
attach failed, you won't be able to try again, because
prog->aux->tgt_link is gone. If that's the problem, we'll need to
introduce locking and copy that link, try to attach, then clear out
prog->aug->tgt_link only if we succeeded. Just bringing this up, as it
might not be obvious (or I might be wrong :).
Yeah, did think about that. From a purist PoV you're right that a
"destructive attempt" is not ideal; but we already agreed that clearing
out the link on attach was an acceptable change in behaviour. And I
figured that a failure in link_prim() or trampoline_link_prog() would be
quite rare, so not something we'd want to expend a lot of effort
ensuring was really atomic...
-Toke
From: Toke Høiland-Jørgensen <redacted>
The check_attach_btf_id() function really does three things:
1. It performs a bunch of checks on the program to ensure that the
attachment is valid.
2. It stores a bunch of state about the attachment being requested in
the verifier environment and struct bpf_prog objects.
3. It allocates a trampoline for the attachment.
This patch splits out (1.) and (3.) into separate functions in preparation
for reusing them when the actual attachment is happening (in the
raw_tracepoint_open syscall operation), which will allow tracing programs
to have multiple (compatible) attachments.
No functional change is intended with this patch.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
include/linux/bpf.h | 9 ++
include/linux/bpf_verifier.h | 9 ++
kernel/bpf/trampoline.c | 22 ++++
kernel/bpf/verifier.c | 233 +++++++++++++++++++++++-------------------
4 files changed, 170 insertions(+), 103 deletions(-)
@@ -11038,43 +11038,29 @@ static int check_non_sleepable_error_inject(u32 btf_id)returnbtf_id_set_contains(&btf_non_sleepable_error_inject,btf_id);}-staticintcheck_attach_btf_id(structbpf_verifier_env*env)+intbpf_check_attach_target(structbpf_verifier_log*log,+conststructbpf_prog*prog,+conststructbpf_prog*tgt_prog,+u32btf_id,+structbtf_func_model*fmodel,+long*tgt_addr,+constchar**tgt_name,+conststructbtf_type**tgt_type){-structbpf_prog*prog=env->prog;boolprog_extension=prog->type==BPF_PROG_TYPE_EXT;-structbpf_prog*tgt_prog=prog->aux->linked_prog;-structbpf_verifier_log*log=&env->log;-u32btf_id=prog->aux->attach_btf_id;constcharprefix[]="btf_trace_";-structbtf_func_modelfmodel;intret=0,subprog=-1,i;-structbpf_trampoline*tr;conststructbtf_type*t;boolconservative=true;constchar*tname;structbtf*btf;-longaddr;-u64key;--if(prog->aux->sleepable&&prog->type!=BPF_PROG_TYPE_TRACING&&-prog->type!=BPF_PROG_TYPE_LSM){-verbose(env,"Only fentry/fexit/fmod_ret and lsm programs can be sleepable\n");-return-EINVAL;-}--if(prog->type==BPF_PROG_TYPE_STRUCT_OPS)-returncheck_struct_ops_btf_id(env);--if(prog->type!=BPF_PROG_TYPE_TRACING&&-prog->type!=BPF_PROG_TYPE_LSM&&-!prog_extension)-return0;+longaddr=0;if(!btf_id){bpf_log(log,"Tracing programs must provide btf_id\n");return-EINVAL;}-btf=bpf_prog_get_target_btf(prog);+btf=tgt_prog?tgt_prog->aux->btf:btf_vmlinux;if(!btf){bpf_log(log,"FENTRY/FEXIT program can only be attached to another program annotated with BTF\n");
@@ -11114,8 +11100,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)"Extension programs should be JITed\n");return-EINVAL;}-env->ops=bpf_verifier_ops[tgt_prog->type];-prog->expected_attach_type=tgt_prog->expected_attach_type;}if(!tgt_prog->jited){bpf_log(log,"Can attach to only JITed progs\n");
@@ -11187,13 +11169,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)/* should never happen in valid vmlinux build */return-EINVAL;-/* remember two read only pointers that are valid for-*thelifetimeofthekernel-*/-prog->aux->attach_func_name=tname;-prog->aux->attach_func_proto=t;-prog->aux->attach_btf_trace=true;-return0;+break;caseBPF_TRACE_ITER:if(!btf_type_is_func(t)){bpf_log(log,"attach_btf_id %u is not a function\n",
@@ -11203,12 +11179,10 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)t=btf_type_by_id(btf,t->type);if(!btf_type_is_func_proto(t))return-EINVAL;-prog->aux->attach_func_name=tname;-prog->aux->attach_func_proto=t;-if(!bpf_iter_prog_supported(prog))-return-EINVAL;-ret=btf_distill_func_proto(log,btf,t,tname,&fmodel);-returnret;+ret=btf_distill_func_proto(log,btf,t,tname,fmodel);+if(ret)+returnret;+break;default:if(!prog_extension)return-EINVAL;
@@ -11217,13 +11191,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)caseBPF_LSM_MAC:caseBPF_TRACE_FENTRY:caseBPF_TRACE_FEXIT:-prog->aux->attach_func_name=tname;-if(prog->type==BPF_PROG_TYPE_LSM){-ret=bpf_lsm_verify_prog(log,prog);-if(ret<0)-returnret;-}-if(!btf_type_is_func(t)){bpf_log(log,"attach_btf_id %u is not a function\n",btf_id);
@@ -11235,24 +11202,14 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)t=btf_type_by_id(btf,t->type);if(!btf_type_is_func_proto(t))return-EINVAL;-tr=bpf_trampoline_lookup(key);-if(!tr)-return-ENOMEM;-/* t is either vmlinux type or another program's type */-prog->aux->attach_func_proto=t;-mutex_lock(&tr->mutex);-if(tr->func.addr){-prog->aux->trampoline=tr;-gotoout;-}-if(tgt_prog&&conservative){-prog->aux->attach_func_proto=NULL;++if(tgt_prog&&conservative)t=NULL;-}-ret=btf_distill_func_proto(log,btf,t,-tname,&tr->func.model);++ret=btf_distill_func_proto(log,btf,t,tname,fmodel);if(ret<0)-gotoout;+returnret;+if(tgt_prog){if(subprog==0)addr=(long)tgt_prog->bpf_func;
@@ -11264,50 +11221,120 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)bpf_log(log,"The address of function %s cannot be found\n",tname);-ret=-ENOENT;-gotoout;+return-ENOENT;}}+break;+}-if(prog->aux->sleepable){-ret=-EINVAL;-switch(prog->type){-caseBPF_PROG_TYPE_TRACING:-/* fentry/fexit/fmod_ret progs can be sleepable only if they are-*attachedtoALLOW_ERROR_INJECTIONandarenotindenylist.-*/-if(!check_non_sleepable_error_inject(btf_id)&&-within_error_injection_list(addr))-ret=0;-break;-caseBPF_PROG_TYPE_LSM:-/* LSM progs check that they are attached to bpf_lsm_*() funcs.-*Onlysomeofthemaresleepable.-*/-if(check_sleepable_lsm_hook(btf_id))-ret=0;-break;-default:-break;-}-if(ret)-bpf_log(log,"%s is not sleepable\n",-prog->aux->attach_func_name);-}elseif(prog->expected_attach_type==BPF_MODIFY_RETURN){-ret=check_attach_modify_return(prog,addr);-if(ret)-bpf_log(log,"%s() is not modifiable\n",-prog->aux->attach_func_name);+if(prog->aux->sleepable){+ret=-EINVAL;+switch(prog->type){+caseBPF_PROG_TYPE_TRACING:+/* fentry/fexit/fmod_ret progs can be sleepable only if they are+*attachedtoALLOW_ERROR_INJECTIONandarenotindenylist.+*/+if(!check_non_sleepable_error_inject(btf_id)&&+within_error_injection_list(addr))+ret=0;+break;+caseBPF_PROG_TYPE_LSM:+/* LSM progs check that they are attached to bpf_lsm_*() funcs.+*Onlysomeofthemaresleepable.+*/+if(check_sleepable_lsm_hook(btf_id))+ret=0;+break;+default:+break;}-if(ret)-gotoout;-tr->func.addr=(void*)addr;-prog->aux->trampoline=tr;-out:-mutex_unlock(&tr->mutex);-if(ret)-bpf_trampoline_put(tr);+if(ret){+bpf_log(log,"%s is not sleepable\n",+prog->aux->attach_func_name);+returnret;+}+}++*tgt_addr=addr;+if(tgt_name)+*tgt_name=tname;+if(tgt_type)+*tgt_type=t;+return0;+}++staticintcheck_attach_btf_id(structbpf_verifier_env*env)+{+structbpf_prog*prog=env->prog;+structbpf_prog*tgt_prog=prog->aux->linked_prog;+u32btf_id=prog->aux->attach_btf_id;+structbtf_func_modelfmodel;+conststructbtf_type*t;+constchar*tname;+longaddr;+intret;+u64key;++if(prog->aux->sleepable&&prog->type!=BPF_PROG_TYPE_TRACING&&+prog->type!=BPF_PROG_TYPE_LSM){+verbose(env,"Only fentry/fexit/fmod_ret and lsm programs can be sleepable\n");+return-EINVAL;+}++if(prog->type==BPF_PROG_TYPE_STRUCT_OPS)+returncheck_struct_ops_btf_id(env);++if(prog->type!=BPF_PROG_TYPE_TRACING&&+prog->type!=BPF_PROG_TYPE_LSM&&+prog->type!=BPF_PROG_TYPE_EXT)+return0;++ret=bpf_check_attach_target(&env->log,prog,tgt_prog,btf_id,+&fmodel,&addr,&tname,&t);+if(ret)returnret;++if(tgt_prog){+if(prog->type==BPF_PROG_TYPE_EXT){+env->ops=bpf_verifier_ops[tgt_prog->type];+prog->expected_attach_type=+tgt_prog->expected_attach_type;+}+key=((u64)tgt_prog->aux->id)<<32|btf_id;+}else{+key=btf_id;+}++prog->aux->attach_func_proto=t;+prog->aux->attach_func_name=tname;++switch(prog->expected_attach_type){+caseBPF_TRACE_RAW_TP:+/* remember two read only pointers that are valid for+*thelifetimeofthekernel+*/+prog->aux->attach_btf_trace=true;+return0;+caseBPF_TRACE_ITER:+if(!bpf_iter_prog_supported(prog))+return-EINVAL;+return0;+caseBPF_MODIFY_RETURN:+ret=check_attach_modify_return(prog,addr);+if(ret){+verbose(env,"%s() is not modifiable\n",+prog->aux->attach_func_name);+returnret;+}+fallthrough;+default:+if(prog->type==BPF_PROG_TYPE_LSM){+ret=bpf_lsm_verify_prog(&env->log,prog);+if(ret<0)+returnret;+}+returnbpf_trampoline_get(key,(void*)addr,&fmodel,+&prog->aux->trampoline);}}
On Fri, Sep 11, 2020 at 3:00 AM Toke Høiland-Jørgensen [off-list ref] wrote:
From: Toke Høiland-Jørgensen <redacted>
The check_attach_btf_id() function really does three things:
1. It performs a bunch of checks on the program to ensure that the
attachment is valid.
2. It stores a bunch of state about the attachment being requested in
the verifier environment and struct bpf_prog objects.
3. It allocates a trampoline for the attachment.
This patch splits out (1.) and (3.) into separate functions in preparation
for reusing them when the actual attachment is happening (in the
raw_tracepoint_open syscall operation), which will allow tracing programs
to have multiple (compatible) attachments.
No functional change is intended with this patch.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
I can't tell if there are any functional changes or not, tbh. The
logic is quite complicated and full of intricate details. I did leave
some suggestions on hopefully simplifying code flow in some places
(and ensuring it's harder to break it on future changes), but I hope
Alexei will give it a very thorough review and check that none of the
subtle details broke.
@@ -616,6 +616,9 @@ static __always_inline unsigned int bpf_dispatcher_nop_func(structbpf_trampoline*bpf_trampoline_lookup(u64key);intbpf_trampoline_link_prog(structbpf_prog*prog);intbpf_trampoline_unlink_prog(structbpf_prog*prog);+intbpf_trampoline_get(u64key,void*addr,+structbtf_func_model*fmodel,+structbpf_trampoline**trampoline);
Given trampoline is clearly a single output parameter, having `struct
bpf_trampoline *` as a return type and encoding error with ERR_PTR()
seems like a more common and natural way (within kernel code)?
Though I do get why you did it this way and with my below suggestion
of having separate switch clauses this reduces duplicated code, so I
guess I don't mind it now.
@@ -11038,43 +11038,29 @@ static int check_non_sleepable_error_inject(u32 btf_id)returnbtf_id_set_contains(&btf_non_sleepable_error_inject,btf_id);}-staticintcheck_attach_btf_id(structbpf_verifier_env*env)+intbpf_check_attach_target(structbpf_verifier_log*log,+conststructbpf_prog*prog,+conststructbpf_prog*tgt_prog,+u32btf_id,+structbtf_func_model*fmodel,+long*tgt_addr,+constchar**tgt_name,+conststructbtf_type**tgt_type){-structbpf_prog*prog=env->prog;boolprog_extension=prog->type==BPF_PROG_TYPE_EXT;-structbpf_prog*tgt_prog=prog->aux->linked_prog;-structbpf_verifier_log*log=&env->log;-u32btf_id=prog->aux->attach_btf_id;constcharprefix[]="btf_trace_";-structbtf_func_modelfmodel;intret=0,subprog=-1,i;-structbpf_trampoline*tr;conststructbtf_type*t;boolconservative=true;constchar*tname;structbtf*btf;-longaddr;-u64key;--if(prog->aux->sleepable&&prog->type!=BPF_PROG_TYPE_TRACING&&-prog->type!=BPF_PROG_TYPE_LSM){-verbose(env,"Only fentry/fexit/fmod_ret and lsm programs can be sleepable\n");-return-EINVAL;-}--if(prog->type==BPF_PROG_TYPE_STRUCT_OPS)-returncheck_struct_ops_btf_id(env);--if(prog->type!=BPF_PROG_TYPE_TRACING&&-prog->type!=BPF_PROG_TYPE_LSM&&-!prog_extension)-return0;+longaddr=0;if(!btf_id){bpf_log(log,"Tracing programs must provide btf_id\n");return-EINVAL;}-btf=bpf_prog_get_target_btf(prog);+btf=tgt_prog?tgt_prog->aux->btf:btf_vmlinux;if(!btf){bpf_log(log,"FENTRY/FEXIT program can only be attached to another program annotated with BTF\n");
@@ -11114,8 +11100,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)"Extension programs should be JITed\n");return-EINVAL;}-env->ops=bpf_verifier_ops[tgt_prog->type];-prog->expected_attach_type=tgt_prog->expected_attach_type;}if(!tgt_prog->jited){bpf_log(log,"Can attach to only JITed progs\n");
@@ -11187,13 +11169,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)/* should never happen in valid vmlinux build */return-EINVAL;-/* remember two read only pointers that are valid for-*thelifetimeofthekernel-*/-prog->aux->attach_func_name=tname;-prog->aux->attach_func_proto=t;-prog->aux->attach_btf_trace=true;-return0;+break;caseBPF_TRACE_ITER:if(!btf_type_is_func(t)){bpf_log(log,"attach_btf_id %u is not a function\n",
@@ -11203,12 +11179,10 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)t=btf_type_by_id(btf,t->type);if(!btf_type_is_func_proto(t))return-EINVAL;-prog->aux->attach_func_name=tname;-prog->aux->attach_func_proto=t;-if(!bpf_iter_prog_supported(prog))-return-EINVAL;-ret=btf_distill_func_proto(log,btf,t,tname,&fmodel);-returnret;+ret=btf_distill_func_proto(log,btf,t,tname,fmodel);+if(ret)+returnret;+break;default:if(!prog_extension)return-EINVAL;
@@ -11217,13 +11191,6 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)caseBPF_LSM_MAC:caseBPF_TRACE_FENTRY:caseBPF_TRACE_FEXIT:-prog->aux->attach_func_name=tname;-if(prog->type==BPF_PROG_TYPE_LSM){-ret=bpf_lsm_verify_prog(log,prog);-if(ret<0)-returnret;-}-if(!btf_type_is_func(t)){bpf_log(log,"attach_btf_id %u is not a function\n",btf_id);
@@ -11235,24 +11202,14 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)t=btf_type_by_id(btf,t->type);if(!btf_type_is_func_proto(t))return-EINVAL;-tr=bpf_trampoline_lookup(key);-if(!tr)-return-ENOMEM;-/* t is either vmlinux type or another program's type */-prog->aux->attach_func_proto=t;-mutex_lock(&tr->mutex);-if(tr->func.addr){-prog->aux->trampoline=tr;-gotoout;-}-if(tgt_prog&&conservative){-prog->aux->attach_func_proto=NULL;++if(tgt_prog&&conservative)t=NULL;-}-ret=btf_distill_func_proto(log,btf,t,-tname,&tr->func.model);++ret=btf_distill_func_proto(log,btf,t,tname,fmodel);if(ret<0)-gotoout;+returnret;+if(tgt_prog){if(subprog==0)addr=(long)tgt_prog->bpf_func;
@@ -11264,50 +11221,120 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)bpf_log(log,"The address of function %s cannot be found\n",tname);-ret=-ENOENT;-gotoout;+return-ENOENT;}}+break;+}-if(prog->aux->sleepable){-ret=-EINVAL;-switch(prog->type){-caseBPF_PROG_TYPE_TRACING:-/* fentry/fexit/fmod_ret progs can be sleepable only if they are-*attachedtoALLOW_ERROR_INJECTIONandarenotindenylist.-*/-if(!check_non_sleepable_error_inject(btf_id)&&-within_error_injection_list(addr))-ret=0;-break;-caseBPF_PROG_TYPE_LSM:-/* LSM progs check that they are attached to bpf_lsm_*() funcs.-*Onlysomeofthemaresleepable.-*/-if(check_sleepable_lsm_hook(btf_id))-ret=0;-break;-default:-break;-}-if(ret)-bpf_log(log,"%s is not sleepable\n",-prog->aux->attach_func_name);-}elseif(prog->expected_attach_type==BPF_MODIFY_RETURN){-ret=check_attach_modify_return(prog,addr);-if(ret)-bpf_log(log,"%s() is not modifiable\n",-prog->aux->attach_func_name);+if(prog->aux->sleepable){+ret=-EINVAL;+switch(prog->type){+caseBPF_PROG_TYPE_TRACING:+/* fentry/fexit/fmod_ret progs can be sleepable only if they are+*attachedtoALLOW_ERROR_INJECTIONandarenotindenylist.+*/+if(!check_non_sleepable_error_inject(btf_id)&&+within_error_injection_list(addr))+ret=0;+break;+caseBPF_PROG_TYPE_LSM:+/* LSM progs check that they are attached to bpf_lsm_*() funcs.+*Onlysomeofthemaresleepable.+*/+if(check_sleepable_lsm_hook(btf_id))+ret=0;+break;+default:+break;}-if(ret)-gotoout;-tr->func.addr=(void*)addr;-prog->aux->trampoline=tr;-out:-mutex_unlock(&tr->mutex);-if(ret)-bpf_trampoline_put(tr);+if(ret){+bpf_log(log,"%s is not sleepable\n",+prog->aux->attach_func_name);+returnret;
the flow would be simpler if you just bpf_log(log, "%s is not
sleepable\n", prog->aux->attach_func_name); and return error here,
same for second case below. I don't think saving single bpf_log
repetition deserves this complication.
+ }
+ }
+
+ *tgt_addr = addr;
+ if (tgt_name)
+ *tgt_name = tname;
+ if (tgt_type)
+ *tgt_type = t;
+ return 0;
+}
+
+static int check_attach_btf_id(struct bpf_verifier_env *env)
+{
+ struct bpf_prog *prog = env->prog;
+ struct bpf_prog *tgt_prog = prog->aux->linked_prog;
+ u32 btf_id = prog->aux->attach_btf_id;
+ struct btf_func_model fmodel;
+ const struct btf_type *t;
+ const char *tname;
+ long addr;
+ int ret;
+ u64 key;
+
+ if (prog->aux->sleepable && prog->type != BPF_PROG_TYPE_TRACING &&
+ prog->type != BPF_PROG_TYPE_LSM) {
+ verbose(env, "Only fentry/fexit/fmod_ret and lsm programs can be sleepable\n");
+ return -EINVAL;
+ }
+
+ if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
+ return check_struct_ops_btf_id(env);
+
+ if (prog->type != BPF_PROG_TYPE_TRACING &&
+ prog->type != BPF_PROG_TYPE_LSM &&
+ prog->type != BPF_PROG_TYPE_EXT)
+ return 0;
+
+ ret = bpf_check_attach_target(&env->log, prog, tgt_prog, btf_id,
+ &fmodel, &addr, &tname, &t);
+ if (ret)
return ret;
+
+ if (tgt_prog) {
+ if (prog->type == BPF_PROG_TYPE_EXT) {
+ env->ops = bpf_verifier_ops[tgt_prog->type];
+ prog->expected_attach_type =
+ tgt_prog->expected_attach_type;
+ }
+ key = ((u64)tgt_prog->aux->id) << 32 | btf_id;
+ } else {
+ key = btf_id;
+ }
+
+ prog->aux->attach_func_proto = t;
+ prog->aux->attach_func_name = tname;
+
+ switch (prog->expected_attach_type) {
+ case BPF_TRACE_RAW_TP:
+ /* remember two read only pointers that are valid for
+ * the life time of the kernel
+ */
comment is misplaced now, it was referring to
prog->aux->attach_func_proto and prog->aux->attach_func_name
+ prog->aux->attach_btf_trace = true;
+ return 0;
+ case BPF_TRACE_ITER:
+ if (!bpf_iter_prog_supported(prog))
+ return -EINVAL;
+ return 0;
+ case BPF_MODIFY_RETURN:
+ ret = check_attach_modify_return(prog, addr);
+ if (ret) {
+ verbose(env, "%s() is not modifiable\n",
+ prog->aux->attach_func_name);
+ return ret;
+ }
+ fallthrough;
this fallthrough is only to do bpf_trampoline_get in the end, is that
right? Why not just call bpf_trampoline_get() here? That's not a lot
of duplication at all, but will be easier to follow and make sure
everything is right.
+ default:
+ if (prog->type == BPF_PROG_TYPE_LSM) {
+ ret = bpf_lsm_verify_prog(&env->log, prog);
+ if (ret < 0)
+ return ret;
+ }
Same here, BPF_PROG_TYPE_LSM (and we can use BPF_LSM_MAC expected
attach type, right?) is clearly a separate case, deserving its own
case?
As for the default one, let's keep it for _EXT case (and enforce it as
original code did). For BPF_TRACE_FEXIT and BPF_TRACE_FENTRY, let's
have a separate case as well.
These switches with default clauses and fallthroughs... they are mind
bending, it's really hard and time consuming to ensure one has
considered all possible code paths leading to this default case. It's
all already quite complicated, let's not add to the complexity.
On Fri, Sep 11, 2020 at 3:00 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted
From: Toke Høiland-Jørgensen <redacted>
The check_attach_btf_id() function really does three things:
1. It performs a bunch of checks on the program to ensure that the
attachment is valid.
2. It stores a bunch of state about the attachment being requested in
the verifier environment and struct bpf_prog objects.
3. It allocates a trampoline for the attachment.
This patch splits out (1.) and (3.) into separate functions in preparation
for reusing them when the actual attachment is happening (in the
raw_tracepoint_open syscall operation), which will allow tracing programs
to have multiple (compatible) attachments.
No functional change is intended with this patch.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
I can't tell if there are any functional changes or not, tbh. The
logic is quite complicated and full of intricate details. I did leave
some suggestions on hopefully simplifying code flow in some places
(and ensuring it's harder to break it on future changes), but I hope
Alexei will give it a very thorough review and check that none of the
subtle details broke.
Yeah, totally agree this is gnarly... :/
Which is also why I chickened out of doing any further changes in an
attempt to simplify the flow, but rather kept as much as the existing
structure as possible (with somewhat mixed results, I suppose).
Let's see what Alexei thinks. I guess I can take another crack at it, in
which case, thank you for the suggestions for simplifying things!
-Toke
From: Toke Høiland-Jørgensen <redacted>
This adds support for supplying a target fd and btf ID for the
raw_tracepoint_open() BPF operation, using a new bpf_raw_tracepoint_opts
structure. This can be used for attaching freplace programs to multiple
destinations.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
tools/lib/bpf/bpf.c | 13 ++++++++++++-
tools/lib/bpf/bpf.h | 9 +++++++++
tools/lib/bpf/libbpf.map | 1 +
3 files changed, 22 insertions(+), 1 deletion(-)
@@ -233,7 +233,16 @@ LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len);LIBBPF_APIintbpf_prog_query(inttarget_fd,enumbpf_attach_typetype,__u32query_flags,__u32*attach_flags,__u32*prog_ids,__u32*prog_cnt);+structbpf_raw_tracepoint_opts{+size_tsz;/* size of this struct for forward/backward compatibility */+inttgt_prog_fd;/* target program to attach to */+__u32tgt_btf_id;/* BTF ID of target function */+};+#define bpf_raw_tracepoint_opts__last_field tgt_btf_id+LIBBPF_APIintbpf_raw_tracepoint_open(constchar*name,intprog_fd);+LIBBPF_APIintbpf_raw_tracepoint_open_opts(constchar*name,intprog_fd,+structbpf_raw_tracepoint_opts*opts);LIBBPF_APIintbpf_load_btf(void*btf,__u32btf_size,char*log_buf,__u32log_buf_size,booldo_log);LIBBPF_APIintbpf_task_fd_query(intpid,intfd,__u32flags,char*buf,
On Fri, Sep 11, 2020 at 3:00 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted hunk
From: Toke Høiland-Jørgensen <redacted>
This adds support for supplying a target fd and btf ID for the
raw_tracepoint_open() BPF operation, using a new bpf_raw_tracepoint_opts
structure. This can be used for attaching freplace programs to multiple
destinations.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
tools/lib/bpf/bpf.c | 13 ++++++++++++-
tools/lib/bpf/bpf.h | 9 +++++++++
tools/lib/bpf/libbpf.map | 1 +
3 files changed, 22 insertions(+), 1 deletion(-)
We've had discussion around naming low-level APIs with options and
agreed with Andrey Ignatov that we'll follow the _xattr suffix naming
for low-level APIs. So let's keep it consistent, I suppose...
Please also add bpf_program__attach_trace_opts() for high-level API.
From: Jiri Olsa <jolsa@kernel.org>
Adding test that setup following program:
SEC("classifier/test_pkt_md_access")
int test_pkt_md_access(struct __sk_buff *skb)
with its extension:
SEC("freplace/test_pkt_md_access")
int test_pkt_md_access_new(struct __sk_buff *skb)
and tracing that extension with:
SEC("fentry/test_pkt_md_access_new")
int BPF_PROG(fentry, struct sk_buff *skb)
The test verifies that the tracing program can
dereference skb argument properly.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
tools/testing/selftests/bpf/prog_tests/trace_ext.c | 93 ++++++++++++++++++++
tools/testing/selftests/bpf/progs/test_trace_ext.c | 18 ++++
.../selftests/bpf/progs/test_trace_ext_tracing.c | 25 +++++
3 files changed, 136 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/trace_ext.c
create mode 100644 tools/testing/selftests/bpf/progs/test_trace_ext.c
create mode 100644 tools/testing/selftests/bpf/progs/test_trace_ext_tracing.c
@@ -595,8 +595,10 @@ union bpf_attr {}query;struct{/* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */-__u64name;-__u32prog_fd;+__u64name;+__u32prog_fd;+__u32tgt_prog_fd;+__u32tgt_btf_id;}raw_tracepoint;struct{/* anonymous struct for BPF_BTF_LOAD */
@@ -595,8 +595,10 @@ union bpf_attr {}query;struct{/* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */-__u64name;-__u32prog_fd;+__u64name;+__u32prog_fd;+__u32tgt_prog_fd;+__u32tgt_btf_id;}raw_tracepoint;struct{/* anonymous struct for BPF_BTF_LOAD */
From: Toke Høiland-Jørgensen <redacted>
This enables support for attaching freplace programs to multiple attach
points. It does this by amending UAPI for bpf_raw_tracepoint_open with a
target prog fd and btf ID pair that can be used to supply the new
attachment point. The target must be compatible with the target that was
supplied at program load time.
The implementation reuses the checks that were factored out of
check_attach_btf_id() to ensure compatibility between the BTF types of the
old and new attachment. If these match, a new bpf_tracing_link will be
created for the new attach target, allowing multiple attachments to
co-exist simultaneously.
The code could theoretically support multiple-attach of other types of
tracing programs as well, but since I don't have a use case for any of
those, the bpf_tracing_prog_attach() function will reject new targets for
anything other than PROG_TYPE_EXT programs.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
include/linux/bpf.h | 3 +
include/uapi/linux/bpf.h | 6 ++-
kernel/bpf/syscall.c | 96 +++++++++++++++++++++++++++++++++++++++-------
kernel/bpf/verifier.c | 9 ++++
4 files changed, 97 insertions(+), 17 deletions(-)
@@ -595,8 +595,10 @@ union bpf_attr {}query;struct{/* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */-__u64name;-__u32prog_fd;+__u64name;+__u32prog_fd;+__u32tgt_prog_fd;+__u32tgt_btf_id;}raw_tracepoint;struct{/* anonymous struct for BPF_BTF_LOAD */
@@ -2613,28 +2620,80 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)err=-EINVAL;gotoout_put_prog;}+if(tgt_prog_fd){+/* For now we only allow new targets for BPF_PROG_TYPE_EXT */+if(prog->type!=BPF_PROG_TYPE_EXT||+!btf_id){+err=-EINVAL;+gotoout_put_prog;+}-link=READ_ONCE(prog->aux->tgt_link);-if(!link){-err=-ENOENT;+tgt_prog=bpf_prog_get(tgt_prog_fd);+if(IS_ERR(tgt_prog)){+err=PTR_ERR(tgt_prog);+tgt_prog=NULL;+gotoout_put_prog;+}++key=((u64)tgt_prog->aux->id)<<32|btf_id;+}elseif(btf_id){+err=-EINVAL;gotoout_put_prog;}-olink=cmpxchg(&prog->aux->tgt_link,link,NULL);-if(olink!=link){-err=-ENOENT;-gotoout_put_prog;++link=READ_ONCE(prog->aux->tgt_link);+if(link){+if(tgt_prog&&link->trampoline->key!=key){+link=NULL;+}else{+structbpf_tracing_link*olink;++olink=cmpxchg(&prog->aux->tgt_link,link,NULL);+if(olink!=link){+link=NULL;+}elseif(tgt_prog){+/* re-using link that already has ref on+*tgt_prog,don'ttakeanother+*/+bpf_prog_put(tgt_prog);+tgt_prog=NULL;+}+}+}++if(!link){+if(!tgt_prog){+err=-ENOENT;+gotoout_put_prog;+}++err=bpf_check_attach_target(NULL,prog,tgt_prog,btf_id,+&fmodel,&addr,NULL,NULL);+if(err)+gotoout_put_prog;++link=bpf_tracing_link_create(prog,tgt_prog);+if(IS_ERR(link)){+err=PTR_ERR(link);+gotoout_put_prog;+}+tgt_prog=NULL;++err=bpf_trampoline_get(key,(void*)addr,&fmodel,&link->trampoline);+if(err)+gotoout_put_link;}err=bpf_link_prime(&link->link,&link_primer);if(err){kfree(link);-gotoout_put_prog;+gotoout_put_link;}err=bpf_trampoline_link_prog(prog,link->trampoline);if(err){bpf_link_cleanup(&link_primer);-gotoout_put_prog;+gotoout_put_link;}/* at this point the link is no longer referenced from struct bpf_prog,
@@ -2643,8 +2702,12 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)link->link.prog=prog;returnbpf_link_settle(&link_primer);+out_put_link:+bpf_link_put(&link->link);out_put_prog:bpf_prog_put(prog);+if(tgt_prog)+bpf_prog_put(tgt_prog);returnerr;}
@@ -2746,8 +2809,9 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)caseBPF_PROG_TYPE_EXT:caseBPF_PROG_TYPE_LSM:if(attr->raw_tracepoint.name){-/* The attach point for this category of programs-*shouldbespecifiedviabtf_idduringprogramload.+/* The attach point for this category of programs should+*bespecifiedviabtf_idduringprogramload,orusing+*tgt_btf_id.*/err=-EINVAL;gotoout_put_prog;
@@ -2757,7 +2821,9 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)tp_name=prog->aux->attach_func_name;break;}-returnbpf_tracing_prog_attach(prog);+returnbpf_tracing_prog_attach(prog,+attr->raw_tracepoint.tgt_prog_fd,+attr->raw_tracepoint.tgt_btf_id);caseBPF_PROG_TYPE_RAW_TRACEPOINT:caseBPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:if(strncpy_from_user(buf,
On Fri, Sep 11, 2020 at 3:01 AM Toke Høiland-Jørgensen [off-list ref] wrote:
From: Toke Høiland-Jørgensen <redacted>
This enables support for attaching freplace programs to multiple attach
points. It does this by amending UAPI for bpf_raw_tracepoint_open with a
target prog fd and btf ID pair that can be used to supply the new
attachment point. The target must be compatible with the target that was
supplied at program load time.
The implementation reuses the checks that were factored out of
check_attach_btf_id() to ensure compatibility between the BTF types of the
old and new attachment. If these match, a new bpf_tracing_link will be
created for the new attach target, allowing multiple attachments to
co-exist simultaneously.
The code could theoretically support multiple-attach of other types of
tracing programs as well, but since I don't have a use case for any of
those, the bpf_tracing_prog_attach() function will reject new targets for
anything other than PROG_TYPE_EXT programs.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
It feels like using a semi-constructed bpf_tracing_link inside
prog->aux->tgt_link is just an unnecessary complication, after reading
this and previous patches. Seems more straightforward and simpler to
store tgt_attach_type/tgt_prog_type (permanently) and
tgt_prog/tgt_trampoline (until first attachment) in prog->aux and then
properly create bpf_link on attach.
@@ -595,8 +595,10 @@ union bpf_attr {}query;struct{/* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */-__u64name;-__u32prog_fd;+__u64name;+__u32prog_fd;+__u32tgt_prog_fd;+__u32tgt_btf_id;}raw_tracepoint;
rant: any chance of putting this into LINK_CREATE instead of extending
very unfortunately named RAW_TRACEPOINT_OPEN?
@@ -2613,28 +2620,80 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)err=-EINVAL;gotoout_put_prog;}+if(tgt_prog_fd){+/* For now we only allow new targets for BPF_PROG_TYPE_EXT */+if(prog->type!=BPF_PROG_TYPE_EXT||+!btf_id){+err=-EINVAL;+gotoout_put_prog;+}-link=READ_ONCE(prog->aux->tgt_link);-if(!link){-err=-ENOENT;+tgt_prog=bpf_prog_get(tgt_prog_fd);+if(IS_ERR(tgt_prog)){+err=PTR_ERR(tgt_prog);+tgt_prog=NULL;+gotoout_put_prog;+}++key=((u64)tgt_prog->aux->id)<<32|btf_id;+}elseif(btf_id){+err=-EINVAL;gotoout_put_prog;}-olink=cmpxchg(&prog->aux->tgt_link,link,NULL);-if(olink!=link){-err=-ENOENT;-gotoout_put_prog;++link=READ_ONCE(prog->aux->tgt_link);+if(link){+if(tgt_prog&&link->trampoline->key!=key){
I think we need to have a proper locking about this. Imagine two
attaches racing, both read non-NULL tgt_link, one of them proceeds to
cmpxchg, attach, detach, and free link. Then this one wakes up and
tries to access freed memory here. We are coordinating multiple
threads on this, it needs to be locked, at least for simplicity, given
that performance is not critical here.
+ link = NULL;
+ } else {
+ struct bpf_tracing_link *olink;
+
+ olink = cmpxchg(&prog->aux->tgt_link, link, NULL);
+ if (olink != link) {
+ link = NULL;
+ } else if (tgt_prog) {
+ /* re-using link that already has ref on
+ * tgt_prog, don't take another
+ */
+ bpf_prog_put(tgt_prog);
+ tgt_prog = NULL;
+ }
+ }
+ }
+
+ if (!link) {
+ if (!tgt_prog) {
+ err = -ENOENT;
+ goto out_put_prog;
+ }
+
+ err = bpf_check_attach_target(NULL, prog, tgt_prog, btf_id,
+ &fmodel, &addr, NULL, NULL);
+ if (err)
+ goto out_put_prog;
+
+ link = bpf_tracing_link_create(prog, tgt_prog);
+ if (IS_ERR(link)) {
+ err = PTR_ERR(link);
+ goto out_put_prog;
+ }
+ tgt_prog = NULL;
+
+ err = bpf_trampoline_get(key, (void *)addr, &fmodel, &link->trampoline);
+ if (err)
+ goto out_put_link;
see previous patch, let's avoid bpf_link_put before bpf_link_settle.
bpf_link_cleanup() is for after priming, otherwise it's just a kfree.
On Fri, Sep 11, 2020 at 3:01 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted
From: Toke Høiland-Jørgensen <redacted>
This enables support for attaching freplace programs to multiple attach
points. It does this by amending UAPI for bpf_raw_tracepoint_open with a
target prog fd and btf ID pair that can be used to supply the new
attachment point. The target must be compatible with the target that was
supplied at program load time.
The implementation reuses the checks that were factored out of
check_attach_btf_id() to ensure compatibility between the BTF types of the
old and new attachment. If these match, a new bpf_tracing_link will be
created for the new attach target, allowing multiple attachments to
co-exist simultaneously.
The code could theoretically support multiple-attach of other types of
tracing programs as well, but since I don't have a use case for any of
those, the bpf_tracing_prog_attach() function will reject new targets for
anything other than PROG_TYPE_EXT programs.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
It feels like using a semi-constructed bpf_tracing_link inside
prog->aux->tgt_link is just an unnecessary complication, after reading
this and previous patches. Seems more straightforward and simpler to
store tgt_attach_type/tgt_prog_type (permanently) and
tgt_prog/tgt_trampoline (until first attachment) in prog->aux and then
properly create bpf_link on attach.
I updated v4 with your comments, but kept the link in prog->aux; the
reason being that having a container for the two pointers makes it
possible to atomically swap it out with xchg() as you suggested
previously. Could you please take a look at v4? If you still think it's
better to just keep two separate pointers (and add a lock) in prog->aux,
I can change it to that. But I'd rather avoid the lock if possible...
-Toke
On Mon, Sep 14, 2020 at 9:08 AM Toke Høiland-Jørgensen [off-list ref] wrote:
Andrii Nakryiko [off-list ref] writes:
quoted
On Fri, Sep 11, 2020 at 3:01 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted
From: Toke Høiland-Jørgensen <redacted>
This enables support for attaching freplace programs to multiple attach
points. It does this by amending UAPI for bpf_raw_tracepoint_open with a
target prog fd and btf ID pair that can be used to supply the new
attachment point. The target must be compatible with the target that was
supplied at program load time.
The implementation reuses the checks that were factored out of
check_attach_btf_id() to ensure compatibility between the BTF types of the
old and new attachment. If these match, a new bpf_tracing_link will be
created for the new attach target, allowing multiple attachments to
co-exist simultaneously.
The code could theoretically support multiple-attach of other types of
tracing programs as well, but since I don't have a use case for any of
those, the bpf_tracing_prog_attach() function will reject new targets for
anything other than PROG_TYPE_EXT programs.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
It feels like using a semi-constructed bpf_tracing_link inside
prog->aux->tgt_link is just an unnecessary complication, after reading
this and previous patches. Seems more straightforward and simpler to
store tgt_attach_type/tgt_prog_type (permanently) and
tgt_prog/tgt_trampoline (until first attachment) in prog->aux and then
properly create bpf_link on attach.
I updated v4 with your comments, but kept the link in prog->aux; the
reason being that having a container for the two pointers makes it
possible to atomically swap it out with xchg() as you suggested
previously. Could you please take a look at v4? If you still think it's
better to just keep two separate pointers (and add a lock) in prog->aux,
I can change it to that. But I'd rather avoid the lock if possible...
I took a very quick look at this specific bit, planning to do another
pass tomorrow.
What's the problem with adding a mutex to bpf_prog_aux? In your case,
now you introduced (unlikely, but still) extra state transition for
tgt_link from non-NULL to NULL and then back to non-NULL? And why?
Just to use atomic xchg, while using atomic operation is not an
absolute necessity because it's not a performance-critical path at
all. We are not optimizing for millions of freplace attachments a
second, right? On the other hand, having a mutex there won't require
restoration logic, it will be dead simple, obvious and
straightforward. So yeah, I still think mutex is better there.
BTW, check Stanislav's latest patch set. He's adding used_maps_mutex
to bpf_prog_aux with no problems at all. It seems to me that we might
want to generalize that used_maps_mutex to be just bpf_prog_aux's
mutex ('prog_aux_mutex' or whatever we'd call it) and use it for such
kinds of low-frequency bpf_prog metadata manipulations/checks.
Thoughts?
On Mon, Sep 14, 2020 at 9:08 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted
Andrii Nakryiko [off-list ref] writes:
quoted
On Fri, Sep 11, 2020 at 3:01 AM Toke Høiland-Jørgensen [off-list ref] wrote:
quoted
From: Toke Høiland-Jørgensen <redacted>
This enables support for attaching freplace programs to multiple attach
points. It does this by amending UAPI for bpf_raw_tracepoint_open with a
target prog fd and btf ID pair that can be used to supply the new
attachment point. The target must be compatible with the target that was
supplied at program load time.
The implementation reuses the checks that were factored out of
check_attach_btf_id() to ensure compatibility between the BTF types of the
old and new attachment. If these match, a new bpf_tracing_link will be
created for the new attach target, allowing multiple attachments to
co-exist simultaneously.
The code could theoretically support multiple-attach of other types of
tracing programs as well, but since I don't have a use case for any of
those, the bpf_tracing_prog_attach() function will reject new targets for
anything other than PROG_TYPE_EXT programs.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
It feels like using a semi-constructed bpf_tracing_link inside
prog->aux->tgt_link is just an unnecessary complication, after reading
this and previous patches. Seems more straightforward and simpler to
store tgt_attach_type/tgt_prog_type (permanently) and
tgt_prog/tgt_trampoline (until first attachment) in prog->aux and then
properly create bpf_link on attach.
I updated v4 with your comments, but kept the link in prog->aux; the
reason being that having a container for the two pointers makes it
possible to atomically swap it out with xchg() as you suggested
previously. Could you please take a look at v4? If you still think it's
better to just keep two separate pointers (and add a lock) in prog->aux,
I can change it to that. But I'd rather avoid the lock if possible...
I took a very quick look at this specific bit, planning to do another
pass tomorrow.
What's the problem with adding a mutex to bpf_prog_aux? In your case,
now you introduced (unlikely, but still) extra state transition for
tgt_link from non-NULL to NULL and then back to non-NULL? And why?
Just to use atomic xchg, while using atomic operation is not an
absolute necessity because it's not a performance-critical path at
all. We are not optimizing for millions of freplace attachments a
second, right? On the other hand, having a mutex there won't require
restoration logic, it will be dead simple, obvious and
straightforward. So yeah, I still think mutex is better there.
So I went ahead and implemented a mutex-based version of this. I'm not
sure I agree with "dead simple", I'd say it's on par with the previous
version; and that is only if I explicitly limit the scope of the mutex
to *writing* of the tgt_* pointers (i.e., I haven't gone through and
protected all the reads from within the verifier).
The mutex version does have the benefit of still making it possible to
retry a bpf_raw_tracepoint_open() if it fails, so I guess that is a
benefit; I'll post it as v5 and you can have a look :)
BTW, check Stanislav's latest patch set. He's adding used_maps_mutex
to bpf_prog_aux with no problems at all. It seems to me that we might
want to generalize that used_maps_mutex to be just bpf_prog_aux's
mutex ('prog_aux_mutex' or whatever we'd call it) and use it for such
kinds of low-frequency bpf_prog metadata manipulations/checks.
I'm not sure I like the idea of widening the scope of the mutex. Or at
least I think that should be done as a follow-up patch that does a
proper analysis of all the different fields it is supposed to protect
and makes sure no deadlocks creep in.
-Toke
From: Toke Høiland-Jørgensen <redacted>
Eelco reported we can't properly access arguments if the tracing
program is attached to extension program.
Having following program:
SEC("classifier/test_pkt_md_access")
int test_pkt_md_access(struct __sk_buff *skb)
with its extension:
SEC("freplace/test_pkt_md_access")
int test_pkt_md_access_new(struct __sk_buff *skb)
and tracing that extension with:
SEC("fentry/test_pkt_md_access_new")
int BPF_PROG(fentry, struct sk_buff *skb)
It's not possible to access skb argument in the fentry program,
with following error from verifier:
; int BPF_PROG(fentry, struct sk_buff *skb)
0: (79) r1 = *(u64 *)(r1 +0)
invalid bpf_context access off=0 size=8
The problem is that btf_ctx_access gets the context type for the
traced program, which is in this case the extension.
But when we trace extension program, we want to get the context
type of the program that the extension is attached to, so we can
access the argument properly in the trace program.
This version of the patch is tweaked slightly from Jiri's original one,
since the refactoring in the previous patches means we have to get the
target prog type from the new variable in prog->aux instead of directly
from the target prog.
Reported-by: Eelco Chaudron <echaudro@redhat.com>
Suggested-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
kernel/bpf/btf.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)