With the introduction of the struct_ops program type, it became possible to
implement kernel functionality in BPF, making it viable to use BPF in place
of a regular kernel module for these particular operations.
Thus far, the only user of this mechanism is for implementing TCP
congestion control algorithms. These are clearly marked as GPL-only when
implemented as modules (as seen by the use of EXPORT_SYMBOL_GPL for
tcp_register_congestion_control()), so it seems like an oversight that this
was not carried over to BPF implementations. And sine this is the only user
of the struct_ops mechanism, just enforcing GPL-only for the struct_ops
program type seems like the simplest way to fix this.
Fixes: 0baf26b0fcd7 ("bpf: tcp: Support tcp_congestion_ops in bpf")
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
kernel/bpf/verifier.c | 5 +++++
1 file changed, 5 insertions(+)
@@ -12166,6 +12166,11 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)return-ENOTSUPP;}+if(!prog->gpl_compatible){+verbose(env,"struct ops programs must have a GPL compatible license\n");+return-EINVAL;+}+t=st_ops->type;member_idx=prog->expected_attach_type;if(member_idx>=btf_type_vlen(t)){
This adds a selftest to check that the verifier rejects a TCP CC struct_ops
with a non-GPL license. To save having to add a whole new BPF object just
for this, reuse the dctcp CC, but rewrite the license field before loading.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
.../selftests/bpf/prog_tests/bpf_tcp_ca.c | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
@@ -227,10 +227,41 @@ static void test_dctcp(void)bpf_dctcp__destroy(dctcp_skel);}+staticvoidtest_invalid_license(void)+{+/* We want to check that the verifier refuses to load a non-GPL TCP CC.+*Ratherthancreateawholenewfile+skeleton,justreuseanexisting+*objectandrewritethelicenseinmemoryafterloading.Sinelibbpf+*doesn'texposethis,wedefineastructthatincludesthefirstcouple+*ofinternalfieldsforstructbpf_objectsowecanoverwritetheright+*bits.Yes,thisisabitofahack,butitmakesthetestalotsimpler.+*/+structbpf_object_fragment{+charname[BPF_OBJ_NAME_LEN];+charlicense[64];+}*obj;+structbpf_dctcp*skel;+interr;++skel=bpf_dctcp__open();+if(CHECK(!skel,"bpf_dctcp__open","failed\n"))+return;++obj=(void*)skel->obj;+obj->license[0]='X';// turns 'GPL' into 'XPL' which will fail the check++err=bpf_dctcp__load(skel);+CHECK(err!=-LIBBPF_ERRNO__VERIFY,"bpf_dctcp__load","err:%d\n",err);++bpf_dctcp__destroy(skel);+}+voidtest_bpf_tcp_ca(void){if(test__start_subtest("dctcp"))test_dctcp();if(test__start_subtest("cubic"))test_cubic();+if(test__start_subtest("invalid_license"))+test_invalid_license();}
From: Martin KaFai Lau <hidden> Date: 2021-03-25 18:17:22
On Thu, Mar 25, 2021 at 04:40:33PM +0100, Toke Høiland-Jørgensen wrote:
quoted hunk
With the introduction of the struct_ops program type, it became possible to
implement kernel functionality in BPF, making it viable to use BPF in place
of a regular kernel module for these particular operations.
Thus far, the only user of this mechanism is for implementing TCP
congestion control algorithms. These are clearly marked as GPL-only when
implemented as modules (as seen by the use of EXPORT_SYMBOL_GPL for
tcp_register_congestion_control()), so it seems like an oversight that this
was not carried over to BPF implementations. And sine this is the only user
of the struct_ops mechanism, just enforcing GPL-only for the struct_ops
program type seems like the simplest way to fix this.
Fixes: 0baf26b0fcd7 ("bpf: tcp: Support tcp_congestion_ops in bpf")
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
kernel/bpf/verifier.c | 5 +++++
1 file changed, 5 insertions(+)
@@ -12166,6 +12166,11 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)return-ENOTSUPP;}+if(!prog->gpl_compatible){+verbose(env,"struct ops programs must have a GPL compatible license\n");+return-EINVAL;+}+
Thanks for the patch.
A nit. Instead of sitting in between of the attach_btf_id check
and expected_attach_type check, how about moving it to the beginning
of this function. Checking attach_btf_id and expected_attach_type
would make more sense to be done next to each other as in the current
code.
Acked-by: Martin KaFai Lau <redacted>
From: Martin KaFai Lau <hidden> Date: 2021-03-25 18:33:14
On Thu, Mar 25, 2021 at 04:40:34PM +0100, Toke Høiland-Jørgensen wrote:
quoted hunk
This adds a selftest to check that the verifier rejects a TCP CC struct_ops
with a non-GPL license. To save having to add a whole new BPF object just
for this, reuse the dctcp CC, but rewrite the license field before loading.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
.../selftests/bpf/prog_tests/bpf_tcp_ca.c | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
@@ -227,10 +227,41 @@ static void test_dctcp(void)bpf_dctcp__destroy(dctcp_skel);}+staticvoidtest_invalid_license(void)+{+/* We want to check that the verifier refuses to load a non-GPL TCP CC.+*Ratherthancreateawholenewfile+skeleton,justreuseanexisting+*objectandrewritethelicenseinmemoryafterloading.Sinelibbpf+*doesn'texposethis,wedefineastructthatincludesthefirstcouple+*ofinternalfieldsforstructbpf_objectsowecanoverwritetheright+*bits.Yes,thisisabitofahack,butitmakesthetestalotsimpler.+*/+structbpf_object_fragment{+charname[BPF_OBJ_NAME_LEN];+charlicense[64];+}*obj;
It is fragile. A new bpf_nogpltcp.c should be created and it does
not have to be a full tcp-cc. A very minimal implementation with
only .init. Something like this (uncompiled code):
char _license[] SEC("license") = "X";
void BPF_STRUCT_OPS(nogpltcp_init, struct sock *sk)
{
}
SEC(".struct_ops")
struct tcp_congestion_ops bpf_nogpltcp = {
.init = (void *)nogpltcp_init,
.name = "bpf_nogpltcp",
};
libbpf_set_print() can also be used to look for the
the verifier log "struct ops programs must have a GPL compatible license".
On Thu, Mar 25, 2021 at 04:40:33PM +0100, Toke Høiland-Jørgensen wrote:
quoted
With the introduction of the struct_ops program type, it became possible to
implement kernel functionality in BPF, making it viable to use BPF in place
of a regular kernel module for these particular operations.
Thus far, the only user of this mechanism is for implementing TCP
congestion control algorithms. These are clearly marked as GPL-only when
implemented as modules (as seen by the use of EXPORT_SYMBOL_GPL for
tcp_register_congestion_control()), so it seems like an oversight that this
was not carried over to BPF implementations. And sine this is the only user
of the struct_ops mechanism, just enforcing GPL-only for the struct_ops
program type seems like the simplest way to fix this.
Fixes: 0baf26b0fcd7 ("bpf: tcp: Support tcp_congestion_ops in bpf")
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
kernel/bpf/verifier.c | 5 +++++
1 file changed, 5 insertions(+)
@@ -12166,6 +12166,11 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)return-ENOTSUPP;}+if(!prog->gpl_compatible){+verbose(env,"struct ops programs must have a GPL compatible license\n");+return-EINVAL;+}+
Thanks for the patch.
A nit. Instead of sitting in between of the attach_btf_id check
and expected_attach_type check, how about moving it to the beginning
of this function. Checking attach_btf_id and expected_attach_type
would make more sense to be done next to each other as in the current
code.
Yeah, good point. Not sure what I was thinking stuffing it in the middle
there; will fix and send a v2!
On Thu, Mar 25, 2021 at 04:40:34PM +0100, Toke Høiland-Jørgensen wrote:
quoted
This adds a selftest to check that the verifier rejects a TCP CC struct_ops
with a non-GPL license. To save having to add a whole new BPF object just
for this, reuse the dctcp CC, but rewrite the license field before loading.
Signed-off-by: Toke Høiland-Jørgensen <redacted>
---
.../selftests/bpf/prog_tests/bpf_tcp_ca.c | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
@@ -227,10 +227,41 @@ static void test_dctcp(void)bpf_dctcp__destroy(dctcp_skel);}+staticvoidtest_invalid_license(void)+{+/* We want to check that the verifier refuses to load a non-GPL TCP CC.+*Ratherthancreateawholenewfile+skeleton,justreuseanexisting+*objectandrewritethelicenseinmemoryafterloading.Sinelibbpf+*doesn'texposethis,wedefineastructthatincludesthefirstcouple+*ofinternalfieldsforstructbpf_objectsowecanoverwritetheright+*bits.Yes,thisisabitofahack,butitmakesthetestalotsimpler.+*/+structbpf_object_fragment{+charname[BPF_OBJ_NAME_LEN];+charlicense[64];+}*obj;
It is fragile. A new bpf_nogpltcp.c should be created and it does
not have to be a full tcp-cc. A very minimal implementation with
only .init. Something like this (uncompiled code):
char _license[] SEC("license") = "X";
void BPF_STRUCT_OPS(nogpltcp_init, struct sock *sk)
{
}
SEC(".struct_ops")
struct tcp_congestion_ops bpf_nogpltcp = {
.init = (void *)nogpltcp_init,
.name = "bpf_nogpltcp",
};
libbpf_set_print() can also be used to look for the
the verifier log "struct ops programs must have a GPL compatible license".
Ah, thanks for the pointers! Will fix this up as well...
-Toke